【问题标题】:Enum to string : return the enum integer value if invalid / not found枚举到字符串:如果无效/未找到,则返回枚举整数值
【发布时间】:2012-04-16 13:41:16
【问题描述】:

所以,我为我经常使用的几个枚举实现了一个 enumToString 函数(在 SO 中经常被问到:Is there a simple way to convert C++ enum to string?Easy way to use variables of enum types as string in 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 推入堆栈。

有什么办法吗?

【问题讨论】:

  • 为什么是const char* 而不是std::string?如果std::string,那么你可以使用std::ostringstream(或类似的)。
  • 查看我对尼克回答的评论:我选择 const char * 来加快通话速度。
  • 您担心错误消息的性能?您是否实际测量过,在某些至少半典型的使用中,错误报告占用了运行时间的很大一部分案例?
  • @Gui13:使用switch 而不是map 会更快。 快得多。只要您不包含default 子句,如果缺少枚举器,任何值得其盐的编译器都会警告您。请参阅我的答案以自动生成此开关。
  • 我实际上赞成你的答案,虽然我不使用 boost(看看你有多少包含单个 switch())。这是一个更好的解决方案,因为没有默认值将优雅地解决我的问题!

标签: c++ string debugging map enums


【解决方案1】:

返回 std::string 而不是 char*

这将允许您使用std::stringstream 来生成您的消息。然后调用站点只需使用std::string 上的.c_str( ) 成员函数来获取C 样式指针(如果需要)。

【讨论】:

  • 是啊……但是不,我选择了const char *,因为编译器将简单地返回字符串的静态地址,而使用std::string 会使对函数的调用变得更重。跨度>
  • 您是否编写了一些分析代码来证明这一点?还是您只是在做先发制人的优化?
  • 拿你的赌注 :D 更重要的是,我使用 C 风格的日志记录函数,所以对函数的调用如下所示:LOG( "Event %s\n", Tools::enumCString( event ) );。重构我对该函数的所有调用以添加.c_str() 将是乏味的。我宁愿找到一个不涉及所有这些的解决方案。
  • 可以不加一个新的LOG(const char* a_fmt, const std::string&amp; a_msg) { LOG(a_fmt, a_msg.c_str()); }函数吗?
  • 好的,我考虑了您的解决方案,我可能只是将内部存储设置为std::string,并让我的enumCString() 调用enumStdString(e).c_str()。这是可以接受的。
【解决方案2】:

就个人而言,我使用 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_);

显然,它只适用于“常规”枚举,不适用于定制的枚举。但是因为它是在代码中的一个地方定义的......没有维护惩罚:)

【讨论】:

  • +1 是一个优雅的解决方案 - 但是它不能解决与处理未定义的枚举值有关的原始问题!
  • @Nick:在某种程度上,不再有任何不匹配字符串的枚举器。唯一可能出现的问题是从 int 转换为 enum 时,FirstLast 提供了可用范围。
  • 非常方便。这对明确定义值的枚举起作用的任何机会,例如enum Type { Foo = 5, Bar = -2, Team = 19 };
  • @ulatekh:如果所有版本都明确定义,那么它可以相对容易(只需将序列的每个参数扩充为一对项目);尽管对于调用者来说,它最原始的形式确实暗示了很多括号:DEF_ENUM(Enum, ((Foo, 5)(Bar, -2)(Team, 19))) 以及在它之外的额外解包级别。此时,手动声明enum 并从上述宏中提取字符串生成部分可能会更简单。结合-Wswitch(在缺少枚举器时发出警告),编译器将检查是否为您翻译了所有枚举器!
【解决方案3】:

【讨论】:

    【解决方案4】:

    在这种情况下,我不会返回值。我会抛出一个异常并让您的消息包含您的无效枚举数的值。对我来说,无效的枚举器值似乎是一个错误。

    如果您不想这样做,那么我同意其他人的观点,即您应该返回 std::string。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-18
      • 2018-11-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-11-02
      相关资源
      最近更新 更多