Есть ли простой способ конвертировать перечисление C++ в строку?

Предположим, у нас есть некоторые именованные перечисления:

enum MyEnum {
      FOO,
      BAR = 0x50
};

То, что я нашел, - это скрипт (любой язык), который сканирует все заголовки в моем проекте и генерирует заголовок с одной функцией на перечисление.

char* enum_to_string(MyEnum t);

И реализация с чем-то вроде этого:

char* enum_to_string(MyEnum t){
      switch(t){
         case FOO:
            return "FOO";
         case BAR:
            return "BAR";
         default:
            return "INVALID ENUM";
      }
 }

Gotcha действительно с перечислениями typedefed и безымянными перечислениями стиля C. Кто-нибудь знает что-нибудь для этого?

РЕДАКТИРОВАТЬ: Решение не должно изменять мой источник, за исключением сгенерированных функций. Перечисления в API, поэтому использование решений, предложенных до сих пор, просто не вариант.

36 ответов

Вот попытка автоматически получить операторы << и >> в enum с помощью однострочной макрокоманды...

Определения:

#include <string>
#include <iostream>
#include <stdexcept>
#include <algorithm>
#include <iterator>
#include <sstream>
#include <vector>

#define MAKE_STRING(str, ...) #str, MAKE_STRING1_(__VA_ARGS__)
#define MAKE_STRING1_(str, ...) #str, MAKE_STRING2_(__VA_ARGS__)
#define MAKE_STRING2_(str, ...) #str, MAKE_STRING3_(__VA_ARGS__)
#define MAKE_STRING3_(str, ...) #str, MAKE_STRING4_(__VA_ARGS__)
#define MAKE_STRING4_(str, ...) #str, MAKE_STRING5_(__VA_ARGS__)
#define MAKE_STRING5_(str, ...) #str, MAKE_STRING6_(__VA_ARGS__)
#define MAKE_STRING6_(str, ...) #str, MAKE_STRING7_(__VA_ARGS__)
#define MAKE_STRING7_(str, ...) #str, MAKE_STRING8_(__VA_ARGS__)
#define MAKE_STRING8_(str, ...) #str, MAKE_STRING9_(__VA_ARGS__)
#define MAKE_STRING9_(str, ...) #str, MAKE_STRING10_(__VA_ARGS__)
#define MAKE_STRING10_(str) #str

#define MAKE_ENUM(name, ...) MAKE_ENUM_(, name, __VA_ARGS__)
#define MAKE_CLASS_ENUM(name, ...) MAKE_ENUM_(friend, name, __VA_ARGS__)

