Есть ли простой способ конвертировать перечисление 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 не могли быть шаблонами
Этот вопрос является дубликатом,
Однако ни на один из вопросов я не нашел хороших ответов.
Углубившись в тему, я нашел два отличных решения с открытым исходным кодом:
- Автономная библиотека 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, но это однострочный оператор. И как краткость против простоты, это встречается где-то в середине. Как постоянное выражение, оно также может быть легко превращено в встроенную функцию.