Перечисление в строку: возвращает целочисленное значение перечисления, если неверно / не найдено
Итак, я реализовал enumToString
функция для нескольких перечислений, которые я часто использую (часто спрашивается в SO: есть ли простой способ преобразовать перечисление C++ в строку?, простой способ использовать переменные типов перечисления в виде строки в C?,...). Это облегчает отладку сообщений об ошибках, но я должен поддерживать функцию, чтобы добавить значения, которые иногда не имеют описания строки.
Мой код выглядит так:
typedef std::map<my_enum_e, const char *> enum_map_t;
static bool s_enum_map_initialized = false;
static enum_map_t s_enum_strings;
static void s_init_maps()
{
#define ADD_ENUM( X ) s_enum_strings[X] = #X;
if( s_enum_strings.size() == 0)
{
ADD_CLASS( MY_ENUM_1 );
ADD_CLASS( MY_ENUM_2 );
/* ... all enums */
}
s_enum_map_initialized = true;
}
const char *Tools::enumCString( my_enum_e e )
{
if( ! s_enum_map_initialized )
{
s_init_maps();
}
// todo: use the iterator instead of searching twice
if( s_enum_strings.find(e) != s_enum_strings.end() )
{
return s_class_strings[e];
}
return "(unknown enum_e)";
}
Теперь, что я хочу, это чтобы я не нашел перечисление на карте "(unknown enum %d)", e
, Что даст мне значение перечисления, которое я пропустил.
Таким образом, даже если я не добавлю его на карту, у меня все равно будет его значение, и я смогу отладить свою программу.
Я не могу найти способ сделать это просто: поток строк, созданный в стеке, будет уничтожен сразу после возврата, статический поток строк не является потокобезопасным,...
редактировать: конечно, используя std::string
поскольку возвращаемый тип позволил бы мне отформатировать его, но я очень часто вызываю эти функции в своем коде, я решил передать const char *
указатель быстрее, так как мне не нужно каждый раз помещать std::string в стек.
Любое решение?
4 ответа
Вернуть std::string
а не char*
,
Это позволит вам использовать std::stringstream
сгенерировать ваше сообщение. Тогда вызывающий сайт просто должен использовать .c_str( )
функция-член на std::string
получить указатель в стиле C (если требуется).
Лично я использую BOOST:)
Пример использования:
SANDBOX_DEFINE_ENUM(MyEnum, (Foo)(Bar)(Team))
Даст:
struct MyEnum {
enum Type {
Foo,
Bar,
Team
};
static Type const First = Foo;
static Type const Last = Team;
};
inline char const* toString(MyEnum::Type value) {
switch(value) {
case MyEnum::Foo: return "Foo";
case MyEnum::Bar: return "Bar";
case MyEnum::Team: return "Team";
}
return 0;
}
Код:
#include <boost/preprocessor/cat.hpp>
#include <boost/preprocessor/stringize.hpp>
#include <boost/preprocessor/seq/enum.hpp>
#include <boost/preprocessor/seq/for_each.hpp>
#include <boost/preprocessor/seq/reverse.hpp>
#include <boost/preprocessor/seq/seq.hpp>
#define SANDBOX_DEFINE_ENUM(Name_, Values_) \
SANDBOX_DEFINE_ENUM_TYPE(Name_, Values_) \
SANDBOX_DEFINE_ENUM_STRING(Name_, Values_)
#define SANDBOX_DEFINE_ENUM_TYPE(Name_, Values_) \
struct Name_ { \
enum Type { \
BOOST_PP_SEQ_ENUM(Values_) \
}; \
static Type const First = BOOST_PP_SEQ_HEAD(Values_); \
static Type const Last = BOOST_PP_SEQ_HEAD(BOOST_PP_SEQ_REVERSE(Values_)); \
};
#define SANDBOX_DEFINE_ENUM_STRING(Name_, Values_) \
inline char const* toString(Name_::Type value) { \
switch(value) { \
BOOST_PP_SEQ_FOR_EACH(SANDBOX_DEFINE_ENUM_TO_STRING_C, Name_, Values_) \
} \
return 0; \
}
#define SANDBOX_DEFINE_ENUM_TO_STRING_C(r, Name_, Elem_) \
case Name_::Elem_: return BOOST_PP_STRINGIZE(Elem_);
Очевидно, он работает только с "обычными" перечислениями, а не с заказными. Но потому что он определен в одном месте в коде... не требует обслуживания:)
Я не вернул бы значение в этом случае. Я бы выдал исключение, и ваше сообщение содержало бы значение вашего недействительного перечислителя. Для меня неверное значение перечислителя может показаться ошибкой.
Если вы не хотите этого делать, тогда я согласен с другими, что вы должны вместо этого возвращать std::string.
Попробуйте определить локальную статическую переменную потока http://gcc.gnu.org/onlinedocs/gcc-3.3.1/gcc/Thread-Local.html