173

How to make printf to show the values of variables which are of an enum type? For instance:

typedef enum {Linux, Apple, Windows} OS_type; 
OS_type myOS = Linux;

and what I need is something like

printenum(OS_type, "My OS is %s", myOS);

which must show a string "Linux", not an integer.

I suppose, first I have to create a value-indexed array of strings. But I don't know if that is the most beautiful way to do it. Is it possible at all?

sk8forether
  • 247
  • 2
  • 9
psihodelia
  • 29,566
  • 35
  • 108
  • 157
  • 4
    Take your pick: [here](http://stackoverflow.com/questions/147267/easy-way-to-use-variables-of-enum-types-as-string-in-c), [here](http://stackoverflow.com/questions/201593/is-there-a-simple-script-to-convert-c-enum-to-string), [here](http://stackoverflow.com/questions/9907160/how-to-convert-enum-names-to-string-in-c). It's a mess, they're all semi-duplicates. – rubenvb Feb 11 '14 at 10:25
  • [Another one](http://stackoverflow.com/questions/28828957/enum-to-string-in-modern-c-and-future-c17), this one is more complete. – bit2shift Mar 27 '16 at 00:21
  • Possible duplicate of http://stackoverflow.com/questions/207976/how-to-easily-map-c-enums-to-strings for C++ and http://stackoverflow.com/questions/9907160/how-to-convert-enum-names-to-string-in-c for C – Ciro Santilli OurBigBook.com Dec 11 '16 at 15:01
  • So many answers yet no "enum class" – Vivick Jun 23 '17 at 13:21

37 Answers37

153

The naive solution, of course, is to write a function for each enumeration that performs the conversion to string:

enum OS_type { Linux, Apple, Windows };

inline const char* ToString(OS_type v)
{
    switch (v)
    {
        case Linux:   return "Linux";
        case Apple:   return "Apple";
        case Windows: return "Windows";
        default:      return "[Unknown OS_type]";
    }
}

This, however, is a maintenance disaster. With the help of the Boost.Preprocessor library, which can be used with both C and C++ code, you can easily take advantage of the preprocessor and let it generate this function for you. The generation macro is as follows:

#include <boost/preprocessor.hpp>

#define X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOSTRING_CASE(r, data, elem)    \
    case elem : return BOOST_PP_STRINGIZE(elem);

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS(name, enumerators)                \
    enum name {                                                               \
        BOOST_PP_SEQ_ENUM(enumerators)                                        \
    };                                                                        \
                                                                              \
    inline const char* ToString(name v)                                       \
    {                                                                         \
        switch (v)                                                            \
        {                                                                     \
            BOOST_PP_SEQ_FOR_EACH(                                            \
                X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOSTRING_CASE,          \
                name,                                                         \
                enumerators                                                   \
            )                                                                 \
            default: return "[Unknown " BOOST_PP_STRINGIZE(name) "]";         \
        }                                                                     \
    }

The first macro (beginning with X_) is used internally by the second. The second macro first generates the enumeration, then generates a ToString function that takes an object of that type and returns the enumerator name as a string (this implementation, for obvious reasons, requires that the enumerators map to unique values).

In C++ you could implement the ToString function as an operator<< overload instead, but I think it's a bit cleaner to require an explicit "ToString" to convert the value to string form.

As a usage example, your OS_type enumeration would be defined as follows:

DEFINE_ENUM_WITH_STRING_CONVERSIONS(OS_type, (Linux)(Apple)(Windows))

While the macro looks at first like it is a lot of work, and the definition of OS_type looks rather foreign, remember that you have to write the macro once, then you can use it for every enumeration. You can add additional functionality to it (e.g., a string-form to enum conversion) without too much trouble, and it completely solves the maintenance problem, since you only have to provide the names once, when you invoke the macro.

The enumeration can then be used as if it were defined normally:

#include <iostream>

int main()
{
    OS_type t = Windows;
    std::cout << ToString(t) << " " << ToString(Apple) << std::endl;
}

The code snippets in this post, beginning with the #include <boost/preprocessor.hpp> line, can be compiled as posted to demonstrate the solution.

This particular solution is for C++ as it uses C++-specific syntax (e.g., no typedef enum) and function overloading, but it would be straightforward to make this work with C as well.

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • 8
    +1, The implementation machinery is scary but the end interface is hard to beat for elegance. – deft_code Feb 23 '11 at 18:39
  • Is there a way to do any kind of string manipulation in BOOST_PP_STRINGIZE, like replace "_" with "."? – User Sep 09 '11 at 01:04
  • @User: No; `BOOST_PP_STRINGIZE` simply turns a sequence of tokens into a string literal that contains those tokens. You could easily change the function to return a `std::string` though and transform the string yourself before you return it. – James McNellis Sep 09 '11 at 02:22
  • To make it return wide strings, `#include `, and replace `BOOST_PP_STRINGIZE` with `BOOST_PP_WSTRINGIZE` (and change the return type of `ToString()` to `const wchar_t*` of course). – Jörgen Sigvardsson Oct 13 '11 at 18:09
  • 4
    Is there anyway to get this to also allow you to give the enum integer values. For example, Windows would be 3, Linux 5, and Apple 7? – Mark Nov 09 '11 at 22:06
  • 4
    Yes, you can change `(Windows)` into `(Windows, 3)` then replace the `BOOST_PP_SEQ_ENUM` with a suitably written `BOOST_PP_SEQ_FOR_EACH`. I don't have an example of that handy, but I can write one if you'd like. – James McNellis Nov 09 '11 at 22:43
  • 2
    @JamesMcNellis I'd definitely like an example of a code that accomplishes what Mark asked for, would you be so kind as to show us the way? :) – Omer Raviv Dec 02 '12 at 13:49
  • 1
    Is there a way without Boost? – Cory Trese Sep 02 '13 at 04:58
  • @CoryTrese: You'd need to implement something similar to what is done by Boost.Preprocessor, which would be painful at best. – James McNellis Sep 02 '13 at 05:22
  • 1
    Is it possible to use this for the other direction too? So "Linux" would map to enum Linux? – smihael Jul 24 '15 at 09:14
  • 2
    NOTE: boost preprocessor has a hard limit of 256 elements. For larger enum's, a different solution is needed. – dshin Nov 11 '15 at 19:58
  • What a nice solution ! I just wonder why you havn't use const reference on ToString. It seems better. – MokaT Jan 03 '18 at 16:11
  • Is it possible to call ToString() without declaring enum? for example to implement a wrapper library that the user may define enum later. – Jafar Gh Mar 03 '20 at 15:58
90

There really is no beautiful way of doing this. Just set up an array of strings indexed by the enum.

If you do a lot of output, you can define an operator<< that takes an enum parameter and does the lookup for you.

bluish
  • 26,356
  • 27
  • 122
  • 180
Bo Persson
  • 90,663
  • 31
  • 146
  • 203
  • 2
    Also you can check at compile time that your array has the expected number of strings in it. – markh44 Feb 23 '11 at 18:06
  • 4
    I know I'm in the vast minority with this, but for programmers like me who don't wish to rely on massive third-party libraries and/or marco-riddled code to resolve shortcomings native to the language, I find this to be the simplest and purest solution for today's standard by far. +1 – Syndog Feb 13 '14 at 14:31
  • 35
    @Syndog Then the 56 enumerators long enum in your production code is updated by this programmer under a lot of pressure to release an overdue feature, and he forgets to update that enum-indexed array. It goes unnoticed, because the related printing facility is only used by the application debugging code. 2 month later, you are the first one to actually execute that debug code: it then gives you the wrong information, so you lose half a day building assumptions based on this wrong information, before realizing you first had to debug the debug code: the design relies on explicit duplication. – Ad N Aug 20 '15 at 08:34
  • @AdN Yes, that's an important caveat to keep in mind. If you're in an enormous rush and sorely careless as you work with a critical, unfamiliar, lengthy, change-prone, poorly tested and/or debug-only enum whose index string array lies someplace completely removed from it, without proper comments to alert a future hapless coder who's likely to lose half a day over this sort of thing, then I wholeheartedly concur. – Syndog Aug 21 '15 at 14:05
  • 2
    @AdN That design is wrong. The mapping from enum to human-readable string shouldn't be implemented as an array of strings indexed by the enum value. Your experience (presumably) shows why. The mapping should be an explitiy array of (enum,string) pairs, so if you forget to add an entry for your new enum value, you get "???" as output, but at least it won't screw up the names of all your other enums. – brewbuck Jan 15 '16 at 05:10
  • 14
    @AdN your scenario is why I prefer a function containing a switch (with no default clause) rather than an array, and to set the compiler switches in the build file to issue an error for a switch over an enum that doesn't cover all possible values. Adding a new enum entry without updating the relevant switch statements will cause a compilation error. – divegeek Nov 30 '16 at 17:36
  • 1
    @AdN Overall, I prefer a lookup-function-local static const enum-to-string unordered_map. However, with that said, if one uses an array, there is at least the option of putting a fake "biggest value" enumerator at the end of the enum and then using static_assert to assert that its value is equal to sizeof(the array)/sizeof(the_array[0]) to ensure that at least there are the same number of enumerators as array elements. This still won't catch mistakes due to reorderings though. – Some Guy Oct 27 '20 at 00:03
  • @SomeGuy You can avoid reordering mistakes with C99 syntax: `{[FIRST] = "first", [SECOND] = "second"};`. Or can't it be used with undeclared sizes? – alx - recommends codidact Nov 12 '21 at 09:57
39

This is the pre processor block

#ifndef GENERATE_ENUM_STRINGS
    #define DECL_ENUM_ELEMENT( element ) element
    #define BEGIN_ENUM( ENUM_NAME ) typedef enum tag##ENUM_NAME
    #define END_ENUM( ENUM_NAME ) ENUM_NAME; \
            char* getString##ENUM_NAME(enum tag##ENUM_NAME index);
#else
    #define DECL_ENUM_ELEMENT( element ) #element
    #define BEGIN_ENUM( ENUM_NAME ) char* gs_##ENUM_NAME [] =
    #define END_ENUM( ENUM_NAME ) ; char* getString##ENUM_NAME(enum \
            tag##ENUM_NAME index){ return gs_##ENUM_NAME [index]; }
#endif

Enum definition

BEGIN_ENUM(OsType)
{
    DECL_ENUM_ELEMENT(WINBLOWS),
    DECL_ENUM_ELEMENT(HACKINTOSH),
} END_ENUM(OsType)

Call using

getStringOsType(WINBLOWS);

Taken from here. How cool is that ? :)

Reno
  • 33,594
  • 11
  • 89
  • 102
  • 3
    This is the only solution that works when your enum has more than 256 elements. – dshin Apr 25 '16 at 23:01
  • 14
    This is both nice and horrifying. Excuse me while I go cry in the shower for a few hours. – allyourcode Jun 11 '21 at 06:13
  • Not So bad as ... ;-) https://stackoverflow.com/questions/70455489/strange-preprocessor-warnings simplest could be include same items twice, but storing dynamic objects in char * array before processing, etc. And intellisense cry a bit itself... – Jan Dec 30 '21 at 12:57
9

I have combined the James', Howard's and Éder's solutions and created a more generic implementation:

  • int value and custom string representation can be optionally defined for each enum element
  • "enum class" is used

The full code is written bellow (use "DEFINE_ENUM_CLASS_WITH_ToString_METHOD" for defining an enum) (online demo).

