3

There are plenty solutions how to get text representation of an enum as string with use of C++ proprocessor (for example this topic).

However, I would like to do the conversion in opposite direction in order to mimick switch on strings.

So for example - given the following enum:

typedef enum { RIGHT, LEFT, FORWARD, BACKWARD} direction;

For small enums, one could define array of strings and use enum values to get the appropriate string, but that is bad for maintanance.

I would like to have some macro which would define a function "direction FromString(char * str)", which would return RIGHT if I have a string "RIGHT".

There are many sollutions for other programming languages like C# or Java, so I don't think that would be such a bad practice.

There is a similar question to mine, but without using preprocessor.

My current sollution (based on one of the answers to another question) looks like this:

#include <boost/preprocessor.hpp>

#define X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOENUM(r, data, elem)          \
    if (!strcmp(s, BOOST_PP_STRINGIZE( elem ))) return elem;

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS(name, enumerators)               \
    enum name {                                                              \
        BOOST_PP_SEQ_ENUM(enumerators)                                       \
    };                                                                       \
    inline const name FromString(char * s) {                                 \
         BOOST_PP_SEQ_FOR_EACH(                                              \
             X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOENUM,                   \
             name,                                                           \
             enumerators                                                     \
         )                                                                   \
      return ERR;                                                            \
  }

One can use it by:

   DEFINE_ENUM_WITH_STRING_CONVERSIONS(direction, (RIGHT)(LEFT)(...)(ERR))

The downside is that one has to include ERR to handle the case when the string does not match any of the enum names. How to avoid this?

I was thinking, that maybe a much more elegant sollution would be to use Boost Bimap.

3
  • "There are many sollutions for other programming languages like C# or Java, so I don't think that would be such a bad practice." - I don't agree. The fact that it's not bad practice in C# or Java does not necessarily translate to C++. Commented Jul 24, 2015 at 12:22
  • So how would you solve my problem otherwise (switch over string tokens)? Commented Jul 24, 2015 at 12:47
  • 1
    I don't mean to sound unhelpful, but I just don't consider this a problem in the first place. In my opinion, it's at best a very minor annoyance while you type the code, whereas the macro solutions thrown at it will cause real problems, including readability. If I were a maintenance programmer and would have to deal with a bug in your code, one of my first actions would be to eliminate the macro and convert all switch statements to if- else if chains to make it clearer what's going on. Commented Jul 24, 2015 at 12:52

5 Answers 5

2

You can try this library (disclaimer – I am the author): https://github.com/aantron/better-enums

The relevant method is _from_string (link is to docs). That method raises an exception in case of error. There is another method that returns an option, similar to boost::optional.

If you want to implement your own version, there is a description of the approach in this answer on Stack Overflow. The code in that answer includes an implementation of _from_string.

Sign up to request clarification or add additional context in comments.

Comments

1

I usually use an array to store strings (C++11 requested)

enum colors : int
{
    RED,
    GREEN,
    BLUE,
    MAX // MAX must be always at the end
};

static const char* const colorsString[] =
{
    "RED",
    "GREEN",
    "BLUE",
};

// Ensure your two "lists" size are matching
static_assert(MAX == sizeof(colorsString) / sizeof(colorsString[0]), "Colors must have the same number of elements.");

Comments

1

If you define your enum using X-macros you can easily have a function generated which implements the switch:

#include <string>

#define MOUSE_BUTTONS \
X(LeftButton, 1)   \
X(MiddleButton, 2) \
X(RightButton, 4)

enum MouseButton {
    None = 0
#define X(name, value) ,name = value
MOUSE_BUTTONS
#undef X
};

static MouseButton stringAsMouseButton( const std::string &s )
{
#define X(name, value) if ( s == #name ) return name;
MOUSE_BUTTONS
#undef X
    return None;
}

2 Comments

Isn't the code here converting to string, not from string?
Good catch @antron -- I copied the code from one of my previous answers; I fixed the code now so that it does indeed convert a string to an enum value.
0

You can create one map as below :

#define RIGHT_DIR "RIGHT"
#define LEFT_DIR "LEFT"

_directionActionMap[RIGHT_DIR] = RIGHT;
_directionActionMap[LEFT_DIR] = LEFT;
...

Then when you want to retrieve enum from string

int GetDirectionENUMFromString(const string& str)
{
  int directionEnum;

  std::map<string,int>::iterator iter;

  iter = _directionActionMap.find(str);

  if (iter != _directionActionMap.end())
  {
      directionEnum = (*iter).second;
  }
  else
  {
     directionEnum = -1;
  }

  return directionEnum; 
}

Usage :

int directionEnum = GetDirectionENUMFromString("RIGHT");

switch( directionEnum )
{
  case RIGHT:
   break;
  case LEFT:
   break;
  ...
}

Comments

0

You can perhaps

#define stringify(name) # name

direction FromString(char * s) {
    if (!strcmp(s, stringify(RIGHT)) return RIGHT;
    if (!strcmp(s, stringify(LEFT)) return LEFT;
    if (!strcmp(s, stringify(FORWARD)) return FORWARD;
    if (!strcmp(s, stringify(BACKWARD)) return BACKWARD;
    return -1;
}

1 Comment

That still needs manual definition of the function. I included my sollution which is smiliar to this, but using preporcessor.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.