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.

Community
  • 1
  • 1
smihael
  • 863
  • 13
  • 29
  • *"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++. – Christian Hackl Jul 24 '15 at 12:22
  • So how would you solve my problem otherwise (switch over string tokens)? – smihael Jul 24 '15 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. – Christian Hackl Jul 24 '15 at 12:52

5 Answers5

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.

Community
  • 1
  • 1
antron
  • 3,749
  • 2
  • 17
  • 23
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.");
Richard Dally
  • 1,432
  • 2
  • 21
  • 38
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;
}
Community
  • 1
  • 1
Frerich Raabe
  • 90,689
  • 19
  • 115
  • 207
  • Isn't the code here converting *to* string, not *from* string? – antron Jul 24 '15 at 19:49
  • 1
    Good catch @antron -- I copied the code from one of my [previous answers](http://stackoverflow.com/a/28625475/91757); I fixed the code now so that it does indeed convert a string to an enum value. – Frerich Raabe Jul 25 '15 at 10:24
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;
  ...
}
CreativeMind
  • 897
  • 6
  • 19
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;
}
Shreevardhan
  • 12,233
  • 3
  • 36
  • 50
  • 1
    That still needs manual definition of the function. I included my sollution which is smiliar to this, but using preporcessor. – smihael Jul 24 '15 at 10:58