#include <boost/preprocessor.hpp>
#include <iostream>

// ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ implementation is taken from:
// http://lists.boost.org/boost-users/2012/09/76055.php
//
// This macro do the following:
// input:
//      (Element1, "Element 1 string repr", 2) (Element2) (Element3, "Element 3 string repr")
// output:
//      ((Element1, "Element 1 string repr", 2)) ((Element2)) ((Element3, "Element 3 string repr"))
#define HELPER1(...) ((__VA_ARGS__)) HELPER2
#define HELPER2(...) ((__VA_ARGS__)) HELPER1
#define HELPER1_END
#define HELPER2_END
#define ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ(sequence) BOOST_PP_CAT(HELPER1 sequence,_END)


// CREATE_ENUM_ELEMENT_IMPL works in the following way:
//  if (elementTuple.GetSize() == 4) {
//      GENERATE: elementTuple.GetElement(0) = elementTuple.GetElement(2)),
//  } else {
//      GENERATE: elementTuple.GetElement(0),
//  }
// Example 1:
//      CREATE_ENUM_ELEMENT_IMPL((Element1, "Element 1 string repr", 2, _))
//  generates:
//      Element1 = 2,
//
// Example 2:
//      CREATE_ENUM_ELEMENT_IMPL((Element2, _))
//  generates:
//      Element1,
#define CREATE_ENUM_ELEMENT_IMPL(elementTuple)                                          \
BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_TUPLE_SIZE(elementTuple), 4),                       \
    BOOST_PP_TUPLE_ELEM(0, elementTuple) = BOOST_PP_TUPLE_ELEM(2, elementTuple),        \
    BOOST_PP_TUPLE_ELEM(0, elementTuple)                                                \
),

// we have to add a dummy element at the end of a tuple in order to make 
// BOOST_PP_TUPLE_ELEM macro work in case an initial tuple has only one element.
// if we have a tuple (Element1), BOOST_PP_TUPLE_ELEM(2, (Element1)) macro won't compile.
// It requires that a tuple with only one element looked like (Element1,).
// Unfortunately I couldn't find a way to make this transformation, so
// I just use BOOST_PP_TUPLE_PUSH_BACK macro to add a dummy element at the end
// of a tuple, in this case the initial tuple will look like (Element1, _) what
// makes it compatible with BOOST_PP_TUPLE_ELEM macro
#define CREATE_ENUM_ELEMENT(r, data, elementTuple)                                      \
    CREATE_ENUM_ELEMENT_IMPL(BOOST_PP_TUPLE_PUSH_BACK(elementTuple, _))

#define DEFINE_CASE_HAVING_ONLY_ENUM_ELEMENT_NAME(enumName, element)                                        \
    case enumName::element : return BOOST_PP_STRINGIZE(element);
#define DEFINE_CASE_HAVING_STRING_REPRESENTATION_FOR_ENUM_ELEMENT(enumName, element, stringRepresentation)  \
    case enumName::element : return stringRepresentation;

// GENERATE_CASE_FOR_SWITCH macro generates case for switch operator.
// Algorithm of working is the following
//  if (elementTuple.GetSize() == 1) {
//      DEFINE_CASE_HAVING_ONLY_ENUM_ELEMENT_NAME(enumName, elementTuple.GetElement(0))
//  } else {
//      DEFINE_CASE_HAVING_STRING_REPRESENTATION_FOR_ENUM_ELEMENT(enumName, elementTuple.GetElement(0), elementTuple.GetElement(1))
//  }
//
// Example 1:
//      GENERATE_CASE_FOR_SWITCH(_, EnumName, (Element1, "Element 1 string repr", 2))
//  generates:
//      case EnumName::Element1 : return "Element 1 string repr";
//
// Example 2:
//      GENERATE_CASE_FOR_SWITCH(_, EnumName, (Element2))
//  generates:
//      case EnumName::Element2 : return "Element2";
#define GENERATE_CASE_FOR_SWITCH(r, enumName, elementTuple)                                                                                                 \
    BOOST_PP_IF(BOOST_PP_EQUAL(BOOST_PP_TUPLE_SIZE(elementTuple), 1),                                                                                       \
        DEFINE_CASE_HAVING_ONLY_ENUM_ELEMENT_NAME(enumName, BOOST_PP_TUPLE_ELEM(0, elementTuple)),                                                          \
        DEFINE_CASE_HAVING_STRING_REPRESENTATION_FOR_ENUM_ELEMENT(enumName, BOOST_PP_TUPLE_ELEM(0, elementTuple), BOOST_PP_TUPLE_ELEM(1, elementTuple))     \
    )


// DEFINE_ENUM_CLASS_WITH_ToString_METHOD final macro witch do the job
#define DEFINE_ENUM_CLASS_WITH_ToString_METHOD(enumName, enumElements)          \
enum class enumName {                                                           \
    BOOST_PP_SEQ_FOR_EACH(                                                      \
        CREATE_ENUM_ELEMENT,                                                    \
        0,                                                                      \
        ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ(enumElements)                     \
    )                                                                           \
};                                                                              \
inline const char* ToString(const enumName element) {                           \
        switch (element) {                                                      \
            BOOST_PP_SEQ_FOR_EACH(                                              \
                GENERATE_CASE_FOR_SWITCH,                                       \
                enumName,                                                       \
                ADD_PARENTHESES_FOR_EACH_TUPLE_IN_SEQ(enumElements)             \
            )                                                                   \
            default: return "[Unknown " BOOST_PP_STRINGIZE(enumName) "]";       \
        }                                                                       \
}

DEFINE_ENUM_CLASS_WITH_ToString_METHOD(Elements,
(Element1)
(Element2, "string representation for Element2 ")
(Element3, "Element3 string representation", 1000)
(Element4, "Element 4 string repr")
(Element5, "Element5", 1005)
(Element6, "Element6 ")
(Element7)
)
// Generates the following:
//      enum class Elements {
//          Element1, Element2, Element3 = 1000, Element4, Element5 = 1005, Element6,
//      };
//      inline const char* ToString(const Elements element) {
//          switch (element) {
//              case Elements::Element1: return "Element1";
//              case Elements::Element2: return "string representation for Element2 ";
//              case Elements::Element3: return "Element3 string representation";
//              case Elements::Element4: return "Element 4 string repr";
//              case Elements::Element5: return "Element5";
//              case Elements::Element6: return "Element6 ";
//              case Elements::Element7: return "Element7";
//              default: return "[Unknown " "Elements" "]";
//          }
//      }

int main() {
    std::cout << ToString(Elements::Element1) << std::endl;
    std::cout << ToString(Elements::Element2) << std::endl;
    std::cout << ToString(Elements::Element3) << std::endl;
    std::cout << ToString(Elements::Element4) << std::endl;
    std::cout << ToString(Elements::Element5) << std::endl;
    std::cout << ToString(Elements::Element6) << std::endl;
    std::cout << ToString(Elements::Element7) << std::endl;

    return 0;
}
PolarBear
  • 1,117
  • 15
  • 24
9

There are lots of good answers already, but magic_enum is worth a look.

It describes itself as -

Static reflection for enums (to string, from string, iteration) for modern C++, work with any enum type without any macro or boilerplate code.

Header-only C++17 library provides static reflection for enums, work with any enum type without any macro or boilerplate code.

Example usage

enum Color { RED = 2, BLUE = 4, GREEN = 8 };


Color color = Color::RED;
auto color_name = magic_enum::enum_name(color);
// color_name -> "RED"

std::string color_name{"GREEN"};
auto color = magic_enum::enum_cast<Color>(color_name);
if (color.has_value()) {
  // color.value() -> Color::GREEN
}
Community
  • 1
  • 1
thegreendroid
  • 3,239
  • 6
  • 31
  • 40
8

Use std::map<OS_type, std::string> and populate it with enum as key, and string representation as values, then you can do these:

printf("My OS is %s", enumMap[myOS].c_str());
std::cout << enumMap[myOS] ;
Nawaz
  • 353,942
  • 115
  • 666
  • 851
7

Did you try this:

#define stringify( name ) # name

enum enMyErrorValue
  {
  ERROR_INVALIDINPUT = 0,
  ERROR_NULLINPUT,
  ERROR_INPUTTOOMUCH,
  ERROR_IAMBUSY
  };

const char* enMyErrorValueNames[] = 
  {
  stringify( ERROR_INVALIDINPUT ),
  stringify( ERROR_NULLINPUT ),
  stringify( ERROR_INPUTTOOMUCH ),
  stringify( ERROR_IAMBUSY )
  };

void vPrintError( enMyErrorValue enError )
  {
  cout << enMyErrorValueNames[ enError ] << endl;
  }

int main()
  {
  vPrintError((enMyErrorValue)1);
  }

The stringify() macro can be used to turn any text in your code into a string, but only the exact text between the parentheses. There are no variable dereferencing or macro substitutions or any other sort of thing done.

http://www.cplusplus.com/forum/general/2949/

bluish
  • 26,356
  • 27
  • 122
  • 180
M.Ali
  • 101
  • 1
  • 5
  • This one shall actually be top, although just the first one of it would be sufficient enough :) – pholat Nov 02 '16 at 16:13
  • Works fine, but you should add #ifndef stringify at the top to avoid compile error. I also changed enum type as std::string as dgmz suggested it. – astarakastara Feb 24 '20 at 08:51
7

The problem with C enums is that it's not a type of it's own, like it is in C++. An enum in C is a way to map identifiers to integral values. Just that. That's why an enum value is interchangeable with integer values.

As you guess correctly, a good way is to create a mapping between the enum value and a string. For example:

char * OS_type_label[] = {
    "Linux",
    "Apple",
    "Windows"
};
Andrew
  • 738
  • 5
  • 8
  • I assumed - apparently incorrectly - the programming language is restricted to C. – Andrew Feb 23 '11 at 16:19
  • 2
    you are a bit off, `enum` *are* types in C. Integral enumeration type constants are of type `int` and not of the `enum` type through which they are defined, is perhaps what you meant to say. But i don't see at all what this has to do with the question. – Jens Gustedt Feb 23 '11 at 17:57
5

My own preference is to minimize both repetitive typing and hard to understand macros and to avoid introducing macro definitions into the general compiler space.

So, in the header file:

enum Level{
        /**
        * zero reserved for internal use
        */
        verbose = 1,
        trace,
        debug,
        info,
        warn,
        fatal
    };

static Level readLevel(const char *);

and the cpp implementation is:

 Logger::Level Logger::readLevel(const char *in) { 
 #  define MATCH(x) if (strcmp(in,#x) ==0) return x; 
    MATCH(verbose);
    MATCH(trace);
    MATCH(debug);
    MATCH(info);
    MATCH(warn);
    MATCH(fatal);
 # undef MATCH
    std::string s("No match for logging level ");
    s += in;
    throw new std::domain_error(s);
 }

Note the #undef of the macro as soon we're done with it.

gerardw
  • 5,822
  • 46
  • 39
5

There are a lot of good answers here, but I thought some people might find mine useful. I like it because the interface that you use to define the macro is about as simple as it can get. It's also handy because you don't have to include any extra libraries - it all comes with C++ and it doesn't even require a really late version. I pulled pieces from various places online so I can't take credit for all of it, but I think it's unique enough to warrant a new answer.

