Перечисление в строку: возвращает целочисленное значение перечисления, если неверно / не найдено

Итак, я реализовалenumToString функция для нескольких перечислений, которые я часто использую (часто спрашивается в SO:Есть ли простой способ конвертировать перечисление C ++ в строку?, Простой способ использовать переменные типов перечисления в виде строки в C?, ...). This makes the error messages WAY easier to debug, but I have to maintain the function to add the values that have no string description sometimes.

Мой код выглядит так:

<code>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)";
}
</code>

Теперь, что я хочу, это когда яdon't найти перечисление на карте, чтобы вернуться"(unknown enum %d)", e , Что даст мне значение перечисления, которое я пропустил.

Таким образом, даже если я не добавил его на карту, у меня все еще есть его значение, и я могу отлаживать свою программу.

Я не могу найти способ сделать это просто: поток строк, созданный в стеке, будет уничтожен сразу после возврата,static stringstream не является потокобезопасным, ...

edit: конечно, используяstd::string поскольку возвращаемый тип позволил бы мне отформатировать его, но я очень часто вызываю эти функции в своем коде, я решил передатьconst char * Указатель работает быстрее, так как мне не нужно каждый раз помещать std :: string в стек.

Любое решение?

 Gui1317 апр. 2012 г., 10:20
Я фактически проголосовал за ваш ответ, хотя я не использую повышение (просто посмотрите, сколько у вас есть для одного переключателя ()). Это более удачное решение, так как отсутствие дефолта элегантно решит мою проблему!
 Matthieu M.17 апр. 2012 г., 10:04
@ Gui13: используяswitch вместоmap будет быстрее.much Быстрее. И любой компилятор, достойный его соли, предупредит вас, если перечислитель отсутствует, если вы не включилиdefault пункт. Смотрите мой ответ для генерации этого переключателя автоматически.
 Christopher Creutzig16 апр. 2012 г., 15:53
Вы беспокоитесь о производительности своегоerror messages? Вы действительно измерили, что сообщение об ошибке занимает значительную часть времени выполнения в некотором, по крайней мере, полусинтетическом случае использования?
 hmjd16 апр. 2012 г., 15:44
Зачемconst char* и неstd::string? Еслиstd::string тогда вы могли бы использоватьstd::ostringstream (или похожие).
 Gui1316 апр. 2012 г., 15:50
Смотрите мой комментарий к ответу Ника: Ichose const char *, чтобы звонить быстрее.

Ответы на вопрос(4)

Я не вернул бы значение в этом случае. Я бы выдал исключение, и ваше сообщение содержало бы значение вашего недействительного перечислителя. Для меня неверное значение перечислителя может показаться ошибкой.

Если вы не хотите этого делать, тогда я согласен с другими, что вы должны вместо этого возвращать std :: string.

Попробуйте определить потоковую локальную статическую переменнуюhttp://gcc.gnu.org/onlinedocs/gcc-3.3.1/gcc/Thread-Local.html

Решение Вопроса

Вернутьstd::string а неchar*.

Это позволит вам использоватьstd::stringstream сгенерировать ваше сообщение. Тогда вызывающий сайт просто должен использовать.c_str( ) функция-член наstd::string получить указатель в стиле C (если требуется).

 Gui1316 апр. 2012 г., 16:01
Хорошо, я подумал о вашем решении, я мог бы просто настроить внутреннюю память какstd::string и мойenumCString() вызовenumStdString(e).c_str(), Это приемлемо.
 16 апр. 2012 г., 15:53
И вы написали код профилирования, чтобы доказать это? Или вы просто делаете упреждающую оптимизацию?
 Gui1316 апр. 2012 г., 15:50
Даааа ... но нет, я выбралconst char * потому что компилятор просто вернет статический адрес строки, а используяstd::string вызовет функцию WAY тяжелее.
 16 апр. 2012 г., 15:59
Вы не можете добавить новыйLOG(const char* a_fmt, const std::string& a_msg) { LOG(a_fmt, a_msg.c_str()); } функционировать?
 Gui1316 апр. 2012 г., 15:56
Примите вашу ставку: D Более серьезно, я использую функцию регистрации в стиле C, поэтому вызовы функций выглядят так:LOG( "Event %s\n", Tools::enumCString( event ) );, Рефакторинг ВСЕХ моих вызовов на эту функцию, чтобы добавить.c_str() было бы утомительно. Я бы скорее нашел решение, которое не включает в себя все это.

Лично я использую 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_);

Очевидно, что он работает только с «обычным» перечисления, а не с заказными. Но поскольку он определен в одном месте в коде ... нет необходимости в обслуживании :)

 21 февр. 2014 г., 19:00
Очень кстати. Любой шанс этого сработает для перечислений, значения которых определены явно, например,enum Type { Foo = 5, Bar = -2, Team = 19 };
 17 апр. 2012 г., 09:37
+1 за элегантное решение - однако оно не решает исходную проблему, связанную с обработкой неопределенных значений перечисления!
 17 апр. 2012 г., 10:03
@Nick: так и есть, больше нет перечислителей, у которых нет подходящей строки. Единственная проблема, которая может появиться, заключается в приведении от int к enum, аFirst а такжеLast предоставить полезный диапазон.
 21 февр. 2014 г., 22:38
@ulatekh: если все версии определены явно, то это может быть относительно просто (просто увеличивая каждый аргумент последовательности до пары элементов); в его самой грубой форме это подразумевает довольно много скобок для вызывающей стороны:DEF_ENUM(Enum, ((Foo, 5)(Bar, -2)(Team, 19))) и дополнительный уровень распаковки снаружи. На этом этапе может быть проще объявитьenum вручную и извлеките из вышеупомянутого макроса часть генерации строки. В сочетании с-Wswitch (который предупреждает об отсутствующих перечислителях) компилятор проверит, что все перечислители переведены для вас!

Ваш ответ на вопрос