35

Is there a simple way in C++ to convert a string to an enum (similar to Enum.Parse in C#)? A switch statement would be very long, so I was wondering if there is a simpler way to do this?

EDIT:

Thanks for all of your replies. I realized that there was a much simpler way to do it for my particular case. The strings always contained the charater 'S' followed by some number so i just did

int i = atoi(myStr.c_str() + 1);

and then did a switch on i.

Daniel
  • 6,595
  • 9
  • 38
  • 70
  • 7
    Unfortunately, you cannot even use `switch` with strings. – Ferdinand Beyer Aug 23 '11 at 15:01
  • I demonstrated a macro implementation of enum -> string mapping in [an answer to another question](http://stackoverflow.com/questions/5093460/how-to-convert-an-enum-type-variable-to-a-string/5094430#5094430). You can easily adapt it to work in the other direction as well. It's best to avoid naming the enumerators multiple times if you can. – James McNellis Aug 23 '11 at 15:05
  • See [here](http://stackoverflow.com/questions/1528374/) for a slick method using Boost to generically convert strings to enums and other integer-based types! – ulatekh Feb 21 '14 at 17:13

13 Answers13

50

A std::map<std::string, MyEnum> (or unordered_map) could do it easily. Populating the map would be just as tedious as the switch statement though.

Edit: Since C++11, populating is trivial:

static std::unordered_map<std::string,E> const table = { {"a",E::a}, {"b",E::b} };
auto it = table.find(str);
if (it != table.end()) {
  return it->second;
} else { error() }
Mark Ransom
  • 299,747
  • 42
  • 398
  • 622
  • 5
    +1 for `std::unordered_map`: For large enums, hashing is probably the simplest and fastest solution. – Ferdinand Beyer Aug 23 '11 at 15:07
  • I have an answer including code to another question about going the other way (enum to string), but it can easily be adapted to work either way. http://stackoverflow.com/a/11586083/5987 – Mark Ransom Sep 04 '14 at 04:05
  • This example won't compile prior to C++17 due to the [init-statement](https://en.cppreference.com/w/cpp/language/if) in the conditional. – dimo414 May 16 '20 at 19:26
  • 1
    @dimo414 you're right. Unfortunately that code was added by someone else long after I created the answer. I'll fix it. – Mark Ransom May 16 '20 at 23:13
  • 1
    #include – ouflak Mar 03 '22 at 14:23
27

Use std::map<std::string, Enum> and use boost::map_list_of to easily initialize it.

Example,

enum X
{
   A,
   B,
   C
};

std::map<std::string, X> xmap = boost::map_list_of("A", A)("B", B)("C",C);
Nawaz
  • 353,942
  • 115
  • 666
  • 851
13

saw this example somewhere

#include <map>
#include <string>

enum responseHeaders
{
    CONTENT_ENCODING,
    CONTENT_LENGTH,
    TRANSFER_ENCODING,
};

// String switch paridgam   
struct responseHeaderMap : public std::map<std::string, responseHeaders>
{
    responseHeaderMap()
    {
        this->operator[]("content-encoding") =  CONTENT_ENCODING;
        this->operator[]("content-length") = CONTENT_LENGTH;
        this->operator[]("transfer-encoding") = TRANSFER_ENCODING;
    };
    ~responseHeaderMap(){}
};
Merlyn Morgan-Graham
  • 58,163
  • 16
  • 128
  • 183
Guy L
  • 2,824
  • 2
  • 27
  • 37
  • 3
    @Napalm I just recompiled and tested it. you probably didn't include the and . – Guy L May 18 '14 at 09:32
  • Old but good answer. Additional question: Is it possibile to handle undefined strings ? I mean if I try to get the value for `responseHeaderMap["cookie"]`, what will be the value? (provided that "cookie" is not defined in the responseHeaderMap – bart s Nov 22 '16 at 12:04
  • 2
    As with so many such answers, this one could use an example of how to call the code, along with the expected result. To use this struct, apparently the developer must declare a variable of type responseHeaderMap and then call the [] operator on that variable. – Scott Hutchinson May 30 '17 at 22:45
  • This will work with an invocation such as: `responseHeaders r = responseHeaderMap()[str];` – dan Oct 08 '19 at 13:33
8

I use this "trick" > http://codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C

After

enum FORM {
    F_NONE = 0,
    F_BOX,
    F_CUBE,
    F_SPHERE,
};

insert

Begin_Enum_String( FORM )
{
    Enum_String( F_NONE );
    Enum_String( F_BOX );
    Enum_String( F_CUBE );
    Enum_String( F_SPHERE );
}
End_Enum_String;

It works fine, if the values in the enum are not duplicates.

Example in code

enum FORM f = ...
const std::string& str = EnumString< FORM >::From( f );

vice versa

assert( EnumString< FORM >::To( f, str ) );
Agricola
  • 572
  • 1
  • 8
  • 20
6

this worked for me:

enum NODES { Cone = 1, BaseColor = 2, NONE = 0 };

std::map<std::string, NODES> nodeMap;
nodeMap["Cone"] = NODES::Cone;
nodeMap["BaseColor"] = NODES::BaseColor;
Mara Black
  • 1,666
  • 18
  • 23
6

There is no "built-in way", but there are ways to achieve this by storing the pair value-name in an array

enum myEnum
{
    enumItem0,
    enumItem1,
    enumItem7 = 7,
    enumItem8
};

std::vector<std::pair<myEnum,std::string>>   gMap;

#define ADDITEM(x)  gMap.push_back(std::pair<myEnum,std::string>(x,#x));

.....

ADDITEM(enumItem0);
ADDITEM(enumItem1);
ADDITEM(enumItem7);
ADDITEM(enumItem8);
bluish
  • 26,356
  • 27
  • 122
  • 180
cprogrammer
  • 5,503
  • 3
  • 36
  • 56
  • 13
    Don't use vectors of pairs, use a map! – Ferdinand Beyer Aug 23 '11 at 15:15
  • Very late to the party, but the downvote because of wrong container seems petty. While map seems obvious, there may be other considerations... – hsmyers May 13 '13 at 19:44
  • 6
    @FerdinandBeyer Map allows for O(log n) look ups, but requires much larger space. A properly sorted vector of pairs allows the same speed of look ups, in a much smaller space. Map is almost certainly unnecessary for a static amount of items. – Alice Aug 08 '14 at 02:12
  • Question is string to enum, not enum to string – Merlyn Morgan-Graham Sep 22 '16 at 10:11
4

In short: there is none. In C++ enums are static values and not objects like in C#. I suggest you use a function with some if else statements.

Constantinius
  • 34,183
  • 8
  • 77
  • 85
2

It is not possible because the names are not available at runtime. During compilation each enum is replaced with the corresponding integer value.

Alexander Sulfrian
  • 3,533
  • 1
  • 16
  • 9
2

While there is no direct solution, there are a few possible workarounds.

Take a look at this question: Easy way to use variables of enum types as string in C?

Community
  • 1
  • 1
TeaWolf
  • 714
  • 5
  • 10
2

You can use macro to minimize repeating yourself. Here is the trick: Enums, Macros, Unicode and Token-Pasting

jj1
  • 155
  • 1
  • 7
0

"Additional question: Is it possibile to handle undefined strings ? I mean if I try to get the value for responseHeaderMap["cookie"], what will be the value? (provided that "cookie" is not defined in the responseHeaderMap – bart s Nov 22 '16 at 12:04"

well, you can just make check before:

auto it = responseHeaderMap.find("cookie");
if (it != responseHeaderMap.end())
{
     // "cookie" exist, can take value 
}

After "cookie" exist check, you can get it value with use:

responseHeaderMap["cookie"]

hope this help

0

No, you'll have to use an if/then construction, or use a map or hash table or some other type of associative data structure to facilitate this.

mwigdahl
  • 16,268
  • 7
  • 50
  • 64
0

You will have to map the string values (chain of characters) to a corresponding enum value (integer). Here's how you would do this in practice using std::map:

Demo

#include <cstdio>
#include <map>
#include <string_view>
#include <stdexcept>

enum class myenum
{
    content_encoding,
    content_length,
    transfer_encoding,
};

// In C++23 use a constexpr flat_map instead
const std::map<std::string_view, myenum> map_myenum = {
    {"content-encoding", myenum::content_encoding},
    {"content-length", myenum::content_length},
    {"transfer-encoding", myenum::transfer_encoding},
};

auto test(std::string_view str)
{
    try {
        switch(map_myenum.at(str)) {
            case myenum::content_encoding:
                printf("String was content_encoding\n");
                break;
            case myenum::content_length:
                printf("String was content_length\n");
                break;
            case myenum::transfer_encoding:
                printf("String was transfer_encoding\n");
                break;
        }
    } catch(const std::out_of_range& e) {
        printf("String didn't match any criteria!\n");
    }
    
}

int main()
{
    test("content-encoding");
    test("content-length");
    test("some random other stuff");
}

Output:

String was content_encoding
String was content_length
String didn't match any criteria!

A few notes:

  • We use a const map to hint to the compiler that this map isn't supposed to be changed at all. The exception handling is a necessary evil as for const maps we can only use the .at() method as it doesn't insert items if it doesn't find the key in the map. It's a bit of an unfortunate decision by the CWG in my view, as this is definitely nothing "exceptional" and we shouldn't have to pay the cost (in this case at least).
  • In C++23 we get flat_map which is more suitable in this case, as I suppose it will contain constexpr initializers and assignement which would allow us to lay out the whole map at compile time, so that we just pay the overhead of copy-pasting the final map to RAM. It's also a lot more cache-friendly.
glades
  • 3,778
  • 1
  • 12
  • 34