First make a header file... call it EnumMacros.h or something like that, and put this in it:

// Search and remove whitespace from both ends of the string
static std::string TrimEnumString(const std::string &s)
{
    std::string::const_iterator it = s.begin();
    while (it != s.end() && isspace(*it)) { it++; }
    std::string::const_reverse_iterator rit = s.rbegin();
    while (rit.base() != it && isspace(*rit)) { rit++; }
    return std::string(it, rit.base());
}

static void SplitEnumArgs(const char* szArgs, std::string Array[], int nMax)
{
    std::stringstream ss(szArgs);
    std::string strSub;
    int nIdx = 0;
    while (ss.good() && (nIdx < nMax)) {
        getline(ss, strSub, ',');
        Array[nIdx] = TrimEnumString(strSub);
        nIdx++;
    }
};
// This will to define an enum that is wrapped in a namespace of the same name along with ToString(), FromString(), and COUNT
#define DECLARE_ENUM(ename, ...) \
    namespace ename { \
        enum ename { __VA_ARGS__, COUNT }; \
        static std::string _Strings[COUNT]; \
        static const char* ToString(ename e) { \
            if (_Strings[0].empty()) { SplitEnumArgs(#__VA_ARGS__, _Strings, COUNT); } \
            return _Strings[e].c_str(); \
        } \
        static ename FromString(const std::string& strEnum) { \
            if (_Strings[0].empty()) { SplitEnumArgs(#__VA_ARGS__, _Strings, COUNT); } \
            for (int i = 0; i < COUNT; i++) { if (_Strings[i] == strEnum) { return (ename)i; } } \
            return COUNT; \
        } \
    }

Then, in your main program you can do this...

#include "EnumMacros.h"
DECLARE_ENUM(OsType, Windows, Linux, Apple)

void main() {
    OsType::OsType MyOs = OSType::Apple;
    printf("The value of '%s' is: %d of %d\n", OsType::ToString(MyOs), (int)OsType::FromString("Apple"), OsType::COUNT);
}

Where the output would be >> The value of 'Apple' is: 2 of 4

Enjoy!

Ph0t0n
  • 970
  • 9
  • 10
  • The key thing I like about this particular approach is that it works with the normal comma separated syntax of the enum (as long as it does not include any value setting assignments within the enum). In my case I had to work with an existing enum with a large number of members, so this was much easier to drop in than the boost approach. – CuriousKea Jun 14 '19 at 15:51
5

For C99 there is P99_DECLARE_ENUM in P99 that lets you simply declare enum like this:

P99_DECLARE_ENUM(color, red, green, blue);

and then use color_getname(A) to obtain a string with the color name.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
4

This simple example worked for me. Hope this helps.

#include <iostream>
#include <string>

#define ENUM_TO_STR(ENUM) std::string(#ENUM)

enum DIRECTION{NORTH, SOUTH, WEST, EAST};

int main()
{
  std::cout << "Hello, " << ENUM_TO_STR(NORTH) << "!\n";
  std::cout << "Hello, " << ENUM_TO_STR(SOUTH) << "!\n";
  std::cout << "Hello, " << ENUM_TO_STR(EAST) << "!\n";
  std::cout << "Hello, " << ENUM_TO_STR(WEST) << "!\n";
}
dgmz
  • 394
  • 3
  • 16
4

Assuming that your enum is already defined, you can create an array of pairs:

std::pair<QTask::TASK, QString> pairs [] = {
std::pair<OS_type, string>(Linux, "Linux"),
std::pair<OS_type, string>(Windows, "Windows"),
std::pair<OS_type, string>(Apple, "Apple"),
};

Now, you can create a map:

std::map<OS_type, std::string> stdmap(pairs, pairs + sizeof(pairs) / sizeof(pairs[0]));

Now, you can use the map. If your enum is changed, you have to add/remove pair from array pairs[]. I thinkk that it is the most elegant way to obtain a string from enum in C++.

Vladimir
  • 1,781
  • 13
  • 12
  • 2
    Apart from the fair comment that there's no need for Qt here, another point is one might want to use Boost's `bimap` in case one wants to parse names and turn them into enums (e.g., from an XML file). – Dmitri Nesteruk Sep 14 '14 at 19:14
  • 5
    Should **not** be using Qt types in a generic C++ question. – Vector Oct 05 '14 at 23:46
3

Here is my C++ code:

/* 
 * File:   main.cpp
 * Author: y2k1234
 *
 * Created on June 14, 2013, 9:50 AM
 */

#include <cstdlib>
#include <stdio.h>

using namespace std;


#define MESSAGE_LIST(OPERATOR)                          \
                                       OPERATOR(MSG_A), \
                                       OPERATOR(MSG_B), \
                                       OPERATOR(MSG_C)
#define GET_LIST_VALUE_OPERATOR(msg)   ERROR_##msg##_VALUE
#define GET_LIST_SRTING_OPERATOR(msg)  "ERROR_"#msg"_NAME"

enum ErrorMessagesEnum
{
   MESSAGE_LIST(GET_LIST_VALUE_OPERATOR)
};
static const char* ErrorMessagesName[] = 
{
   MESSAGE_LIST(GET_LIST_SRTING_OPERATOR)
};

int main(int argc, char** argv) 
{

    int totalMessages = sizeof(ErrorMessagesName)/4;

    for (int i = 0; i < totalMessages; i++)
    {
        if (i == ERROR_MSG_A_VALUE)
        {
                printf ("ERROR_MSG_A_VALUE => [%d]=[%s]\n", i, ErrorMessagesName[i]);
        }
        else if (i == ERROR_MSG_B_VALUE)
        {
                printf ("ERROR_MSG_B_VALUE => [%d]=[%s]\n", i, ErrorMessagesName[i]);
        }
        else if (i == ERROR_MSG_C_VALUE)
        {
                printf ("ERROR_MSG_C_VALUE => [%d]=[%s]\n", i, ErrorMessagesName[i]);
        }
        else
        {
                printf ("??? => [%d]=[%s]\n", i, ErrorMessagesName[i]);
        }
    }   

    return 0;
}

Output:

ERROR_MSG_A_VALUE => [0]=[ERROR_MSG_A_NAME]

ERROR_MSG_B_VALUE => [1]=[ERROR_MSG_B_NAME]

ERROR_MSG_C_VALUE => [2]=[ERROR_MSG_C_NAME]

RUN SUCCESSFUL (total time: 126ms)
y2k1234
  • 31
  • 1
3

My solution, not using boost:

#ifndef EN2STR_HXX_
#define EN2STR_HXX_

#define MAKE_STRING_1(str     ) #str
#define MAKE_STRING_2(str, ...) #str, MAKE_STRING_1(__VA_ARGS__)
#define MAKE_STRING_3(str, ...) #str, MAKE_STRING_2(__VA_ARGS__)
#define MAKE_STRING_4(str, ...) #str, MAKE_STRING_3(__VA_ARGS__)
#define MAKE_STRING_5(str, ...) #str, MAKE_STRING_4(__VA_ARGS__)
#define MAKE_STRING_6(str, ...) #str, MAKE_STRING_5(__VA_ARGS__)
#define MAKE_STRING_7(str, ...) #str, MAKE_STRING_6(__VA_ARGS__)
#define MAKE_STRING_8(str, ...) #str, MAKE_STRING_7(__VA_ARGS__)

#define PRIMITIVE_CAT(a, b) a##b
#define MAKE_STRING(N, ...) PRIMITIVE_CAT(MAKE_STRING_, N)     (__VA_ARGS__)


#define PP_RSEQ_N() 8,7,6,5,4,3,2,1,0
#define PP_ARG_N(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define PP_NARG_(...) PP_ARG_N(__VA_ARGS__)
#define PP_NARG( ...) PP_NARG_(__VA_ARGS__,PP_RSEQ_N())

#define MAKE_ENUM(NAME, ...) enum NAME { __VA_ARGS__ };            \
  struct NAME##_str {                                              \
    static const char * get(const NAME et) {                       \
      static const char* NAME##Str[] = {                           \
                MAKE_STRING(PP_NARG(__VA_ARGS__), __VA_ARGS__) };  \
      return NAME##Str[et];                                        \
      }                                                            \
    };

#endif /* EN2STR_HXX_ */

And here is how to use it

int main()
  {
  MAKE_ENUM(pippo, pp1, pp2, pp3,a,s,d);
  pippo c = d;
  cout << pippo_str::get(c) << "\n";
  return 0;
  }
Marco Amagliani
  • 147
  • 1
  • 4
2

A little late to the party, but here's my C++11 solution:

namespace std {
    template<> struct hash<enum_one> {
        std::size_t operator()(const enum_one & e) const {
            return static_cast<std::size_t>(e);
        }
    };
    template<> struct hash<enum_two> { //repeat for each enum type
        std::size_t operator()(const enum_two & e) const {
            return static_cast<std::size_t>(e);
        }
    };
}

const std::string & enum_name(const enum_one & e) {
    static const std::unordered_map<enum_one, const std::string> names = {
    #define v_name(n) {enum_one::n, std::string(#n)}
        v_name(value1),
        v_name(value2),
        v_name(value3)
    #undef v_name
    };
    return names.at(e);
}

const std::string & enum_name(const enum_two & e) { //repeat for each enum type
    .................
}
OneOfOne
  • 95,033
  • 20
  • 184
  • 185
2

Another late to the party, using the preprocessor:

 1  #define MY_ENUM_LIST \
 2      DEFINE_ENUM_ELEMENT(First) \
 3      DEFINE_ENUM_ELEMENT(Second) \
 4      DEFINE_ENUM_ELEMENT(Third) \
 5  
 6  //--------------------------------------
 7  #define DEFINE_ENUM_ELEMENT(name) , name
 8  enum MyEnum {
 9      Zeroth = 0
10      MY_ENUM_LIST
11  };
12  #undef DEFINE_ENUM_ELEMENT
13 
14  #define DEFINE_ENUM_ELEMENT(name) , #name
15  const char* MyEnumToString[] = {
16      "Zeroth"
17      MY_ENUM_LIST
18  };
19  #undef DEFINE_ENUM_ELEMENT
20
21  #define DEFINE_ENUM_ELEMENT(name) else if (strcmp(s, #name)==0) return name;
22  enum MyEnum StringToMyEnum(const char* s){
23      if (strcmp(s, "Zeroth")==0) return Zeroth;
24      MY_ENUM_LIST
25      return NULL;
26  }
27  #undef DEFINE_ENUM_ELEMENT

(I just put in line numbers so it's easier to talk about.) Lines 1-4 are what you edit to define the elements of the enum. (I have called it a "list macro", because it's a macro that makes a list of things. @Lundin informs me these are a well-known technique called X-macros.)

Line 7 defines the inner macro so as to fill in the actual enum declaration in lines 8-11. Line 12 undefines the inner macro (just to silence the compiler warning).

Line 14 defines the inner macro so as to create a string version of the enum element name. Then lines 15-18 generate an array that can convert an enum value to the corresponding string.

Lines 21-27 generate a function that converts a string to the enum value, or returns NULL if the string doesn't match any.

This is a little cumbersome in the way it handles the 0th element. I've actually worked around that in the past.

I admit this technique bothers people who don't want to think the preprocessor itself can be programmed to write code for you. I think it strongly illustrates the difference between readability and maintainability. The code is difficult to read, but if the enum has a few hundred elements, you can add, remove, or rearrange elements and still be sure the generated code has no errors.

Mike Dunlavey
  • 40,059
  • 14
  • 91
  • 135
  • "X macros" are rarely ever an elegant solution to any problem. In this case, it would be far more readable to simply define the macro items as `#define TEST_1 hello #define TEST_2 world` then `typedef enum { TEST_1, TEST_2 } test_t;` and then create a string look-up table that uses a stringify macro: `const char* table[]= { STRINGIFY(TEST_1), STRINGIFY(TEST_2), };` There are already multiple answers hinting at similar solutions. Far more readable. – Lundin Oct 05 '16 at 13:57
  • 1
    @Lundin: I only claim 1) this works with even the most primitive C compiler, and 2) adding or deleting an element is a 1-line edit. – Mike Dunlavey Oct 05 '16 at 14:01
  • I've posted an answer of my own: http://stackoverflow.com/a/39877228/584518. Hopefully it will save some poor soul from x macros solutions. – Lundin Oct 05 '16 at 14:50
  • 1
    I used your solution. I think it is the best. The C-syntax is still there so that you understand what happens and the list is only defined once. You could remove the 0th element, by placing the comma after the entry in your DEFINE_ENUM_ELEMENT. – isgoed May 05 '20 at 13:30