#define MAKE_ENUM_(attribute, name, ...) name { __VA_ARGS__ }; \
    attribute std::istream& operator>>(std::istream& is, name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        std::string str; \
        std::istream& r = is >> str; \
        const size_t len = sizeof(name##Str)/sizeof(name##Str[0]); \
        const std::vector<std::string> enumStr(name##Str, name##Str + len); \
        const std::vector<std::string>::const_iterator it = std::find(enumStr.begin(), enumStr.end(), str); \
        if (it != enumStr.end())\
            e = name(it - enumStr.begin()); \
        else \
            throw std::runtime_error("Value \"" + str + "\" is not part of enum "#name); \
        return r; \
    }; \
    attribute std::ostream& operator<<(std::ostream& os, const name& e) { \
        const char* name##Str[] = { MAKE_STRING(__VA_ARGS__) }; \
        return (os << name##Str[e]); \
    }

Использование:

// Declare global enum
enum MAKE_ENUM(Test3, Item13, Item23, Item33, Itdsdgem43);

class Essai {
public:
    // Declare enum inside class
    enum MAKE_CLASS_ENUM(Test, Item1, Item2, Item3, Itdsdgem4);

};

int main() {
    std::cout << Essai::Item1 << std::endl;

    Essai::Test ddd = Essai::Item1;
    std::cout << ddd << std::endl;

    std::istringstream strm("Item2");
    strm >> ddd;

    std::cout << (int) ddd << std::endl;
    std::cout << ddd << std::endl;
}

Не уверен насчет ограничений этой схемы, хотя... комментарии приветствуются!

Ну, еще один вариант. Типичный вариант использования - где вам нужны константы для HTTP-глаголов, а также использование строковых значений версии.

Пример:

int main () {

  VERB a = VERB::GET;
  VERB b = VERB::GET;
  VERB c = VERB::POST;
  VERB d = VERB::PUT;
  VERB e = VERB::DELETE;


  std::cout << a.toString() << std::endl;

  std::cout << a << std::endl;

  if ( a == VERB::GET ) {
    std::cout << "yes" << std::endl;
  }

  if ( a == b ) {
    std::cout << "yes" << std::endl;
  }

  if ( a != c ) {
    std::cout << "no" << std::endl;
  }

}

VERB класс:

// -----------------------------------------------------------
// -----------------------------------------------------------
class VERB {

private:

  // private constants
  enum Verb {GET_=0, POST_, PUT_, DELETE_};

  // private string values
  static const std::string theStrings[];

  // private value
  const Verb value;
  const std::string text;

  // private constructor
  VERB (Verb v) :
  value(v), text (theStrings[v])
  {
    // std::cout << " constructor \n";
  }

public:

  operator const char * ()  const { return text.c_str(); }

  operator const std::string ()  const { return text; }

  const std::string toString () const { return text; }

  bool operator == (const VERB & other) const { return (*this).value == other.value; }

  bool operator != (const VERB & other) const { return ! ( (*this) == other); }

  // ---

  static const VERB GET;
  static const VERB POST;
  static const VERB PUT;
  static const VERB DELETE;

};

const std::string VERB::theStrings[] = {"GET", "POST", "PUT", "DELETE"};

const VERB VERB::GET = VERB ( VERB::Verb::GET_ );
const VERB VERB::POST = VERB ( VERB::Verb::POST_ );
const VERB VERB::PUT = VERB ( VERB::Verb::PUT_ );
const VERB VERB::DELETE = VERB ( VERB::Verb::DELETE_ );
// end of file

Не так давно я сделал несколько хитростей, чтобы перечисления правильно отображались в QComboBox и чтобы определения перечисления и представления строк были одним оператором

#pragma once
#include <boost/unordered_map.hpp>

namespace enumeration
{

   struct enumerator_base : boost::noncopyable
   {
      typedef
         boost::unordered_map<int, std::wstring>
         kv_storage_t;
      typedef
         kv_storage_t::value_type
         kv_type;
      kv_storage_t const & kv() const
      {
         return storage_;
      }

      LPCWSTR name(int i) const
      {
         kv_storage_t::const_iterator it = storage_.find(i);
         if(it != storage_.end())
            return it->second.c_str();
         return L"empty";
      }

   protected:
      kv_storage_t storage_;
   };

   template<class T>
   struct enumerator;

   template<class D>
   struct enum_singleton : enumerator_base
   {
      static enumerator_base const & instance()
      {
         static D inst;
         return inst;
      }
   };
}

#define QENUM_ENTRY(K, V, N)  K, N storage_.insert(std::make_pair((int)K, V));

#define QBEGIN_ENUM(NAME, C)   \
enum NAME                     \
{                             \
   C                          \
}                             \
};                            \
}                             \

#define QEND_ENUM(NAME) \
};                     \
namespace enumeration  \
{                      \
template<>             \
struct enumerator<NAME>\
   : enum_singleton< enumerator<NAME> >\
{                      \
   enumerator()        \
   {

//usage
/*
QBEGIN_ENUM(test_t,
   QENUM_ENTRY(test_entry_1, L"number uno",
   QENUM_ENTRY(test_entry_2, L"number dos",
   QENUM_ENTRY(test_entry_3, L"number tres",
QEND_ENUM(test_t)))))
*/

Теперь у вас есть enumeration::enum_singleton<your_enum>::instance() возможность конвертировать перечисления в строки. Если вы замените kv_storage_t с boost::bimap, вы также сможете сделать обратное преобразование. Общий базовый класс для конвертера был введен для хранения его в объекте Qt, потому что объекты Qt не могли быть шаблонами

Предыдущее появление

Этот вопрос является дубликатом,

Однако ни на один из вопросов я не нашел хороших ответов.

Углубившись в тему, я нашел два отличных решения с открытым исходным кодом:

wise_enum

  • Автономная библиотека smart enum для C++ 11/14/17. Он поддерживает все стандартные функциональные возможности, которые вы ожидаете от класса smart enum в C++.
  • Ограничения: требуется как минимум C++11.

Лучшие перечисления

  • Рефлексивная библиотека enum времени компиляции с чистым синтаксисом, в одном заголовочном файле и без зависимостей.
  • Ограничения: основаны на макросах, не могут использоваться внутри класса.

Примечание: я повторяю эту рекомендацию здесь. Этот вопрос имеет много трафика / просмотров и действительно требует перечисления решений выше.

Вот программа CLI, которую я написал, чтобы легко конвертировать перечисления в строки. Он прост в использовании, и на его выполнение уходит около 5 секунд (включая время, чтобы перейти к каталогу, содержащему программу, а затем запустить его, передав ему файл, содержащий перечисление).

Скачать здесь: http://www.mediafire.com/?nttignoozzz

Тема для обсуждения здесь: http://cboard.cprogramming.com/projects-job-recruitment/127488-free-program-im-sharing-convertenumtostrings.html

Запустите программу с аргументом --help, чтобы получить описание, как ее использовать.

Использование составных троичных операторов может быть несколько элегантным для перечислений с несколькими элементами (однострочное). Выражение растет только приблизительно линейно по длине вместе с количеством элементов.

Вот хороший пример использования:

enum log_level {INFO, WARNING, ERROR};
...
void logger::write(const std::string log, const log_level l) {
    ...
    std::string s = (l == INFO) ? "INFO" : 
                    (l == WARNING) ? "WARNING" : 
                    (l == ERROR) ? "ERROR" : "UNKNOWN";
    ...
}
...

Конечно, это просто еще один оператор switch/if, но это однострочный оператор. И как краткость против простоты, это встречается где-то в середине. Как постоянное выражение, оно также может быть легко превращено в встроенную функцию.

Другие вопросы по тегам