Enum to string : return the enum integer value if

2020-02-06 16:39发布

So, I implemented an enumToString function for several enums that I use a lot (often asked in SO: Is there a simple way to convert C++ enum to string?, Easy way to use variables of enum types as string in 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.

My code looks like this:

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

Now, what I want, is that when I don't find the enum in the map, to return "(unknown enum %d)", e . Which will give me the value of the enum I missed.

This way, even if I didn't add it to the map, I still have its value and I can debug my program.

I can't find a way to do that simply: a stringstream instanciated on the stack will be destroyed right after the return, a static stringstream is not thread-safe, ...

edit: of course, using a std::string as return type would allow me to format it, but I call these functions very often in my code, I figured passing a const char * pointer is faster, since I don't have to push the std::string onto the stack each time.

Any solution?

4条回答
爷、活的狠高调
2楼-- · 2020-02-06 17:03
Luminary・发光体
3楼-- · 2020-02-06 17:15

I wouldn't return a value in this case. I would throw an exception and have your message contain the value of your invalid enumerator. To me, an invalid enumerator value would seem to be an error.

If you don't want to do that, then I agree with others that you should be returning a std::string instead.

查看更多
Anthone
4楼-- · 2020-02-06 17:19

Personally, I use BOOST :)

Example of use:

SANDBOX_DEFINE_ENUM(MyEnum, (Foo)(Bar)(Team))

Will yield:

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;
}

Code:

#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_);

Obviously, it only work with "regular" enums, not with custom made ones. But because it's defined in a single place in the code... no maintenance penalty :)

查看更多
戒情不戒烟
5楼-- · 2020-02-06 17:25

Return a std::string rather than a char*.

This would allow you to use a std::stringstream to generate your message. The calling site would then just have to use the .c_str( ) member function on std::string to get the C-style pointer (if required).

查看更多
登录 后发表回答