2

I needed this to work in both directions AND I frequently embed my enums inside a containing class, and so I started with the solution by James McNellis way, way at the top of these answers, but I made this solution. Note also I prefer enum class rather than just enum, which complicates the answer somewhat.

#define X_DEFINE_ENUMERATION(r, datatype, elem) case datatype::elem : return BOOST_PP_STRINGIZE(elem);

// The data portion of the FOR_EACH should be (variable type)(value)
#define X_DEFINE_ENUMERATION2(r, dataseq, elem) \
    if (BOOST_PP_SEQ_ELEM(1, dataseq) == BOOST_PP_STRINGIZE(elem) ) return BOOST_PP_SEQ_ELEM(0, dataseq)::elem;

#define DEFINE_ENUMERATION_MASTER(modifier, name, toFunctionName, enumerators)    \
    enum class name {                                                         \
        Undefined,                                                            \
        BOOST_PP_SEQ_ENUM(enumerators)                                        \
    };                                                                        \
                                                                              \
    modifier const char* ToString(const name & v)                               \
    {                                                                         \
        switch (v)                                                            \
        {                                                                     \
            BOOST_PP_SEQ_FOR_EACH(                                            \
                X_DEFINE_ENUMERATION,                                         \
                name,                                                         \
                enumerators                                                   \
            )                                                                 \
            default: return "[Unknown " BOOST_PP_STRINGIZE(name) "]";         \
        }                                                                     \
    }                                                                         \
                                                                              \
    modifier const name toFunctionName(const std::string & value)               \
    {                                                                         \
        BOOST_PP_SEQ_FOR_EACH(                                                \
            X_DEFINE_ENUMERATION2,                                            \
            (name)(value),                                                    \
            enumerators                                                       \
        )                                                                     \
        return name::Undefined;                                               \
    }

#define DEFINE_ENUMERATION(name, toFunctionName, enumerators)                 \
    DEFINE_ENUMERATION_MASTER(inline, name, toFunctionName, enumerators)

#define DEFINE_ENUMERATION_INSIDE_CLASS(name, toFunctionName, enumerators)                 \
    DEFINE_ENUMERATION_MASTER(static, name, toFunctionName, enumerators)

To use it inside a class, you could do something like this:

class ComponentStatus {
public:
    /** This is a simple bad, iffy, and good status. See other places for greater details. */
    DEFINE_ENUMERATION_INSIDE_CLASS(Status, toStatus, (RED)(YELLOW)(GREEN)
}

And I wrote a CppUnit test, which demonstrates how to use it:

void
ComponentStatusTest::testSimple() {
    ComponentStatus::Status value = ComponentStatus::Status::RED;

    const char * valueStr = ComponentStatus::ToString(value);

    ComponentStatus::Status convertedValue = ComponentStatus::toStatus(string(valueStr));

    CPPUNIT_ASSERT_EQUAL_MESSAGE("Incorrect conversion to a string.", (const char *)"RED", valueStr);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Incorrect conversion back from a string.", convertedValue, value);
}

DEFINE_ENUMERATION(Status, toStatus, (RED)(YELLOW)(GREEN))

void
ComponentStatusTest::testOutside() {
    Status value = Status::RED;

    const char * valueStr = ToString(value);

    Status convertedValue = toStatus(string(valueStr));

    CPPUNIT_ASSERT_EQUAL_MESSAGE("Incorrect conversion to a string.", (const char *)"RED", valueStr);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Incorrect conversion back from a string.", convertedValue, value);
}

You have to pick which macro to use, either DEFINE_ENUMERATION or DEFINE_ENUMERATION_INSIDE_CLASS. You'll see I used the latter when defining ComponentStatus::Status but I used the former when just defining Status. The difference is simple. Inside a class, I prefix the to/from methods as "static" and if not in a class, I use "inline". Trivial differences, but necessary.

Unfortunately, I don't think there's a clean way to avoid having to do this:

const char * valueStr = ComponentStatus::ToString(value);

although you could manually create an inline method after your class definition that simply chains to the class method, something like:

inline const char * toString(const ComponentStatus::Status value) { return ComponentStatus::ToString(value); }
Joseph Larson
  • 8,530
  • 1
  • 19
  • 36
1

Here's the Old Skool method (used to be used extensively in gcc) using just the C pre-processor. Useful if you're generating discrete data structures but need to keep the order consistent between them. The entries in mylist.tbl can of course be extended to something much more complex.

test.cpp:

enum {
#undef XX
#define XX(name, ignore) name ,
#include "mylist.tbl"
  LAST_ENUM
};

char * enum_names [] = {
#undef XX
#define XX(name, ignore) #name ,
#include "mylist.tbl"
   "LAST_ENUM"
};

And then mylist.tbl:

/*    A = enum                  */
/*    B = some associated value */
/*     A        B   */
  XX( enum_1 , 100)
  XX( enum_2 , 100 )
  XX( enum_3 , 200 )
  XX( enum_4 , 900 )
  XX( enum_5 , 500 )
TheDuke
  • 750
  • 1
  • 7
  • 22
1

To extend James' answer, someone want some example code to support enum define with int value, I also have this requirement, so here is my way:

First one the is internal use macro, which is used by FOR_EACH:

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS_EXPAND_VALUE(r, data, elem)         \
    BOOST_PP_IF(                                                                \
        BOOST_PP_EQUAL(BOOST_PP_TUPLE_SIZE(elem), 2),                           \
        BOOST_PP_TUPLE_ELEM(0, elem) = BOOST_PP_TUPLE_ELEM(1, elem),            \
        BOOST_PP_TUPLE_ELEM(0, elem) ),

And, here is the define macro:

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS(name, enumerators)                  \
    enum name {                                                                 \
        BOOST_PP_SEQ_FOR_EACH(DEFINE_ENUM_WITH_STRING_CONVERSIONS_EXPAND_VALUE, \
                              0, enumerators) };

So when using it, you may like to write like this:

DEFINE_ENUM_WITH_STRING_CONVERSIONS(MyEnum,
    ((FIRST, 1))
    ((SECOND))
    ((MAX, SECOND)) )

which will expand to:

enum MyEnum
{
    FIRST = 1,
    SECOND,
    MAX = SECOND,
};

The basic idea is to define a SEQ, which every element is a TUPLE, so we can put addition value for enum member. In FOR_EACH loop, check the item TUPLE size, if the size is 2, expand the code to KEY = VALUE, else just keep the first element of TUPLE.

Because the input SEQ is actually TUPLEs, so if you want to define STRINGIZE functions, you may need to pre-process the input enumerators first, here is the macro to do the job:

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM(r, data, elem)           \
    BOOST_PP_TUPLE_ELEM(0, elem),

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM_SEQ(enumerators)         \
    BOOST_PP_SEQ_SUBSEQ(                                                        \
        BOOST_PP_TUPLE_TO_SEQ(                                                  \
            (BOOST_PP_SEQ_FOR_EACH(                                             \
                DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM, 0, enumerators) \
            )),                                                                 \
            0,                                                                  \
            BOOST_PP_SEQ_SIZE(enumerators))

The macro DEFINE_ENUM_WITH_STRING_CONVERSIONS_FIRST_ELEM_SEQ will only keep the first element in every TUPLE, and later convert to SEQ, now modify James' code, you will have the full power.

My implementation maybe not the simplest one, so if you do not find any clean code, mine for your reference.

1

Clean, safe solution in pure standard C:

#include <stdio.h>

#define STRF(x) #x
#define STRINGIFY(x) STRF(x)

/* list of enum constants */
#define TEST_0 hello
#define TEST_1 world

typedef enum
{
  TEST_0,
  TEST_1,
  TEST_N
} test_t;

const char* test_str[]=
{
  STRINGIFY(TEST_0),
  STRINGIFY(TEST_1),
};

int main()
{  
  _Static_assert(sizeof test_str / sizeof *test_str == TEST_N, 
                 "Incorrect number of items in enum or look-up table");

  printf("%d %s\n", hello, test_str[hello]);
  printf("%d %s\n", world, test_str[world]);
  test_t x = world;
  printf("%d %s\n", x, test_str[x]);

  return 0;
}

Output

0 hello
1 world
1 world

Rationale

When solving the core problem "have enum constants with corresponding strings", a sensible programmer will come up with the following requirements:

  • Avoid code repetition ("DRY" principle).
  • The code must be scalable, maintainable and safe even if items are added or removed inside the enum.
  • All code should be of high quality: easy to read, easy to maintain.

The first requirement, and maybe also the second, can be fulfilled with various messy macro solutions such as the infamous "x macro" trick, or other forms of macro magic. The problem with such solutions is that they leave you with a completely unreadable mess of mysterious macros - they don't meet the third requirement above.

The only thing needed here is actually to have a string look-up table, which we can access by using the enum variable as index. Such a table must naturally correspond directly to the enum and vice versa. When one of them is updated, the other has to be updated too, or it will not work.


Explanation of the code

Suppose we have an enum like

typedef enum
{
  hello,
  world
} test_t;

This can be changed to

#define TEST_0 hello
#define TEST_1 world

typedef enum
{
  TEST_0,
  TEST_1,
} test_t;

With the advantage that these macro constants can now be used elsewhere, to for example generate a string look-up table. Converting a pre-processor constant to a string can be done with a "stringify" macro:

#define STRF(x) #x
#define STRINGIFY(x) STRF(x)

const char* test_str[]=
{
  STRINGIFY(TEST_0),
  STRINGIFY(TEST_1),
};

And that's it. By using hello, we get the enum constant with value 0. By using test_str[hello] we get the string "hello".

To make the enum and look-up table correspond directly, we have to ensure that they contain the very same amount of items. If someone would maintain the code and only change the enum, and not the look-up table, or vice versa, this method won't work.

The solution is to have the enum to tell you how many items it contains. There is a commonly-used C trick for this, simply add an item at the end, which only fills the purpose of telling how many items the enum has:

typedef enum
{
  TEST_0,
  TEST_1,
  TEST_N  // will have value 2, there are 2 enum constants in this enum
} test_t;

Now we can check at compile time that the number of items in the enum is as many as the number of items in the look-up table, preferably with a C11 static assert:

_Static_assert(sizeof test_str / sizeof *test_str == TEST_N, 
               "Incorrect number of items in enum or look-up table");

(There are ugly but fully-functional ways to create static asserts in older versions of the C standard too, if someone insists on using dinosaur compilers. As for C++, it supports static asserts too.)


As a side note, in C11 we can also achieve higher type safety by changing the stringify macro:

#define STRINGIFY(x) _Generic((x), int : STRF(x))

(int because enumeration constants are actually of type int, not test_t)

This will prevent code like STRINGIFY(random_stuff) from compiling.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • I understand what you're saying, but the point remains. The typical foreseeable alterations should require minimal edits (like 1 line). (I think that's the reason behind DRY.) So here, if the size of the enum is like 500, and you want to insert a new element in the middle (or remove/rename/swap), how many lines of code must you change, and how much checking must you do to be sure you didn't make a mistake? There may also be other pieces of code that do something uniform for each element of the list. – Mike Dunlavey Oct 05 '16 at 16:38
  • 1
    Thanks for telling me these are called [*X-macros*](https://en.wikipedia.org/wiki/X_Macro). I didn't know that. What I don't see is people denigrating them in general. – Mike Dunlavey Oct 05 '16 at 16:51
  • @MikeDunlavey No matter the size of the enum, you'll have to change exactly 3 lines: add a `#define`, add a reference to that define in the enum declaration and the look-up table. If you would goof up when adding those lines, the program will not compile. The numbers I added to the identifiers are by no means mandatory, you could as well write `#define APPLES hello` and `#define ORANGES world` followed by `typedef enum { APPES, ORANGES, TEST_N } test_t;` and so on. – Lundin Oct 06 '16 at 07:36
  • @MikeDunlavey Regarding X macros, the arguments against them are the same as the arguments against any function-like macros. You won't need to look far to find lots of very valid criticism against function-like macros. – Lundin Oct 06 '16 at 07:39
1

My own answer, not using boost - using my own approach without heavy define magic, and this solution has a limitation of not be able to define specific enum value.

#pragma once
#include <string>

template <class Enum>
class EnumReflect
{
public:
    static const char* getEnums() { return ""; }
};

#define DECLARE_ENUM(name, ...)                                         \
    enum name { __VA_ARGS__ };                                          \
    template <>                                                         \
    class EnumReflect<##name> {                                         \
    public:                                                             \
        static const char* getEnums() { return #__VA_ARGS__; }          \
    };

/*
    Basic usage:

    Declare enumeration:

DECLARE_ENUM( enumName,

    enumValue1,
    enumValue2,
    enumValue3,

    // comment
    enumValue4
);

    Conversion logic:

    From enumeration to string:

        printf( EnumToString(enumValue3).c_str() );

    From string to enumeration:

       enumName value;

       if( !StringToEnum("enumValue4", value) )
            printf("Conversion failed...");

    WARNING: At the moment assigning enum value to specific number is not supported.
*/

//
//  Converts enumeration to string, if not found - empty string is returned.
//
template <class T>
std::string EnumToString(T t)
{
    const char* enums = EnumReflect<T>::getEnums();
    const char *token, *next = enums - 1;
    int id = (int)t;

    do
    {
        token = next + 1;
        if (*token == ' ') token++;
        next = strchr(token, ',');
        if (!next) next = token + strlen(token);

        if (id == 0)
            return std::string(token, next);
        id--;
    } while (*next != 0);

    return std::string();
}

//
//  Converts string to enumeration, if not found - false is returned.
//
template <class T>
bool StringToEnum(const char* enumName, T& t)
{
    const char* enums = EnumReflect<T>::getEnums();
    const char *token, *next = enums - 1;
    int id = 0;

    do
    {
        token = next + 1;
        if (*token == ' ') token++;
        next = strchr(token, ',');
        if (!next) next = token + strlen(token);

        if (strncmp(token, enumName, next - token) == 0)
        {
            t = (T)id;
            return true;
        }

        id++;
    } while (*next != 0);

    return false;
}

Latest version can be found on github in here:

https://github.com/tapika/cppscriptcore/blob/master/SolutionProjectModel/EnumReflect.h

TarmoPikaro
  • 4,723
  • 2
  • 50
  • 62
1

There are many other answers to this but I think a better way is to use C++17 features and to use constexpr so that translations are done at compile time. This is type safe and we do not need to mess with macros. See below:

//enum.hpp
#include <array>
#include <string_view>

namespace Enum
{

template <class ENUM_TYPE, size_t SIZE>
constexpr ENUM_TYPE findKey(const char * value, std::array<std::pair<ENUM_TYPE, const char *>, SIZE> map, size_t index = -1)
{
    index = (index == -1) ? map.size() : index;
    return
        (index == 0) ? throw "Value not in map":
        (std::string_view(map[index - 1].second) == value) ? map[index- 1].first:
        findKey(value, map, index - 1);
};

template <class ENUM_TYPE, size_t SIZE>
constexpr const char * findValue(ENUM_TYPE key, std::array<std::pair<ENUM_TYPE, const char *>, SIZE> map, size_t index = -1)
{
    index = (index == -1) ? map.size() : index;
    return
        (index == 0) ? throw "Key not in map":
        (map[index - 1].first == key) ? map[index- 1].second:
        findValue(key, map, index - 1);
};

}

//test_enum.hpp
#include "enum.hpp"

namespace TestEnum
{
    enum class Fields
    {
        Test1,
        Test2,
        Test3,
        //This has to be at the end
        NUMBER_OF_FIELDS
    };

    constexpr std::array<std::pair<Fields, const char *>, (size_t)Fields::NUMBER_OF_FIELDS> GetMap()
    {
        std::array<std::pair<Fields, const char *>, (size_t)Fields::NUMBER_OF_FIELDS> map =
        {
            {
                    {Fields::Test1, "Test1"},
                    {Fields::Test2, "Test2"},
                    {Fields::Test3, "Test3"},
            }
        };
        return map;
    };

    constexpr Fields StringToEnum(const char * value)
    {
        return Enum::findKey(value, GetMap());
    }

    constexpr const char * EnumToString(Fields key)
    {
        return Enum::findValue(key, GetMap());
    }

}

This can then easily be used so that string key errors are detected at compile time:

#include "test_enum.hpp"

int main()
{
    auto constexpr a = TestEnum::StringToEnum("Test2"); //a = TestEnum::Fields::Test2
    auto constexpr b = TestEnum::EnumToString(TestEnum::Fields::Test1); //b = "Test1"
    auto constexpr c = TestEnum::StringToEnum("AnyStringNotInTheMap"); //compile time failure
    return 0;
}

The code is more verbose than some other solutions but we can easily do Enum to String conversion and String to Enum conversion at compile time and detect type errors. With some of the future C++20 features this can probably be simplified a bit more.

Marius
  • 380
  • 3
  • 10
1

Personally, I would go for something simple and use an operator to do so.

Considering the following enum:

enum WeekDay { MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY };

We can create an operator to output the result in an std::ostream.

std::ostream &operator<<(std::ostream &stream, const WeekDay day) {
  switch (day) {
    case MONDAY:
      stream << "Monday";
      break;
    case TUESDAY:
      stream << "Tuesday";
      break;
    case WEDNESDAY:
      stream << "Wednesday";
      break;
    case THURSDAY:
      stream << "Thursday";
      break;
    case FRIDAY:
      stream << "Friday";
      break;
    case SATURDAY:
      stream << "Saturday";
      break;
    case SUNDAY:
      stream << "Sunday";
      break;
  }

  return stream;
}

The boilerplate code is indeed pretty big compared to some other methods presented in this thread. Still, it has the avantage of being pretty straightforward and easy to use.

std::cout << "First day of the week is " << WeekDay::Monday << std::endl;
  • 1
    Personally, I think this solution is great for enums that are rather small and that are not likely to grow in size (like the days of the week). – Diego ROJAS Apr 21 '21 at 14:24
0
#include <EnumString.h>

from http://www.codeproject.com/Articles/42035/Enum-to-String-and-Vice-Versa-in-C and 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;

Works fine if values in the enum are not duplicate.

Sample code for converting an enum value to string:

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

Sample code for just the opposite:

assert( EnumString< FORM >::To( f, str ) );
Hermes
  • 756
  • 7
  • 10
0

Thanks James for your suggestion. It was very useful so I implemented the other way around to contribute in some way.

#include <iostream>
#include <boost/preprocessor.hpp>

using namespace std;

#define X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOSTRING_CASE(r, data,  elem) \
    case data::elem : return BOOST_PP_STRINGIZE(elem);

#define X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOENUM_IF(r, data, elem) \
    if (BOOST_PP_SEQ_TAIL(data) ==                                     \
            BOOST_PP_STRINGIZE(elem)) return                           \
            static_cast<int>(BOOST_PP_SEQ_HEAD(data)::elem); else

#define DEFINE_ENUM_WITH_STRING_CONVERSIONS(name, enumerators)         \
    enum class name {                                                  \
        BOOST_PP_SEQ_ENUM(enumerators)                                 \
    };                                                                 \
                                                                       \
    inline const char* ToString(name v)                                \
    {                                                                  \
        switch (v)                                                     \
        {                                                              \
            BOOST_PP_SEQ_FOR_EACH(                                     \
                X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOSTRING_CASE,   \
                name,                                                  \
                enumerators                                            \
            )                                                          \
            default: return "[Unknown " BOOST_PP_STRINGIZE(name) "]";  \
        }                                                              \
    }                                                                  \
                                                                       \
    inline int ToEnum(std::string s)                                   \
    {                                                                  \
        BOOST_PP_SEQ_FOR_EACH(                                         \
                X_DEFINE_ENUM_WITH_STRING_CONVERSIONS_TOENUM_IF,       \
                (name)(s),                                             \
                enumerators                                            \
            )                                                          \
        return -1;                                                     \
    }


DEFINE_ENUM_WITH_STRING_CONVERSIONS(OS_type, (Linux)(Apple)(Windows));

int main(void)
{
    OS_type t = OS_type::Windows;

    cout << ToString(t) << " " << ToString(OS_type::Apple) << " " << ToString(OS_type::Linux) << endl;

    cout << ToEnum("Windows") << " " << ToEnum("Apple") << " " << ToEnum("Linux") << endl;

    return 0;
}
eder
  • 1
  • 1
0

What I made is a combination of what I have seen here and in similar questions on this site. I made this is Visual Studio 2013. I have not tested it with other compilers.

First of all I define a set of macros that will do the tricks.

// concatenation macros
#define CONCAT_(A, B) A ## B
#define CONCAT(A, B)  CONCAT_(A, B)

// generic expansion and stringification macros
#define EXPAND(X)           X
#define STRINGIFY(ARG)      #ARG
#define EXPANDSTRING(ARG)   STRINGIFY(ARG)        

// number of arguments macros
#define NUM_ARGS_(X100, X99, X98, X97, X96, X95, X94, X93, X92, X91, X90, X89, X88, X87, X86, X85, X84, X83, X82, X81, X80, X79, X78, X77, X76, X75, X74, X73, X72, X71, X70, X69, X68, X67, X66, X65, X64, X63, X62, X61, X60, X59, X58, X57, X56, X55, X54, X53, X52, X51, X50, X49, X48, X47, X46, X45, X44, X43, X42, X41, X40, X39, X38, X37, X36, X35, X34, X33, X32, X31, X30, X29, X28, X27, X26, X25, X24, X23, X22, X21, X20, X19, X18, X17, X16, X15, X14, X13, X12, X11, X10, X9, X8, X7, X6, X5, X4, X3, X2, X1, N, ...) N
#define NUM_ARGS(...) EXPAND(NUM_ARGS_(__VA_ARGS__, 100, 99, 98, 97, 96, 95, 94, 93, 92, 91, 90, 89, 88, 87, 86, 85, 84, 83, 82, 81, 80, 79, 78, 77, 76, 75, 74, 73, 72, 71, 70, 69, 68, 67, 66, 65, 64, 63, 62, 61, 60, 59, 58, 57, 56, 55, 54, 53, 52, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 40, 39, 38, 37, 36, 35, 34, 33, 32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1))

// argument extraction macros
#define FIRST_ARG(ARG, ...) ARG
#define REST_ARGS(ARG, ...) __VA_ARGS__

// arguments to strings macros
#define ARGS_STR__(N, ...)  ARGS_STR_##N(__VA_ARGS__)
#define ARGS_STR_(N, ...)   ARGS_STR__(N, __VA_ARGS__)
#define ARGS_STR(...)       ARGS_STR_(NUM_ARGS(__VA_ARGS__), __VA_ARGS__)

#define ARGS_STR_1(ARG)     EXPANDSTRING(ARG)
#define ARGS_STR_2(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_1(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_3(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_2(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_4(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_3(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_5(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_4(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_6(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_5(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_7(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_6(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_8(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_7(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_9(...)     EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_8(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_10(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_9(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_11(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_10(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_12(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_11(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_13(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_12(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_14(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_13(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_15(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_14(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_16(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_15(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_17(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_16(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_18(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_17(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_19(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_18(EXPAND(REST_ARGS(__VA_ARGS__)))
#define ARGS_STR_20(...)    EXPANDSTRING(FIRST_ARG(__VA_ARGS__)), ARGS_STR_19(EXPAND(REST_ARGS(__VA_ARGS__)))
// expand until _100 or as much as you need

Next define a single macro that will create the enum class and the functions to get the strings.

#define ENUM(NAME, ...)                                                                                             \
    enum class NAME                                                                                                 \
    {                                                                                                               \
        __VA_ARGS__                                                                                                 \
    };                                                                                                              \
                                                                                                                    \
    static const std::array<std::string, NUM_ARGS(__VA_ARGS__)> CONCAT(NAME, Strings) = { ARGS_STR(__VA_ARGS__) };  \
                                                                                                                    \
    inline const std::string& ToString(NAME value)                                                                  \
    {                                                                                                               \
        return CONCAT(NAME, Strings)[static_cast<std::underlying_type<NAME>::type>(value)];                         \
    }                                                                                                               \
                                                                                                                    \
    inline std::ostream& operator<<(std::ostream& os, NAME value)                                                   \
    {                                                                                                               \
        os << ToString(value);                                                                                      \
        return os;                                                                                                  \
    }

Now defining an enum type and have strings for it becomes really easy. All you need to do is:

ENUM(MyEnumType, A, B, C);

The following lines can be used to test it.

int main()
{
    std::cout << MyEnumTypeStrings.size() << std::endl;

    std::cout << ToString(MyEnumType::A) << std::endl;
    std::cout << ToString(MyEnumType::B) << std::endl;
    std::cout << ToString(MyEnumType::C) << std::endl;

    std::cout << MyEnumType::A << std::endl;
    std::cout << MyEnumType::B << std::endl;
    std::cout << MyEnumType::C << std::endl;

    auto myVar = MyEnumType::A;
    std::cout << myVar << std::endl;
    myVar = MyEnumType::B;
    std::cout << myVar << std::endl;
    myVar = MyEnumType::C;
    std::cout << myVar << std::endl;

    return 0;
}

This will output:

3
A
B
C
A
B
C
A
B
C

I believe it is very clean and easy to use. There are some limitations:

  • You cannot assign values to the enum members.
  • The enum member's values are used as index, but that should be fine, because everything is defined in a single macro.
  • You cannot use it to define an enum type inside a class.

If you can work around this. I think, especially how to use it, this is nice and lean. Advantages:

  • Easy to use.
  • No string splitting at runtime required.
  • Separate strings are available at compile time.
  • Easy to read. The first set of macros may need an extra second, but aren't really that complicated.
jokr
  • 73
  • 1
  • 9
0

A clean solution to this problem would be:

#define RETURN_STR(val, e) {if (val == e) {return #e;}}

std::string conv_dxgi_format_to_string(int value) {
    RETURN_STR(value, DXGI_FORMAT_UNKNOWN);
    RETURN_STR(value, DXGI_FORMAT_R32G32B32A32_TYPELESS);
    RETURN_STR(value, DXGI_FORMAT_R32G32B32A32_FLOAT);
    RETURN_STR(value, DXGI_FORMAT_R32G32B32A32_UINT);
    RETURN_STR(value, DXGI_FORMAT_R32G32B32A32_SINT);
    RETURN_STR(value, DXGI_FORMAT_R32G32B32_TYPELESS);
    RETURN_STR(value, DXGI_FORMAT_R32G32B32_FLOAT);

    /* ... */

    return "<UNKNOWN>";
}

The good thing about this solution is that it is simple and also constructing the function can be done easily via copy and replace. Note that if you are going to do a lot of conversions and your enum has too many possible values, this solution might become CPU intensive.

Ali Alidoust
  • 93
  • 3
  • 5
0

I'm a bit late but here's my solution using g++ and only standard libraries. I've tried to minimise namespace pollution and remove any need to re-typing enum names.

The header file "my_enum.hpp" is:

#include <cstring>

namespace ENUM_HELPERS{
    int replace_commas_and_spaces_with_null(char* string){
        int i, N;
        N = strlen(string);
        for(i=0; i<N; ++i){
            if( isspace(string[i]) || string[i] == ','){
                string[i]='\0';
            }
        }
        return(N);
    }

    int count_words_null_delim(char* string, int tot_N){
        int i;
        int j=0;
        char last = '\0';
        for(i=0;i<tot_N;++i){
            if((last == '\0') && (string[i]!='\0')){
                ++j;
            }
            last = string[i];
        }
        return(j);
    }

    int get_null_word_offsets(char* string, int tot_N, int current_w){
        int i;
        int j=0;
        char last = '\0';
        for(i=0; i<tot_N; ++i){
            if((last=='\0') && (string[i]!='\0')){
                if(j == current_w){
                    return(i);
                }
                ++j;
            }
            last = string[i];
        }
        return(tot_N); //null value for offset
    }

    int find_offsets(int* offsets, char* string, int tot_N, int N_words){
        int i;
        for(i=0; i<N_words; ++i){
            offsets[i] = get_null_word_offsets(string, tot_N, i);
        }
        return(0);
    }
}


#define MAKE_ENUM(NAME, ...)                                            \
namespace NAME{                                                         \
    enum ENUM {__VA_ARGS__};                                            \
    char name_holder[] = #__VA_ARGS__;                                  \
    int name_holder_N =                                                 \
        ENUM_HELPERS::replace_commas_and_spaces_with_null(name_holder); \
    int N =                                                             \
        ENUM_HELPERS::count_words_null_delim(                           \
            name_holder, name_holder_N);                                \
    int offsets[] = {__VA_ARGS__};                                      \
    int ZERO =                                                          \
        ENUM_HELPERS::find_offsets(                                     \
            offsets, name_holder, name_holder_N, N);                    \
    char* tostring(int i){                                              \
       return(&name_holder[offsets[i]]);                                \
    }                                                                   \
}

Example of use:

#include <cstdio>
#include "my_enum.hpp"

MAKE_ENUM(Planets, MERCURY, VENUS, EARTH, MARS)

int main(int argc, char** argv){    
    Planets::ENUM a_planet = Planets::EARTH;
    printf("%s\n", Planets::tostring(Planets::MERCURY));
    printf("%s\n", Planets::tostring(a_planet));
}

This will output:

MERCURY
EARTH

You only have to define everything once, your namespace shouldn't be polluted, and all of the computation is only done once (the rest is just lookups). However, you don't get the type-safety of enum classes (they are still just short integers), you cannot assign values to the enums, you have to define enums somewhere you can define namespaces (e.g. globally).

I'm not sure how good the performance on this is, or if it's a good idea (I learnt C before C++ so my brain still works that way). If anyone knows why this is a bad idea feel free to point it out.

0

It's 2017 but the question is still alive

Yet another way:

#include <iostream>

#define ERROR_VALUES \
ERROR_VALUE(NO_ERROR, 0, "OK") \
ERROR_VALUE(FILE_NOT_FOUND, 1, "Not found") \
ERROR_VALUE(LABEL_UNINITIALISED, 2, "Uninitialized usage")

enum Error
{
#define ERROR_VALUE(NAME, VALUE, TEXT) NAME = VALUE,
    ERROR_VALUES
#undef ERROR_VALUE
};

inline std::ostream& operator<<(std::ostream& os, Error err)
{
    int errVal = static_cast<int>(err);
    switch (err)
    {
#define ERROR_VALUE(NAME, VALUE, TEXT) case NAME: return os << "[" << errVal << "]" << #NAME << ", " << TEXT;
    ERROR_VALUES
#undef ERROR_VALUE
    default:
        // If the error value isn't found (shouldn't happen)
        return os << errVal;
    }
}

int main() {
    std::cout << "Error: " << NO_ERROR << std::endl;
    std::cout << "Error: " << FILE_NOT_FOUND << std::endl;
    std::cout << "Error: " << LABEL_UNINITIALISED << std::endl;
    return 0;
}

Outputs:

Error: [0]NO_ERROR, OK
Error: [1]FILE_NOT_FOUND, Not found
Error: [2]LABEL_UNINITIALISED, Uninitialized usage
eungenue
  • 71
  • 3
0
#pragma once

#include <string>
#include <vector>
#include <sstream>
#include <algorithm>

namespace StringifyEnum
{
static std::string TrimEnumString(const std::string &s)
{
    std::string::const_iterator it = s.begin();
    while (it != s.end() && isspace(*it)) { it++; }
    std::string::const_reverse_iterator rit = s.rbegin();
    while (rit.base() != it && isspace(*rit)) { ++rit; }
    return std::string(it, rit.base());
}

static std::vector<std::string> SplitEnumArgs(const char* szArgs, int     nMax)
{
    std::vector<std::string> enums;
    std::stringstream ss(szArgs);
    std::string strSub;
    int nIdx = 0;
    while (ss.good() && (nIdx < nMax)) {
        getline(ss, strSub, ',');
        enums.push_back(StringifyEnum::TrimEnumString(strSub));
        ++nIdx;
    }
    return std::move(enums);
}    
}

#define DECLARE_ENUM_SEQ(ename, n, ...) \
    enum class ename { __VA_ARGS__ }; \
    const int MAX_NUMBER_OF_##ename(n); \
    static std::vector<std::string> ename##Strings = StringifyEnum::SplitEnumArgs(#__VA_ARGS__, MAX_NUMBER_OF_##ename); \
    inline static std::string ename##ToString(ename e) { \
        return ename##Strings.at((int)e); \
    } \
    inline static ename StringTo##ename(const std::string& en) { \
        const auto it = std::find(ename##Strings.begin(), ename##Strings.end(), en); \
        if (it != ename##Strings.end()) \
            return (ename) std::distance(ename##Strings.begin(), it); \
        throw std::runtime_error("Could not resolve string enum value");     \
    }

This is an elaborated class extended enum version...it does not add any other enum value other than those provided.

Usage: DECLARE_ENUM_SEQ(CameraMode, (3), Fly, FirstPerson, PerspectiveCorrect)

Michal Turlik
  • 192
  • 2
  • 6
0

In c++ like this:

enum OS_type{Linux, Apple, Windows};

std::string ToString( const OS_type v )
{
  const std::map< OS_type, std::string > lut =
    boost::assign::map_list_of( Linux, "Linux" )(Apple, "Apple )( Windows,"Windows");
  std::map< OS_type, std::string >::const_iterator it = lut.find( v );
  if ( lut.end() != it )
    return it->second;
  return "NOT FOUND";
}
BЈовић
  • 62,405
  • 41
  • 173
  • 273
0

Yet another dependency-free standard-compilant preprocessor-based solution, which will generate constexpr to_string founction for a given enum (and doesn't require any code changes for existing enum) is here.

Usage:

#include "generate_to_string.hpp"

enum class test_enum
{
    value1,
    value2
};


GENERATE_TO_STRING(test_enum, value1, value2) // emits warning if not all values are listed

const char* foo(test_enum v)
{
    return to_string(v);
}

A full code taken from the gist to copy&paste:

// workaround for old msvc preprocessor:
// https://stackoverflow.com/questions/5134523/msvc-doesnt-expand-va-args-correctly
#define ETS_EXP(x) x

#define ETS_CASE(e, x)                                                         \
    case e::x:                                                                 \
        return #x;

#define ETS_FE_0(e)
#define ETS_FE_1(e, x) ETS_CASE(e, x)
#define ETS_FE_2(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_1(e, __VA_ARGS__))
#define ETS_FE_3(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_2(e, __VA_ARGS__))
#define ETS_FE_4(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_3(e, __VA_ARGS__))
#define ETS_FE_5(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_4(e, __VA_ARGS__))
#define ETS_FE_6(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_5(e, __VA_ARGS__))
#define ETS_FE_7(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_6(e, __VA_ARGS__))
#define ETS_FE_8(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_7(e, __VA_ARGS__))
#define ETS_FE_9(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_8(e, __VA_ARGS__))
#define ETS_FE_10(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_9(e, __VA_ARGS__))
#define ETS_FE_11(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_10(e, __VA_ARGS__))
#define ETS_FE_12(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_11(e, __VA_ARGS__))
#define ETS_FE_13(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_12(e, __VA_ARGS__))
#define ETS_FE_14(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_13(e, __VA_ARGS__))
#define ETS_FE_15(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_14(e, __VA_ARGS__))
#define ETS_FE_16(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_15(e, __VA_ARGS__))
#define ETS_FE_17(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_16(e, __VA_ARGS__))
#define ETS_FE_18(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_17(e, __VA_ARGS__))
#define ETS_FE_19(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_18(e, __VA_ARGS__))
#define ETS_FE_20(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_19(e, __VA_ARGS__))
#define ETS_FE_21(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_20(e, __VA_ARGS__))
#define ETS_FE_22(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_21(e, __VA_ARGS__))
#define ETS_FE_23(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_22(e, __VA_ARGS__))
#define ETS_FE_24(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_23(e, __VA_ARGS__))
#define ETS_FE_25(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_24(e, __VA_ARGS__))
#define ETS_FE_26(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_25(e, __VA_ARGS__))
#define ETS_FE_27(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_26(e, __VA_ARGS__))
#define ETS_FE_28(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_27(e, __VA_ARGS__))
#define ETS_FE_29(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_28(e, __VA_ARGS__))
#define ETS_FE_30(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_29(e, __VA_ARGS__))
#define ETS_FE_31(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_30(e, __VA_ARGS__))
#define ETS_FE_32(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_31(e, __VA_ARGS__))
#define ETS_FE_33(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_32(e, __VA_ARGS__))
#define ETS_FE_34(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_33(e, __VA_ARGS__))
#define ETS_FE_35(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_34(e, __VA_ARGS__))
#define ETS_FE_36(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_35(e, __VA_ARGS__))
#define ETS_FE_37(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_36(e, __VA_ARGS__))
#define ETS_FE_38(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_37(e, __VA_ARGS__))
#define ETS_FE_39(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_38(e, __VA_ARGS__))
#define ETS_FE_40(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_39(e, __VA_ARGS__))
#define ETS_FE_41(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_40(e, __VA_ARGS__))
#define ETS_FE_42(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_41(e, __VA_ARGS__))
#define ETS_FE_43(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_42(e, __VA_ARGS__))
#define ETS_FE_44(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_43(e, __VA_ARGS__))
#define ETS_FE_45(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_44(e, __VA_ARGS__))
#define ETS_FE_46(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_45(e, __VA_ARGS__))
#define ETS_FE_47(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_46(e, __VA_ARGS__))
#define ETS_FE_48(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_47(e, __VA_ARGS__))
#define ETS_FE_49(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_48(e, __VA_ARGS__))
#define ETS_FE_50(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_49(e, __VA_ARGS__))
#define ETS_FE_51(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_50(e, __VA_ARGS__))
#define ETS_FE_52(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_51(e, __VA_ARGS__))
#define ETS_FE_53(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_52(e, __VA_ARGS__))
#define ETS_FE_54(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_53(e, __VA_ARGS__))
#define ETS_FE_55(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_54(e, __VA_ARGS__))
#define ETS_FE_56(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_55(e, __VA_ARGS__))
#define ETS_FE_57(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_56(e, __VA_ARGS__))
#define ETS_FE_58(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_57(e, __VA_ARGS__))
#define ETS_FE_59(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_58(e, __VA_ARGS__))
#define ETS_FE_60(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_59(e, __VA_ARGS__))
#define ETS_FE_61(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_60(e, __VA_ARGS__))
#define ETS_FE_62(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_61(e, __VA_ARGS__))
#define ETS_FE_63(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_62(e, __VA_ARGS__))
#define ETS_FE_64(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_63(e, __VA_ARGS__))
#define ETS_FE_65(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_64(e, __VA_ARGS__))
#define ETS_FE_66(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_65(e, __VA_ARGS__))
#define ETS_FE_67(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_66(e, __VA_ARGS__))
#define ETS_FE_68(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_67(e, __VA_ARGS__))
#define ETS_FE_69(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_68(e, __VA_ARGS__))
#define ETS_FE_70(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_69(e, __VA_ARGS__))
#define ETS_FE_71(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_70(e, __VA_ARGS__))
#define ETS_FE_72(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_71(e, __VA_ARGS__))
#define ETS_FE_73(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_72(e, __VA_ARGS__))
#define ETS_FE_74(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_73(e, __VA_ARGS__))
#define ETS_FE_75(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_74(e, __VA_ARGS__))
#define ETS_FE_76(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_75(e, __VA_ARGS__))
#define ETS_FE_77(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_76(e, __VA_ARGS__))
#define ETS_FE_78(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_77(e, __VA_ARGS__))
#define ETS_FE_79(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_78(e, __VA_ARGS__))
#define ETS_FE_80(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_79(e, __VA_ARGS__))
#define ETS_FE_81(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_80(e, __VA_ARGS__))
#define ETS_FE_82(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_81(e, __VA_ARGS__))
#define ETS_FE_83(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_82(e, __VA_ARGS__))
#define ETS_FE_84(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_83(e, __VA_ARGS__))
#define ETS_FE_85(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_84(e, __VA_ARGS__))
#define ETS_FE_86(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_85(e, __VA_ARGS__))
#define ETS_FE_87(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_86(e, __VA_ARGS__))
#define ETS_FE_88(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_87(e, __VA_ARGS__))
#define ETS_FE_89(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_88(e, __VA_ARGS__))
#define ETS_FE_90(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_89(e, __VA_ARGS__))
#define ETS_FE_91(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_90(e, __VA_ARGS__))
#define ETS_FE_92(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_91(e, __VA_ARGS__))
#define ETS_FE_93(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_92(e, __VA_ARGS__))
#define ETS_FE_94(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_93(e, __VA_ARGS__))
#define ETS_FE_95(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_94(e, __VA_ARGS__))
#define ETS_FE_96(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_95(e, __VA_ARGS__))
#define ETS_FE_97(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_96(e, __VA_ARGS__))
#define ETS_FE_98(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_97(e, __VA_ARGS__))
#define ETS_FE_99(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_98(e, __VA_ARGS__))
#define ETS_FE_100(e, x, ...) ETS_CASE(e, x) ETS_EXP(ETS_FE_99(e, __VA_ARGS__))

#define ETS_MATCH_ARGS(                                                        \
    ign0, ign1, ign2, ign3, ign4, ign5, ign6, ign7, ign8, ign9, ign10, ign11,  \
    ign12, ign13, ign14, ign15, ign16, ign17, ign18, ign19, ign20, ign21,      \
    ign22, ign23, ign24, ign25, ign26, ign27, ign28, ign29, ign30, ign31,      \
    ign32, ign33, ign34, ign35, ign36, ign37, ign38, ign39, ign40, ign41,      \
    ign42, ign43, ign44, ign45, ign46, ign47, ign48, ign49, ign50, ign51,      \
    ign52, ign53, ign54, ign55, ign56, ign57, ign58, ign59, ign60, ign61,      \
    ign62, ign63, ign64, ign65, ign66, ign67, ign68, ign69, ign70, ign71,      \
    ign72, ign73, ign74, ign75, ign76, ign77, ign78, ign79, ign80, ign81,      \
    ign82, ign83, ign84, ign85, ign86, ign87, ign88, ign89, ign90, ign91,      \
    ign92, ign93, ign94, ign95, ign96, ign97, ign98, ign99, ign100, name, ...) \
    name

#define ETS_FOR_EACH(e, ...)                                                   \
    ETS_EXP(ETS_MATCH_ARGS(                                                    \
        _0, __VA_ARGS__, ETS_FE_100, ETS_FE_99, ETS_FE_98, ETS_FE_97,          \
        ETS_FE_96, ETS_FE_95, ETS_FE_94, ETS_FE_93, ETS_FE_92, ETS_FE_91,      \
        ETS_FE_90, ETS_FE_89, ETS_FE_88, ETS_FE_87, ETS_FE_86, ETS_FE_85,      \
        ETS_FE_84, ETS_FE_83, ETS_FE_82, ETS_FE_81, ETS_FE_80, ETS_FE_79,      \
        ETS_FE_78, ETS_FE_77, ETS_FE_76, ETS_FE_75, ETS_FE_74, ETS_FE_73,      \
        ETS_FE_72, ETS_FE_71, ETS_FE_70, ETS_FE_69, ETS_FE_68, ETS_FE_67,      \
        ETS_FE_66, ETS_FE_65, ETS_FE_64, ETS_FE_63, ETS_FE_62, ETS_FE_61,      \
        ETS_FE_60, ETS_FE_59, ETS_FE_58, ETS_FE_57, ETS_FE_56, ETS_FE_55,      \
        ETS_FE_54, ETS_FE_53, ETS_FE_52, ETS_FE_51, ETS_FE_50, ETS_FE_49,      \
        ETS_FE_48, ETS_FE_47, ETS_FE_46, ETS_FE_45, ETS_FE_44, ETS_FE_43,      \
        ETS_FE_42, ETS_FE_41, ETS_FE_40, ETS_FE_39, ETS_FE_38, ETS_FE_37,      \
        ETS_FE_36, ETS_FE_35, ETS_FE_34, ETS_FE_33, ETS_FE_32, ETS_FE_31,      \
        ETS_FE_30, ETS_FE_29, ETS_FE_28, ETS_FE_27, ETS_FE_26, ETS_FE_25,      \
        ETS_FE_24, ETS_FE_23, ETS_FE_22, ETS_FE_21, ETS_FE_20, ETS_FE_19,      \
        ETS_FE_18, ETS_FE_17, ETS_FE_16, ETS_FE_15, ETS_FE_14, ETS_FE_13,      \
        ETS_FE_12, ETS_FE_11, ETS_FE_10, ETS_FE_9, ETS_FE_8, ETS_FE_7,         \
        ETS_FE_6, ETS_FE_5, ETS_FE_4, ETS_FE_3, ETS_FE_2, ETS_FE_1,            \
        ETS_FE_0)(e, __VA_ARGS__))

#define GENERATE_TO_STRING(enum_type, ...)                                     \
    constexpr const char* to_string(enum_type val)                             \
    {                                                                          \
        switch (val)                                                           \
        {                                                                      \
            ETS_FOR_EACH(enum_type, __VA_ARGS__)                               \
        }                                                                      \
        return "<unknown>";                                                    \
    }

We hope C++ built-in reflection solution that rule them all will finally get into C++26 (now we know it won't happen as for C++23).

Mariusz Jaskółka
  • 4,137
  • 2
  • 21
  • 47
0

Expanding on @Reno's answer, here is a working example,

#include <stdio.h>

//debug macro, keep it defined or undefine
#define DEBUG 
//#undef DEBUG

#ifndef DEBUG
        #define DECL_ENUM_ELEMENT( element ) element
        #define BEGIN_ENUM( ENUM_NAME ) typedef enum
        #define END_ENUM( ENUM_NAME )  ENUM_NAME;
#else
        #define DECL_ENUM_ELEMENT( element ) #element
        #define BEGIN_ENUM( ENUM_NAME ) const char* gs_##ENUM_NAME [] =
        #define END_ENUM( ENUM_NAME ); \
                                int gs_##ENUM_NAME##size = sizeof(gs_##ENUM_NAME)/sizeof(gs_##ENUM_NAME[0]); \
                                const char* MatchEnumToString##ENUM_NAME(int  index) { \
                                if (index > (gs_##ENUM_NAME##size - 1) || index < 0) \
                                { \
                                       return "ERR: invalid"; \
                                }  \
                                else \
                                       return gs_##ENUM_NAME [index]; \
                                }
#endif                            

BEGIN_ENUM(Days)
{
    DECL_ENUM_ELEMENT(sunday),
    DECL_ENUM_ELEMENT(monday),
    DECL_ENUM_ELEMENT(tuesday),
    DECL_ENUM_ELEMENT(wednesday),
    DECL_ENUM_ELEMENT(thursday),
    DECL_ENUM_ELEMENT(friday),
    DECL_ENUM_ELEMENT(saturday)
}
END_ENUM(Days)

BEGIN_ENUM(fruit)
{
    DECL_ENUM_ELEMENT(apple),
    DECL_ENUM_ELEMENT(orange),
    DECL_ENUM_ELEMENT(mango)
}
END_ENUM(fruit)

void match_etos( int index )
{
#ifdef DEBUG
            printf("Day is %s ,", MatchEnumToStringDays(index) );
            printf("Fruit is %s\n", MatchEnumToStringfruit(index) );
#else
            printf("disabled match_etos, index: %d\n", index);
#endif                      
}

int main()
{
        match_etos(0);
        match_etos(1);
        match_etos(2);
        match_etos(3);
        match_etos(4);
        match_etos(5);
        match_etos(6);
        match_etos(-43);
#ifdef DEBUG        
        printf("gs_Dayssize %d, gs_fruitsize %d\n", gs_Dayssize, gs_fruitsize);
#endif
        return 0;
}

Compile above example with

g++ <savedfilename>.cpp
./a.out

my output when DEBUG is defined,

Day is sunday ,Fruit is apple
Day is monday ,Fruit is orange
Day is tuesday ,Fruit is mango
Day is wednesday ,Fruit is ERR: invalid
Day is thursday ,Fruit is ERR: invalid
Day is friday ,Fruit is ERR: invalid
Day is saturday ,Fruit is ERR: invalid
Day is ERR: invalid ,Fruit is ERR: invalid
gs_Dayssize 7, gs_fruitsize 3

When DEBUG is not defined,

disabled match_etos, index: 0
disabled match_etos, index: 1
disabled match_etos, index: 2
disabled match_etos, index: 3
disabled match_etos, index: 4
disabled match_etos, index: 5
disabled match_etos, index: 6
disabled match_etos, index: -43

if DEBUG macro is defined it will create an array, if its not defined original enum will be created after pre-processing.

mrigendra
  • 1,472
  • 3
  • 19
  • 33
0

https://godbolt.org/z/c6rK5ErMc


ENUM(bfield,
    one = 1,
    two = 2,
    four = 4,
    eight = 8,
    sixteen = 16,
    sixteen2 = 32
);

ENUM(Enum,
    one,
    two,
    four,
    eight,
    sixteen,
    sixteen2
);

int main()
{
    std::cout << "bfield " << enumToString(static_cast<bfield>(1 | 2 | 4)) << "\n";
    std::cout << "isBitField " << isBitField<bfield> << "\n";
    std::cout << "Enum " << enumToString(Enum::sixteen) << "\n";
    std::cout << "isBitField " << isBitField<Enum> << "\n";

    return 0;
}
Program returned: 0
bfield { one, two, four }
isBitField 1
Enum sixteen
isBitField 0
X-Ray
  • 11
  • 2
0

XMACROS

xmacros.h:

#ifdef XDEF
//   name
XDEF(Linux)
XDEF(Apple)
XDEF(Windows)
#undef XDEF
#endif

code:

typedef enum {
    #define XDEF(n) n,
    #include "xmacros.h"
} OS_type;


static const char* OS_name[] = {
    #define XDEF(n) #n,
    #include "xmacros.h"
};

OS_type myOS = Linux;

printf("%s\n", OS_name[myOS]);
  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Apr 27 '23 at 00:37
-1

I have added one answer, which was not supporting enumeration values, now added support which supports enumeration value assignment as well. Like in previous solution this one uses minimum define magic.

Here is header file:

#pragma once
#include <string>
#include <map>
#include <regex>

template <class Enum>
class EnumReflect
{
public:
    static const char* getEnums() { return ""; }
};

//
//  Just a container for each enumeration type.
//
template <class Enum>
class EnumReflectBase
{
public:
    static std::map<std::string, int> enum2int;
    static std::map<int, std::string> int2enum;

    static void EnsureEnumMapReady( const char* enumsInfo )
    {
        if (*enumsInfo == 0 || enum2int.size() != 0 )
            return;

        // Should be called once per each enumeration.
        std::string senumsInfo(enumsInfo);
        std::regex re("^([a-zA-Z_][a-zA-Z0-9_]+) *=? *([^,]*)(,|$) *");     // C++ identifier to optional " = <value>"
        std::smatch sm;
        int value = 0;

        for (; regex_search(senumsInfo, sm, re); senumsInfo = sm.suffix(), value++)
        {
            string enumName = sm[1].str();
            string enumValue = sm[2].str();

            if (enumValue.length() != 0)
                value = atoi(enumValue.c_str());

            enum2int[enumName] = value;
            int2enum[value] = enumName;
        }
    }
};

template <class Enum>
std::map<std::string, int> EnumReflectBase<Enum>::enum2int;

template <class Enum>
std::map<int, std::string> EnumReflectBase<Enum>::int2enum;


#define DECLARE_ENUM(name, ...)                                         \
    enum name { __VA_ARGS__ };                                          \
    template <>                                                         \
    class EnumReflect<##name>: public EnumReflectBase<##name> {         \
    public:                                                             \
        static const char* getEnums() { return #__VA_ARGS__; }          \
    };




/*
    Basic usage:

    Declare enumeration:

DECLARE_ENUM( enumName,

    enumValue1,
    enumValue2,
    enumValue3 = 5,

    // comment
    enumValue4
);

    Conversion logic:

    From enumeration to string:

        printf( EnumToString(enumValue3).c_str() );

    From string to enumeration:

       enumName value;

       if( !StringToEnum("enumValue4", value) )
            printf("Conversion failed...");
*/

//
//  Converts enumeration to string, if not found - empty string is returned.
//
template <class T>
std::string EnumToString(T t)
{
    EnumReflect<T>::EnsureEnumMapReady(EnumReflect<T>::getEnums());
    auto& int2enum = EnumReflect<T>::int2enum;
    auto it = int2enum.find(t);

    if (it == int2enum.end())
        return "";

    return it->second;
}

//
//  Converts string to enumeration, if not found - false is returned.
//
template <class T>
bool StringToEnum(const char* enumName, T& t)
{
    EnumReflect<T>::EnsureEnumMapReady(EnumReflect<T>::getEnums());
    auto& enum2int = EnumReflect<T>::enum2int;
    auto it = enum2int.find(enumName);

    if (it == enum2int.end())
        return false;

    t = (T) it->second;
    return true;
}

And here is example test application:

DECLARE_ENUM(TestEnum,
    ValueOne,
    ValueTwo,
    ValueThree = 5,
    ValueFour = 7
);

DECLARE_ENUM(TestEnum2,
    ValueOne2 = -1,
    ValueTwo2,
    ValueThree2 = -4,
    ValueFour2
);

void main(void)
{
    string sName1 = EnumToString(ValueOne);
    string sName2 = EnumToString(ValueTwo);
    string sName3 = EnumToString(ValueThree);
    string sName4 = EnumToString(ValueFour);

    TestEnum t1, t2, t3, t4, t5 = ValueOne;
    bool b1 = StringToEnum(sName1.c_str(), t1);
    bool b2 = StringToEnum(sName2.c_str(), t2);
    bool b3 = StringToEnum(sName3.c_str(), t3);
    bool b4 = StringToEnum(sName4.c_str(), t4);
    bool b5 = StringToEnum("Unknown", t5);

    string sName2_1 = EnumToString(ValueOne2);
    string sName2_2 = EnumToString(ValueTwo2);
    string sName2_3 = EnumToString(ValueThree2);
    string sName2_4 = EnumToString(ValueFour2);

    TestEnum2 t2_1, t2_2, t2_3, t2_4, t2_5 = ValueOne2;
    bool b2_1 = StringToEnum(sName2_1.c_str(), t2_1);
    bool b2_2 = StringToEnum(sName2_2.c_str(), t2_2);
    bool b2_3 = StringToEnum(sName2_3.c_str(), t2_3);
    bool b2_4 = StringToEnum(sName2_4.c_str(), t2_4);
    bool b2_5 = StringToEnum("Unknown", t2_5);

Updated version of same header file will be kept here:

https://github.com/tapika/cppreflect/blob/master/cppreflect/enumreflect.h

TarmoPikaro
  • 4,723
  • 2
  • 50
  • 62