I just learned of X-Macros. What real-world uses of X-Macros have you seen? When are they the right tool for the job?
I discovered X-macros a couple of years ago when I started making use of function pointers in my code. I am an embedded programmer and I use state machines frequently. Often I would write code like this:
/* declare an enumeration of state codes */
enum{ STATE0, STATE1, STATE2, ... , STATEX, NUM_STATES};
/* declare a table of function pointers */
p_func_t jumptable[NUM_STATES] = {func0, func1, func2, ... , funcX};
The problem was that I considered it very error prone to have to maintain the ordering of my function pointer table such that it matched the ordering of my enumeration of states.
A friend of mine introduced me to X-macros and it was like a light-bulb went off in my head. Seriously, where have you been all my life x-macros!
So now I define the following table:
#define STATE_TABLE \
ENTRY(STATE0, func0) \
ENTRY(STATE1, func1) \
ENTRY(STATE2, func2) \
...
ENTRY(STATEX, funcX) \
And I can use it as follows:
enum
{
#define ENTRY(a,b) a,
STATE_TABLE
#undef ENTRY
NUM_STATES
};
and
p_func_t jumptable[NUM_STATES] =
{
#define ENTRY(a,b) b,
STATE_TABLE
#undef ENTRY
};
as a bonus, I can also have the pre-processor build my function prototypes as follows:
#define ENTRY(a,b) static void b(void);
STATE_TABLE
#undef ENTRY
Another usage is to declare and initialize registers
#define IO_ADDRESS_OFFSET (0x8000)
#define REGISTER_TABLE\
ENTRY(reg0, IO_ADDRESS_OFFSET + 0, 0x11)\
ENTRY(reg1, IO_ADDRESS_OFFSET + 1, 0x55)\
ENTRY(reg2, IO_ADDRESS_OFFSET + 2, 0x1b)\
...
ENTRY(regX, IO_ADDRESS_OFFSET + X, 0x33)\
/* declare the registers (where _at_ is a compiler specific directive) */
#define ENTRY(a, b, c) volatile uint8_t a _at_ b:
REGISTER_TABLE
#undef ENTRY
/* initialize registers */
#define ENTRY(a, b, c) a = c;
REGISTER_TABLE
#undef ENTRY
My favourite usage however is when it comes to communication handlers
First I create a comms table, containing each command name and code:
#define COMMAND_TABLE \
ENTRY(RESERVED, reserved, 0x00) \
ENTRY(COMMAND1, command1, 0x01) \
ENTRY(COMMAND2, command2, 0x02) \
...
ENTRY(COMMANDX, commandX, 0x0X) \
I have both the uppercase and lowercase names in the table, because the upper case will be used for enums and the lowercase for function names.
Then I also define structs for each command to define what each command looks like:
typedef struct {...}command1_cmd_t;
typedef struct {...}command2_cmd_t;
etc.
Likewise I define structs for each command response:
typedef struct {...}command1_resp_t;
typedef struct {...}command2_resp_t;
etc.
Then I can define my command code enumeration:
enum
{
#define ENTRY(a,b,c) a##_CMD = c,
COMMAND_TABLE
#undef ENTRY
};
I can define my command length enumeration:
enum
{
#define ENTRY(a,b,c) a##_CMD_LENGTH = sizeof(b##_cmd_t);
COMMAND_TABLE
#undef ENTRY
};
I can define my response length enumeration:
enum
{
#define ENTRY(a,b,c) a##_RESP_LENGTH = sizeof(b##_resp_t);
COMMAND_TABLE
#undef ENTRY
};
I can determine how many commands there are as follows:
typedef struct
{
#define ENTRY(a,b,c) uint8_t b;
COMMAND_TABLE
#undef ENTRY
} offset_struct_t;
#define NUMBER_OF_COMMANDS sizeof(offset_struct_t)
NOTE: I never actually instantiate the offset_struct_t, I just use it as a way for the compiler to generate for me my number of commands definition.
Note then I can generate my table of function pointers as follows:
p_func_t jump_table[NUMBER_OF_COMMANDS] =
{
#define ENTRY(a,b,c) process_##b,
COMMAND_TABLE
#undef ENTRY
}
And my function prototypes:
#define ENTRY(a,b,c) void process_##b(void);
COMMAND_TABLE
#undef ENTRY
Now lastly for the coolest use ever, I can have the compiler calculate how big my transmit buffer should be.
/* reminder the sizeof a union is the size of its largest member */
typedef union
{
#define ENTRY(a,b,c) uint8_t b##_buf[sizeof(b##_cmd_t)];
COMMAND_TABLE
#undef ENTRY
}tx_buf_t
Again this union is like my offset struct, it is not instantiated, instead I can use the sizeof operator to declare my transmit buffer size.
uint8_t tx_buf[sizeof(tx_buf_t)];
Now my transmit buffer tx_buf is the optimal size and as I add commands to this comms handler, my buffer will always be the optimal size. Cool!
One other use is to create offset tables:
Since memory is often a constraint on embedded systems, I don't want to use 512 bytes for my jump table (2 bytes per pointer X 256 possible commands) when it is a sparse array. Instead I will have a table of 8bit offsets for each possible command. This offset is then used to index into my actual jump table which now only needs to be NUM_COMMANDS * sizeof(pointer). In my case with 10 commands defined. My jump table is 20bytes long and I have an offset table that is 256 bytes long, which is a total of 276bytes instead of 512bytes. I then call my functions like so:
jump_table[offset_table[command]]();
instead of
jump_table[command]();
I can create an offset table like so:
/* initialize every offset to 0 */
static uint8_t offset_table[256] = {0};
/* for each valid command, initialize the corresponding offset */
#define ENTRY(a,b,c) offset_table[c] = offsetof(offset_struct_t, b);
COMMAND_TABLE
#undef ENTRY
where offsetof is a standard library macro defined in "stddef.h"
As a side benefit, there is a very easy way to determine if a command code is supported or not:
bool command_is_valid(uint8_t command)
{
/* return false if not valid, or true (non 0) if valid */
return offset_table[command];
}
This is also why in my COMMAND_TABLE I reserved command byte 0. I can create one function called "process_reserved()" which will be called if any invalid command byte is used to index into my offset table.
X-Macros are essentially parameterized templates. So they are the right tool for the job if you need several similar things in several guises. They allow you to create an abstract form and instantiate it according to different rules.
I use X-macros to output enum values as strings. And since encountering it, I strongly prefer this form which takes a "user" macro to apply to each element. Multiple file inclusion is just far more painful to work with.
/* x-macro constructors for error and type
enums and string tables */
#define AS_BARE(a) a ,
#define AS_STR(a) #a ,
#define ERRORS(_) \
_(noerror) \
_(dictfull) _(dictstackoverflow) _(dictstackunderflow) \
_(execstackoverflow) _(execstackunderflow) _(limitcheck) \
_(VMerror)
enum err { ERRORS(AS_BARE) };
char *errorname[] = { ERRORS(AS_STR) };
/* puts(errorname[(enum err)limitcheck]); */
I'm also using them for function dispatch based on object type. Again by hijacking the same macro I used to create the enum values.
#define TYPES(_) \
_(invalid) \
_(null) \
_(mark) \
_(integer) \
_(real) \
_(array) \
_(dict) \
_(save) \
_(name) \
_(string) \
/*enddef TYPES */
#define AS_TYPE(_) _ ## type ,
enum { TYPES(AS_TYPE) };
Using the macro guarantees that all my array indices will match the associated enum values, because they construct their various forms using the bare tokens from the macro definition (the TYPES macro).
typedef void evalfunc(context *ctx);
void evalquit(context *ctx) { ++ctx->quit; }
void evalpop(context *ctx) { (void)pop(ctx->lo, adrent(ctx->lo, OS)); }
void evalpush(context *ctx) {
push(ctx->lo, adrent(ctx->lo, OS),
pop(ctx->lo, adrent(ctx->lo, ES)));
}
evalfunc *evalinvalid = evalquit;
evalfunc *evalmark = evalpop;
evalfunc *evalnull = evalpop;
evalfunc *evalinteger = evalpush;
evalfunc *evalreal = evalpush;
evalfunc *evalsave = evalpush;
evalfunc *evaldict = evalpush;
evalfunc *evalstring = evalpush;
evalfunc *evalname = evalpush;
evalfunc *evaltype[stringtype/*last type in enum*/+1];
#define AS_EVALINIT(_) evaltype[_ ## type] = eval ## _ ;
void initevaltype(void) {
TYPES(AS_EVALINIT)
}
void eval(context *ctx) {
unsigned ades = adrent(ctx->lo, ES);
object t = top(ctx->lo, ades, 0);
if ( isx(t) ) /* if executable */
evaltype[type(t)](ctx); /* <--- the payoff is this line here! */
else
evalpush(ctx);
}
Using X-macros this way actually helps the compiler to give helpful error messages. I omitted the evalarray function from the above because it would distract from my point. But if you attempt to compile the above code (commenting-out the other function calls, and providing a dummy typedef for context, of course), the compiler would complain about a missing function. For each new type I add, I am reminded to add a handler when I recompile this module. So the X-macro helps to guarantee that parallel structures remain intact even as the project grows.
Edit:
This answer has raised my reputation 50%. So here's a little more. The following is a negative example, answering the question: when not to use X-Macros?
This example shows the packing of arbitrary code fragments into the X-"record". I eventually abandoned this branch of the project and did not use this strategy in later designs (and not for want of trying). It became unweildy, somehow. Indeed the macro is named X6 because at one point there were 6 arguments, but I got tired of changing the macro name.
/* Object types */
/* "'X'" macros for Object type definitions, declarations and initializers */
// a b c d
// enum, string, union member, printf d
#define OBJECT_TYPES \
X6( nulltype, "null", int dummy , ("<null>")) \
X6( marktype, "mark", int dummy2 , ("<mark>")) \
X6( integertype, "integer", int i, ("%d",o.i)) \
X6( booleantype, "boolean", bool b, (o.b?"true":"false")) \
X6( realtype, "real", float f, ("%f",o.f)) \
X6( nametype, "name", int n, ("%s%s", \
(o.flags & Fxflag)?"":"/", names[o.n])) \
X6( stringtype, "string", char *s, ("%s",o.s)) \
X6( filetype, "file", FILE *file, ("<file %p>",(void *)o.file)) \
X6( arraytype, "array", Object *a, ("<array %u>",o.length)) \
X6( dicttype, "dict", struct s_pair *d, ("<dict %u>",o.length)) \
X6(operatortype, "operator", void (*o)(), ("<op>")) \
#define X6(a, b, c, d) #a,
char *typestring[] = { OBJECT_TYPES };
#undef X6
// the Object type
//forward reference so s_object can contain s_objects
typedef struct s_object Object;
// the s_object structure:
// a bit convoluted, but it boils down to four members:
// type, flags, length, and payload (union of type-specific data)
// the first named union member is integer, so a simple literal object
// can be created on the fly:
// Object o = {integertype,0,0,4028}; //create an int object, value: 4028
// Object nl = {nulltype,0,0,0};
struct s_object {
#define X6(a, b, c, d) a,
enum e_type { OBJECT_TYPES } type;
#undef X6
unsigned int flags;
#define Fread 1
#define Fwrite 2
#define Fexec 4
#define Fxflag 8
size_t length; //for lint, was: unsigned int
#define X6(a, b, c, d) c;
union { OBJECT_TYPES };
#undef X6
};
One big problem was the printf format strings. While it looks cool, it's just hocus pocus. Since it's only used in one function, overuse of the macro actually separated information that should be together; and it makes the function unreadable by itself. The obfuscation is doubly unfortunate in a debugging function like this one.
//print the object using the type's format specifier from the macro
//used by O_equal (ps: =) and O_equalequal (ps: ==)
void printobject(Object o) {
switch (o.type) {
#define X6(a, b, c, d) \
case a: printf d; break;
OBJECT_TYPES
#undef X6
}
}
So don't get carried away. Like I did.
Some real-world uses of X-Macros by popular and large projects:
Java HotSpot
In the Oracle HotSpot Virtual Machine for the Java® Programming Language, there is the file globals.hpp, which uses the RUNTIME_FLAGS in that way.
See the source code:
JDK 7
JDK 8
JDK 9
Chromium
The list of network errors in net_error_list.h is a long, long list of macro expansions of this form:
NET_ERROR(IO_PENDING, -1)
It is used by net_errors.h from the same directory:
enum Error {
OK = 0,
#define NET_ERROR(label, value) ERR_ ## label = value,
#include "net/base/net_error_list.h"
#undef NET_ERROR
};
The result of this preprocessor magic is:
enum Error {
OK = 0,
ERR_IO_PENDING = -1,
};
What I don't like about this particular use is that the name of the constant is created dynamically by adding the ERR_. In this example, NET_ERROR(IO_PENDING, -100) defines the constant ERR_IO_PENDING.
Using a simple text search for ERR_IO_PENDING, it is not possible to see where this constant it defined. Instead, to find the definition, one has to search for IO_PENDING. This makes the code hard to navigate and therefore adds to the obfuscation of the whole code base.
I like to use X macros for creating 'rich enumerations' which support iterating the enum values as well as getting the string representation for each enum value:
#define MOUSE_BUTTONS \
X(LeftButton, 1) \
X(MiddleButton, 2) \
X(RightButton, 4)
struct MouseButton {
enum Value {
None = 0
#define X(name, value) ,name = value
MOUSE_BUTTONS
#undef X
};
static const int *values() {
static const int a[] = {
None,
#define X(name, value) name,
MOUSE_BUTTONS
#undef X
-1
};
return a;
}
static const char *valueAsString( Value v ) {
#define X(name, value) static const char str_##name[] = #name;
MOUSE_BUTTONS
#undef X
switch ( v ) {
case None: return "None";
#define X(name, value) case name: return str_##name;
MOUSE_BUTTONS
#undef X
}
return 0;
}
};
This not only defines a MouseButton::Value enum, it also lets me do things like
// Print names of all supported mouse buttons
for ( const int *mb = MouseButton::values(); *mb != -1; ++mb ) {
std::cout << MouseButton::valueAsString( (MouseButton::Value)*mb ) << "\n";
}
I use a pretty massive X-macro to load contents of INI-file into a configuration struct, amongst other things revolving around that struct.
This is what my "configuration.def" -file looks like:
#define NMB_DUMMY(...) X(__VA_ARGS__)
#define NMB_INT_DEFS \
TEXT("long int") , long , , , GetLongValue , _ttol , NMB_SECT , SetLongValue ,
#define NMB_STR_DEFS NMB_STR_DEFS__(TEXT("string"))
#define NMB_PATH_DEFS NMB_STR_DEFS__(TEXT("path"))
#define NMB_STR_DEFS__(ATYPE) \
ATYPE , basic_string<TCHAR>* , new basic_string<TCHAR>\
, delete , GetValue , , NMB_SECT , SetValue , *
/* X-macro starts here */
#define NMB_SECT "server"
NMB_DUMMY(ip,TEXT("Slave IP."),TEXT("10.11.180.102"),NMB_STR_DEFS)
NMB_DUMMY(port,TEXT("Slave portti."),TEXT("502"),NMB_STR_DEFS)
NMB_DUMMY(slaveid,TEXT("Slave protocol ID."),0xff,NMB_INT_DEFS)
.
. /* And so on for about 40 items. */
It's a bit confusing, I admit. It quickly become clear that I don't actually want to write all those type declarations after every field-macro. (Don't worry, there's a big comment to explain everything which I omitted for brevity.)
And this is how I declare the configuration struct:
typedef struct {
#define X(ID,DESC,DEFVAL,ATYPE,TYPE,...) TYPE ID;
#include "configuration.def"
#undef X
basic_string<TCHAR>* ini_path; //Where all the other stuff gets read.
long verbosity; //Used only by console writing functions.
} Config;
Then, in the code, firstly the default values are read into the configuration struct:
#define X(ID,DESC,DEFVAL,ATYPE,TYPE,CONSTRUCTOR,DESTRUCTOR,GETTER,STRCONV,SECT,SETTER,...) \
conf->ID = CONSTRUCTOR(DEFVAL);
#include "configuration.def"
#undef X
Then, the INI is read into the configuration struct as follows, using library SimpleIni:
#define X(ID,DESC,DEFVAL,ATYPE,TYPE,CONSTRUCTOR,DESTRUCTOR,GETTER,STRCONV,SECT,SETTER,DEREF...)\
DESTRUCTOR (conf->ID);\
conf->ID = CONSTRUCTOR( ini.GETTER(TEXT(SECT),TEXT(#ID),DEFVAL,FALSE) );\
LOG3A(<< left << setw(13) << TEXT(#ID) << TEXT(": ") << left << setw(30)\
<< DEREF conf->ID << TEXT(" (") << DEFVAL << TEXT(").") );
#include "configuration.def"
#undef X
And overrides from commandline flags, that also are formatted with the same names (in GNU long form), are applies as follows in the foillowing manner using library SimpleOpt:
enum optflags {
#define X(ID,...) ID,
#include "configuration.def"
#undef X
};
CSimpleOpt::SOption sopt[] = {
#define X(ID,DESC,DEFVAL,ATYPE,TYPE,...) {ID,TEXT("--") #ID TEXT("="), SO_REQ_CMB},
#include "configuration.def"
#undef X
SO_END_OF_OPTIONS
};
CSimpleOpt ops(argc,argv,sopt,SO_O_NOERR);
while(ops.Next()){
switch(ops.OptionId()){
#define X(ID,DESC,DEFVAL,ATYPE,TYPE,CONSTRUCTOR,DESTRUCTOR,GETTER,STRCONV,SECT,...) \
case ID:\
DESTRUCTOR (conf->ID);\
conf->ID = STRCONV( CONSTRUCTOR ( ops.OptionArg() ) );\
LOG3A(<< TEXT("Omitted ")<<left<<setw(13)<<TEXT(#ID)<<TEXT(" : ")<<conf->ID<<TEXT(" ."));\
break;
#include "configuration.def"
#undef X
}
}
And so on, I also use the same macro to print the --help -flag output and sample default ini file, configuration.def is included 8 times in my program. "Square peg into a round hole", maybe; how would an actually competent programmer proceed with this? Lots and lots of loops and string processing?
https://github.com/whunmr/DataEx
I am using the following xmacros to generate a C++ class, with serialize and deserialize functionality built in.
#define __FIELDS_OF_DataWithNested(_) \
_(1, a, int ) \
_(2, x, DataX) \
_(3, b, int ) \
_(4, c, char ) \
_(5, d, __array(char, 3)) \
_(6, e, string) \
_(7, f, bool)
DEF_DATA(DataWithNested);
Usage:
TEST_F(t, DataWithNested_should_able_to_encode_struct_with_nested_struct) {
DataWithNested xn;
xn.a = 0xCAFEBABE;
xn.x.a = 0x12345678;
xn.x.b = 0x11223344;
xn.b = 0xDEADBEEF;
xn.c = 0x45;
memcpy(&xn.d, "XYZ", strlen("XYZ"));
char buf_with_zero[] = {0x11, 0x22, 0x00, 0x00, 0x33};
xn.e = string(buf_with_zero, sizeof(buf_with_zero));
xn.f = true;
__encode(DataWithNested, xn, buf_);
char expected[] = { 0x01, 0x04, 0x00, 0xBE, 0xBA, 0xFE, 0xCA,
0x02, 0x0E, 0x00 /*T and L of nested X*/,
0x01, 0x04, 0x00, 0x78, 0x56, 0x34, 0x12,
0x02, 0x04, 0x00, 0x44, 0x33, 0x22, 0x11,
0x03, 0x04, 0x00, 0xEF, 0xBE, 0xAD, 0xDE,
0x04, 0x01, 0x00, 0x45,
0x05, 0x03, 0x00, 'X', 'Y', 'Z',
0x06, 0x05, 0x00, 0x11, 0x22, 0x00, 0x00, 0x33,
0x07, 0x01, 0x00, 0x01};
EXPECT_TRUE(ArraysMatch(expected, buf_));
}
Also, another example is in https://github.com/whunmr/msgrpc.
Chromium has an interesting variation of a X-macro at dom_code_data.inc. Except it's not just a macro, but an entirely separate file.
This file is intended for keyboard input mapping between different platforms' scancodes, USB HID codes, and string-like names.
The file contains code like:
DOM_CODE_DECLARATION {
// USB evdev XKB Win Mac Code
DOM_CODE(0x000000, 0x0000, 0x0000, 0x0000, 0xffff, NULL, NONE), // Invalid
...
};
Each macro invocation actually passes in 7 arguments, and the macro can choose which arguments to use and which to ignore. One usage is to map between OS keycodes and platform-independent scancodes and DOM strings. Different macros are used on different OSes to pick the keycodes appropriate for that OS.
// Table of USB codes (equivalent to DomCode values), native scan codes,
// and DOM Level 3 |code| strings.
#if defined(OS_WIN)
#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
{ usb, win, code }
#elif defined(OS_LINUX)
#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
{ usb, xkb, code }
#elif defined(OS_MACOSX)
#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
{ usb, mac, code }
#elif defined(OS_ANDROID)
#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
{ usb, evdev, code }
#else
#define DOM_CODE(usb, evdev, xkb, win, mac, code, id) \
{ usb, 0, code }
#endif
#define DOM_CODE_DECLARATION const KeycodeMapEntry usb_keycode_map[] =
#include "ui/events/keycodes/dom/dom_code_data.inc"
#undef DOM_CODE
#undef DOM_CODE_DECLARATION
My humble example:
One of the steps to speed up the FFmpeg HEVC decoder - hardcode a matrix consisting of only three rows of small integer coefficients, which is used in several places:
https://github.com/aliakseis/FFmpegPlayer/commit/53a28b61cd98e1dda6d04251b713d39122c021d2#diff-8c65aa37510be2621e7b5a550a33c445b4c85607a789c9b483c2e78cdffcd65bL607
Related
I am looking for a way to use a single macro call (multiple times):
#define MAGIC ???????
MAGIC(ref1, "text 1");
MAGIC(ref2, "text 2");
to expand into something like this:
const char *texts[] = {"text 1", "text 2"};
enum {ref1, ref2}
I got two ways of create first line:
#define _COUNT(z, a, b, c, d, e, f, g, h, i, j, count, ...) count
#define COUNT(...) _COUNT(0, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
#define EVENTS(...) \
const struct \
{ \
int n = COUNT(__VA_ARGS__); \
const char *texts[COUNT(__VA_ARGS__)] = {__VA_ARGS__}; \
}
EVENTS("text 1", "text 2", "text N");
and:
#define BEGIN(n) \
const struct \
{ \
int cnt = (n); \
const char *texts[(n) + 1] = {
#define EVENT(id, text) text,
#define END \
0 \
} \
; \
} \
;
BEGIN(2)
EVENT(ref1, "text 1")
EVENT(ref2, "text 2")
END
both will work to create the fist line. However the first not even include the reference name and the second one ignores it.
I am sure I can create de second line as well, but how to create both at the same time?
I have hundreds of pairs like this to declare in a more complex code, and hope this will improve reading.
Any way to do it using GCC?
how to create both at the same time?
It is not possible - macros have no "state" that they can "set" from inside a macro expansion. You can't remember that you have seen some macro invocation and act on it later.
What you are (re-)implementing are X macros, where you use the same file with a different macro twice, solving the problem with remembering state. "State" depends on the location in the file, where you can change the macro definition.
// file_with_magic.h
MAGIC(ref1, "text 1")
MAGIC(ref2, "text 2")
// header.h
const char *texts[] = {
#define MAGIC(a, b) b
#include <file_with_magic.h>
#undef MAGIC
};
enum {
#define MAGIC(a, b) a
#include <file_with_magic.h>
#undef MAGIC
};
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?
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.
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.
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 ? :)
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;
}
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] ;
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
}
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"
};
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/
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.
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.
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!
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++.
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";
}
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)
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;
}
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
.................
}
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.
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); }
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 )
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.
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.
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
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.
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;
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";
}
#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 ) );
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;
}
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.
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.
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.
is it possible to reset the __COUNTER__ macro at the start of a header file to make its evaluation within the header file consistent over several compile units?
How about an enum?
enum { COUNTER_BASE = __COUNTER__ };
#define LOCAL_COUNTER (__COUNTER__ - COUNTER_BASE)
You can set BASE to __COUNTER__ at the top of your header file, and then use __COUNTER__ - BASE later on.
However, do this after you've included all necessary headers, because else thee result would depend on the use of __COUNTER__ within the header guards of those nested include files.
No, there is no way to reset that value.
Take a look at the GCC source that increments the counter:
case BT_COUNTER:
if (CPP_OPTION (pfile, directives_only) && pfile->state.in_directive)
cpp_error (pfile, CPP_DL_ERROR,
"__COUNTER__ expanded inside directive with -fdirectives-only");
number = pfile->counter++;
break;
And if you look arount this library, nowhere is the counter modified again. It is default initialized to 0 and then incremented at every use.
Note that the pfile, where the counter variable resides, represents the the preprocessor input, that in this case is the current compilation unit, not the actual file.
thanks to #Michael, and by using enum you can have complete usage of __COUNTER__ as below:
"source: ESF compiler extension"
//! >>>>>>>>>>>>>>>> Counter >>>>>>>>>>>>>>>>
#pragma region Counter
/* Counter
* Ex:
* <code>
CountOn(InitSteps);
int foo(void)
{
CountIt();
...
CountIt();
...
CountIt();
...
}
Counted(InitSteps);
* </code>
* */
#define CountOn(name) enum { name ## _BASE = __COUNTER__ }; const U8 name
#define CountIt(name) (U8)(__COUNTER__ - (name ## _BASE))
#define Counted(name) const U8 name = (U8)(__COUNTER__ - (name ## _BASE) -1);
/* alias of Counter
* Ex:
* <code>
TakeSteps(InitSteps);
int foo(void)
{
AddStep();
...
AddStep();
...
AddStep();
...
}
TotalSteps(InitSteps);
* </code>
* */
#define TakeSteps(name) CountOn(name)
/* this is invalid to read, want to get value ? use 'CountIt(name)' OR 'NextStep(name)' */
#define AddStep() do { __COUNTER__; } while (0);
#define TotalSteps(name) Counted(name)
/* better use of Counter (on format print 'step, total')
* 'step' starts at 1
* Ex:
* <code>
TakeSteps(InitSteps);
int foo(void)
{
printf(StepsPRT " Initializing system clock...", NextStep(InitSteps));
...
printf(StepsPRT " Loading configurations...", NextStep(InitSteps));
...
printf(StepsPRT " Applying...", NextStep(InitSteps));
...
}
TotalSteps(InitSteps);
* </code>
* */
#define NextStep(name) CountIt(name), name
#define StepsPRT "%d of %d"
#define NextStepP StepsPRT
#pragma endregion Counter
//! <<<<<<<<<<<<<<<< .Counter <<<<<<<<<<<<<<<<
Before you ask, I've looked and looked for this on SO, and cannot find a solid answer.
I need to be able to dynamically iterate over an enum that has non-incremental values, as an example:
typedef enum {
CAPI_SUBTYPE_NULL = 0, /* Null subtype. */
CAPI_SUBTYPE_DIAG_DFD = 1, /* Data Flow diag. */
CAPI_SUBTYPE_DIAG_ERD = 2, /* Entity-Relationship diag. */
CAPI_SUBTYPE_DIAG_STD = 3, /* State Transition diag. */
CAPI_SUBTYPE_DIAG_STC = 4, /* Structure Chart diag. */
CAPI_SUBTYPE_DIAG_DSD = 5, /* Data Structure diag. */
CAPI_SUBTYPE_SPEC_PROCESS = 6, /* Process spec. */
CAPI_SUBTYPE_SPEC_MODULE = 7, /* Module spec. */
CAPI_SUBTYPE_SPEC_TERMINATOR = 8, /* Terminator spec. */
CAPI_SUBTYPE_DD_ALL = 13, /* DD Entries (All). */
CAPI_SUBTYPE_DD_COUPLE = 14, /* DD Entries (Couples). */
CAPI_SUBTYPE_DD_DATA_AREA = 15, /* DD Entries (Data Areas). */
CAPI_SUBTYPE_DD_DATA_OBJECT = 16, /* DD Entries (Data Objects). */
CAPI_SUBTYPE_DD_FLOW = 17, /* DD Entries (Flows). */
CAPI_SUBTYPE_DD_RELATIONSHIP = 18, /* DD Entries (Relationships). */
CAPI_SUBTYPE_DD_STORE = 19, /* DD Entries (Stores). */
CAPI_SUBTYPE_DIAG_PAD = 35, /* Physical architecture diagram. */
CAPI_SUBTYPE_DIAG_BD = 36, /* Behaviour diagram. */
CAPI_SUBTYPE_DIAG_UCD = 37, /* UML Use case diagram. */
CAPI_SUBTYPE_DIAG_PD = 38, /* UML Package diagram. */
CAPI_SUBTYPE_DIAG_COD = 39, /* UML Collaboration diagram. */
CAPI_SUBTYPE_DIAG_SQD = 40, /* UML Sequence diagram. */
CAPI_SUBTYPE_DIAG_CD = 41, /* UML Class diagram. */
CAPI_SUBTYPE_DIAG_SCD = 42, /* UML State chart. */
CAPI_SUBTYPE_DIAG_ACD = 43, /* UML Activity chart. */
CAPI_SUBTYPE_DIAG_CPD = 44, /* UML Component diagram. */
CAPI_SUBTYPE_DIAG_DPD = 45, /* UML Deployment diagram. */
CAPI_SUBTYPE_DIAG_PFD = 47, /* Process flow diagram. */
CAPI_SUBTYPE_DIAG_HIER = 48, /* Hierarchy diagram. */
CAPI_SUBTYPE_DIAG_IDEF0 = 49, /* IDEF0 diagram. */
CAPI_SUBTYPE_DIAG_AID = 50, /* AID diagram. */
CAPI_SUBTYPE_DIAG_SAD = 51, /* SAD diagram. */
CAPI_SUBTYPE_DIAG_ASG = 59 /* ASG diagram. */
} CAPI_SUBTYPE_E ;
The reason I'd like to be able to do this is because the enum is given in an API (which I cannot change, obviously) and would prefer to be able to, regardless of the API version, be able to iterate over these values.
Any direction is appreciated.
With C++, the only way to iterate through enums is store them in an array and iterate through the same. The main challenge is how to track the same order in the enum declaration and the array declaration?
You can automate the way you order them in the enum as well as array. I feel that this is a decent way:
// CAPI_SUBTYPE_E_list.h
// This header file contains all the enum in the order
// Whatever order is set will be followed everywhere
NAME_VALUE(CAPI_SUBTYPE_NULL, 0), /* Null subtype. */
NAME_VALUE(CAPI_SUBTYPE_DIAG_DFD, 1), /* Data Flow diag. */
NAME_VALUE(CAPI_SUBTYPE_DIAG_ERD, 2), /* Entity-Relationship diag. */
...
NAME_VALUE(CAPI_SUBTYPE_DD_ALL, 13), /* DD Entries (All). */
NAME_VALUE(CAPI_SUBTYPE_DD_COUPLE, 14), /* DD Entries (Couples). */
...
NAME_VALUE(CAPI_SUBTYPE_DIAG_ASG, 59) /* ASG diagram. */
Now you #include this file in your enum declaration and array declaration both places with macro redefinition:
// Enum.h
typedef enum {
#define NAME_VALUE(NAME,VALUE) NAME = VALUE
#include"CAPI_SUBTYPE_E_list.h"
#undef NAME_VALUE
}CAPI_SUBTYPE_E;
And put the same file for array with other macro definition:
// array file
// Either this array can be declared `static` or inside unnamed `namespace` to make
// ... it visible through a header file; Or it should be declared `extern` and keep ...
// ... the record of its size; declare a getter method for both array and the size
unsigned int CAPI_SUBTYPE_E_Array [] = {
#define NAME_VALUE(NAME,VALUE) NAME
#include"CAPI_SUBTYPE_E_list.h"
#undef NAME_VALUE
};
Now iterate in C++03 as:
for(unsigned int i = 0, size = sizeof(CAPI_SUBTYPE_E_Array)/sizeof(CAPI_SUBTYPE_E_Array[0]);
i < size; ++i)
or yet simple in C++11:
for(auto i : CAPI_SUBTYPE_E_Array)
It is about tricky and more C than C++ practice, but you can use X macros. It is very ugly and you need to keep TABLE in right order. In C++ I believe we don't need to iterate over enumerations and more we don't need to assign values to enumeration (ostensibly enumeration value is random in every compilation). So think of it as a joke :)
#include <iostream>
#define CAPI_SUBTYPE_TABLE \
CAPI_SUBTYPE_X(CAPI_SUBTYPE_NULL, 0 ) \
CAPI_SUBTYPE_X(CAPI_SUBTYPE_DIAG_DFD, 1 ) \
CAPI_SUBTYPE_X(CAPI_SUBTYPE_DD_ALL, 13)
#define CAPI_SUBTYPE_X(name, value) name = value,
enum CAPI_SUBTYPE
{
CAPI_SUBTYPE_TABLE
CAPI_SUBTYPE_END
};
#undef CAPI_SUBTYPE_X
#define CAPI_SUBTYPE_X(name, value) name,
CAPI_SUBTYPE subtype_iteratable[] =
{
CAPI_SUBTYPE_TABLE
CAPI_SUBTYPE_END
};
#undef CAPI_SUBTYPE_X
#define CAPI_SUBTYPE_SIZE (sizeof(subtype_iteratable) / sizeof(subtype_iteratable[0]) - 1)
int main()
{
for (unsigned i = 0; i < CAPI_SUBTYPE_SIZE; ++i)
std::cout << subtype_iteratable[i] << std::endl; // 0, 1, 13
}
I agree with the already given statements that this isn't possible without either altering or copying the definitions of the enum. However, in C++11 (maybe even C++03?) you can go as far as providing a syntax where all you have to do (literally) is to copy and paste the enumerator definitions from the enum into a macro. This works as long as every enumerator has an explicit definition (using =).
Edit: You can expand this to work even if not every enumerator has an explicit definition, but this shouldn't be required in this case.
I've once developed this for some physicists, so the example is about particles.
Usage example:
// required for this example
#include <iostream>
enum ParticleEnum
{
PROTON = 11,
ELECTRON = 42,
MUON = 43
};
// define macro (see below)
MAKE_ENUM(
ParticleEnum, // name of enum type
particle_enum_detail, // some namespace to place some types in
all_particles, // name of array to list all enumerators
// paste the enumerator definitions of your enum here
PROTON = 11,
ELECTRON = 42,
MUON = 43
) // don't forget the macro's closing paranthesis
int main()
{
for(ParticleEnum p : all_particles)
{
std::cout << p << ", ";
}
}
The macro yields to (effectively):
namespace particle_enum_detail
{
// definition of a type and some constants
constexpr ParticleEnum all_particles[] = {
PROTON,
ELECTRON,
MUON
};
}
using particle_enum_detail::all_particles;
macro definition
#define MAKE_ENUM(ENUM_TYPE, NAMESPACE, ARRAY_NAME, ...) \
namespace NAMESPACE \
{ \
struct iterable_enum_ \
{ \
using storage_type = ENUM_TYPE; \
template < typename T > \
constexpr iterable_enum_(T p) \
: m{ static_cast<storage_type>(p) } \
{} \
constexpr operator storage_type() \
{ return m; } \
template < typename T > \
constexpr iterable_enum_ operator= (T p) \
{ return { static_cast<storage_type>(p) }; } \
private: \
storage_type m; \
}; \
\
/* the "enumeration" */ \
constexpr iterable_enum_ __VA_ARGS__; \
/* the array to store all "enumerators" */ \
constexpr ENUM_TYPE ARRAY_NAME[] = { __VA_ARGS__ }; \
} \
using NAMESPACE::ARRAY_NAME; // macro end
Note: the type iterable_enum_ could as well be defined once outside the macro.
macro explanation
The idea is to allow a syntax like proton = 11, electron = 12 within the macro invocation. This works very easy for any kind of declaration, yet it makes problems for storing the names:
#define MAKE_ENUM(ASSIGNMEN1, ASSIGNMENT2) \
enum my_enum { ASSIGNMENT1, ASSIGNMENT2 }; \
my_enum all[] = { ASSIGNMENT1, ASSIGNMENT2 };
MAKE_ENUM(proton = 11, electron = 22);
yields to:
enum my_enum { proton = 11, electron = 22 }; // would be OK
my_enum all[] = { proton = 11, electron = 22 }; // cannot assign to enumerator
As with many syntactical tricks, operator overloading provides a way to overcome this problem; but the assignment operator has to be a member functions - and enums are not classes.
So why not use some constant objects instead of an enum?
enum my_enum { proton = 11, electron = 22 };
// alternatively
constexpr int proton = 11, electron = 12;
// the `constexpr` here is equivalent to a `const`
This does not yet solve our problem, it just demonstrates we can easily replace enums by a list of constants if we don't need the auto-increment feature of enumerators.
Now, the syntactical trick with operator overloading:
struct iterable_enum_
{
// the trick: a constexpr assignment operator
constexpr iterable_enum_ operator= (int p) // (op)
{ return {p}; }
// we need a ctor for the syntax `object = init`
constexpr iterable_enum_(int p) // (ctor)
: m{ static_cast<ParticleEnum>(p) }
{}
private:
ParticleEnum m;
};
constexpr iterable_enum_ proton = 11, electron = 22; // (1)
iterable_enum_ all_particles[] = { proton = 11, electron = 22 }; // (2)
The trick is, in line (1) the = designates a copy-initialisation, which is done by converting the number (11, 22) to a temporary of type particle by using the (ctor) and copying/moving the temporary via an implicitly-defined ctor to the destination object (proton, electron).
In contrast, the = in line (2) is resolved to an operator call to (op), which effectively returns a copy of the object on which it has been called (*this). The constexpr stuff allows to use these variables at compile time, e.g. in a template declaration. Due to restrictions on constexpr functions, we cannot simply return *this in the (op) function. Additionally, constexpr implies all restrictions of const.
By providing an implicit conversion operator, you can create the array in line (2) of type ParticleEnum:
// in struct particle
constexpr operator ParticleEnum() { return m; }
// in namespace particle_enum_detail
ParticleEnum all_particles[] = { proton = 11, electron = 22 };
Based on the articles given at the begin of the question, I derived a solution that is based in the assumption that you know the invalids ranges.
I really wanna knows if this is a good solution.
First, end you enum with something like that: CAPI_END = 60. It will helps to interates. So my code is:
typedef enum {
CAPI_SUBTYPE_NULL = 0, /* Null subtype. */
CAPI_SUBTYPE_DIAG_DFD = 1, /* Data Flow diag. */
CAPI_SUBTYPE_DIAG_ERD = 2, /* Entity-Relationship diag. */
CAPI_SUBTYPE_DIAG_STD = 3, /* State Transition diag. */
CAPI_SUBTYPE_DIAG_STC = 4, /* Structure Chart diag. */
CAPI_SUBTYPE_DIAG_DSD = 5, /* Data Structure diag. */
CAPI_SUBTYPE_SPEC_PROCESS = 6, /* Process spec. */
CAPI_SUBTYPE_SPEC_MODULE = 7, /* Module spec. */
CAPI_SUBTYPE_SPEC_TERMINATOR = 8, /* Terminator spec. */
CAPI_SUBTYPE_DD_ALL = 13, /* DD Entries (All). */
CAPI_SUBTYPE_DD_COUPLE = 14, /* DD Entries (Couples). */
CAPI_SUBTYPE_DD_DATA_AREA = 15, /* DD Entries (Data Areas). */
CAPI_SUBTYPE_DD_DATA_OBJECT = 16, /* DD Entries (Data Objects). */
CAPI_SUBTYPE_DD_FLOW = 17, /* DD Entries (Flows). */
CAPI_SUBTYPE_DD_RELATIONSHIP = 18, /* DD Entries (Relationships). */
CAPI_SUBTYPE_DD_STORE = 19, /* DD Entries (Stores). */
CAPI_SUBTYPE_DIAG_PAD = 35, /* Physical architecture diagram. */
CAPI_SUBTYPE_DIAG_BD = 36, /* Behaviour diagram. */
CAPI_SUBTYPE_DIAG_UCD = 37, /* UML Use case diagram. */
CAPI_SUBTYPE_DIAG_PD = 38, /* UML Package diagram. */
CAPI_SUBTYPE_DIAG_COD = 39, /* UML Collaboration diagram. */
CAPI_SUBTYPE_DIAG_SQD = 40, /* UML Sequence diagram. */
CAPI_SUBTYPE_DIAG_CD = 41, /* UML Class diagram. */
CAPI_SUBTYPE_DIAG_SCD = 42, /* UML State chart. */
CAPI_SUBTYPE_DIAG_ACD = 43, /* UML Activity chart. */
CAPI_SUBTYPE_DIAG_CPD = 44, /* UML Component diagram. */
CAPI_SUBTYPE_DIAG_DPD = 45, /* UML Deployment diagram. */
CAPI_SUBTYPE_DIAG_PFD = 47, /* Process flow diagram. */
CAPI_SUBTYPE_DIAG_HIER = 48, /* Hierarchy diagram. */
CAPI_SUBTYPE_DIAG_IDEF0 = 49, /* IDEF0 diagram. */
CAPI_SUBTYPE_DIAG_AID = 50, /* AID diagram. */
CAPI_SUBTYPE_DIAG_SAD = 51, /* SAD diagram. */
CAPI_SUBTYPE_DIAG_ASG = 59, /* ASG diagram. */
CAPI_END = 60 /* just to mark the end of your enum */
} CAPI_SUBTYPE_E ;
CAPI_SUBTYPE_E& operator++(CAPI_SUBTYPE_E& capi)
{
const int ranges = 2; // you have 2 invalid ranges in your example
int invalid[ranges][2] = {{8, 12}, {19, 34}}; // {min, max} (inclusive, exclusive)
CAPI_SUBTYPE_E next = CAPI_SUBTYPE_NULL;
for (int i = 0; i < ranges; i++)
if ( capi >= invalid[i][0] && capi < invalid[i][1] ) {
next = static_cast<CAPI_SUBTYPE_E>(invalid[i][1] + 1);
break;
} else {
next = static_cast<CAPI_SUBTYPE_E>(capi + 1);
}
// if ( next > CAPI_END )
// throw an exception
return capi = next;
}
int main()
{
for(CAPI_SUBTYPE_E i = CAPI_SUBTYPE_NULL; i < CAPI_END; ++i)
cout << i << endl;
cout << endl;
}
I'm providing only a pre increment operator. A post increment operator is let to be implemanted later.
The answer is "no, you cannot iterate over the elements of an enum in C++03 or C++11".
Now, you can describe the set of values of an enum in a way that can be understood at compile time.
template<typename E, E... Es>
struct TypedEnumList {};
typedef TypedEnumList<
CAPI_SUBTYPE_E,
CAPI_SUBTYPE_NULL, // etc
// ...
CAPI_SUBTYPE_DIAG_ASG
> CAPI_SUBTYPE_E_LIST;
which gives you a type CAPI_SUBTYPE_E_LIST which encapsulates the list of enum values.
We can then populate an array with these easily:
template<typename T, T... Es>
std::array<T, sizeof...(Es)> GetRuntimeArray( TypedEnumList<T, Es... > ) {
return { Es... };
}
auto Capis = GetRuntimeArray( CAPI_SUBTYPE_E_LIST() );
if you really need it. But this is just a special case of the more general case of being able to generate code for each element of your enum CAPI_SUBTYPE_E -- directly building a for loop isn't needed.
Amusingly, with a compliant C++11 compiler, we could write code that would generate our CAPI_SUBTYPE_E_LIST with particular enum elements iff those elements are actually in CAPI_SUBTYPE_E using SFINAE. This would be useful because we can take the most recent version of the API we can support, and have it auto-degrade (at compile time) if the API we compile against is more primitive.
To demonstrate the technique, I'll start with a toy enum
enum Foo { A = 0, /* B = 1 */ };
Imagine that B=1 is uncommented in the most modern version of the API, but is not there in the more primitive.
template<int index, typename EnumList, typename=void>
struct AddElementN: AddElementN<index-1, EnumList> {};
template<typename EnumList>
struct AddElementN<-1, EnumList, void> {
typedef EnumList type;
};
template<typename Enum, Enum... Es>
struct AddElementN<0, TypedEnumList<Enum, Es...>, typename std::enable_if< Enum::A == Enum::A >::type >:
AddElement<-1, TypedEnumList<Enum, A, Es...>>
{};
template<typename Enum, Enum... Es>
struct AddElementN<1, TypedEnumList<Enum, Es...>, typename std::enable_if< Enum::B == Enum::B >::type >:
AddElement<0, TypedEnumList<Enum, B, Es...>>
{};
// specialize this for your enum to call AddElementN:
template<typename Enum>
struct BuildTypedList;
template<>
struct BuildTypedList<CAPI_SUBTYPE_E>:
AddElementN<1, TypedEnumList<CAPI_SUBTYPE_E>>
{};
template<typename Enum>
using TypedList = typename BuildTypedList<Enum>::type;
now, if I wrote that right, TypedList<CAPI_SUBTYPE_E> contains B iff B is defined as an element of CAPI_SUBTYPE_E. This lets you compile against more than one version of the library, and get a different set of elements in your enum element list depending on what is in the library. You do have to maintain that annoying boilerplate (which could probably be made easier with macros or code generation) against the "final" version of the enums elements, but it should automatically handle previous versions at compile time.
This sadly requires lots of maintenance to work.
Finally, your requirement that this be dynamic: the only practical way for this to be dynamic is to wrap the 3rd party API in code that knows what the version of the API is, and exposes a different buffer of enum values (I'd put it in a std::vector) depending on what the version the API is. Then when you load the API, you also load this helper wrapper, which then uses the above techniques to build the set of elements of the enum, which you iterate over.
Some of this boilerplate can be made easier to write with some horrible macros, like ones that build the various AddElementN type SFINAE code by using the __LINE__ to index the recursive types. But that would be horrible.
Somewhat clearer (???) with a bit of boost preprocessing.
You define your enums by a sequence
#define CAPI_SUBTYPE_E_Sequence \
(CAPI_SUBTYPE_NULL)(0) \
(CAPI_SUBTYPE_DIAG_DFD)(1) ...
then you can automate (through macros) the declaration of the enum,
DECL_ENUM(CAPI_SUBTYPE_E) ;
the table that indexes it
DECL_ENUM_TABLE(CAPI_SUBTYPE_E);
the number of enums / size of the table
ENUM_SIZE(CAPI_SUBTYPE_E)
and access to it:
ITER_ENUM_i(i,CAPI_SUBTYPE_E)
Here is the full text.
#include <boost/preprocessor.hpp>
// define your enum as (name)(value) sequence
#define CAPI_SUBTYPE_E_Sequence \
(CAPI_SUBTYPE_NULL)(0) /* Null subtype. */ \
(CAPI_SUBTYPE_DIAG_DFD)(1) /* Data Flow diag. */ \
(CAPI_SUBTYPE_DIAG_ERD)(2) /* Entity-Relationship diag. */ \
(CAPI_SUBTYPE_DIAG_DSD)(5) /* Data Structure diag. */ \
(CAPI_SUBTYPE_DD_ALL)(13) /* DD Entries (All). */
// # enums
#define ENUM_SIZE(name) \
BOOST_PP_DIV(BOOST_PP_SEQ_SIZE(BOOST_PP_CAT(name,_Sequence)),2)
#define ENUM_NAME_N(N,seq) BOOST_PP_SEQ_ELEM(BOOST_PP_MUL(N,2),seq)
#define ENUM_VALUE_N(N,seq) BOOST_PP_SEQ_ELEM(BOOST_PP_INC(BOOST_PP_MUL(N,2)),seq)
// declare Nth enum
#define DECL_ENUM_N(Z,N,seq) \
BOOST_PP_COMMA_IF(N) ENUM_NAME_N(N,seq) = ENUM_VALUE_N(N,seq)
// declare whole enum
#define DECL_ENUM(name) \
typedef enum { \
BOOST_PP_REPEAT( ENUM_SIZE(name) , DECL_ENUM_N , BOOST_PP_CAT(name,_Sequence) ) \
} name
DECL_ENUM(CAPI_SUBTYPE_E) ;
// declare Nth enum value
#define DECL_ENUM_TABLE_N(Z,N,seq) \
BOOST_PP_COMMA_IF(N) ENUM_NAME_N(N,seq)
// declare table
#define DECL_ENUM_TABLE(name) \
static const name BOOST_PP_CAT(name,_Table) [ENUM_SIZE(name)] = { \
BOOST_PP_REPEAT( ENUM_SIZE(name) , DECL_ENUM_TABLE_N , BOOST_PP_CAT(name,_Sequence) ) \
}
DECL_ENUM_TABLE(CAPI_SUBTYPE_E);
#define ITER_ENUM_i(i,name) BOOST_PP_CAT(name,_Table) [i]
// demo
// outputs : [0:0] [1:1] [2:2] [3:5] [4:13]
#include <iostream>
int main() {
for (int i=0; i<ENUM_SIZE(CAPI_SUBTYPE_E) ; i++)
std::cout << "[" << i << ":" << ITER_ENUM_i(i,CAPI_SUBTYPE_E) << "] ";
return 0;
}
// bonus : check enums are unique and in-order
#include <boost/preprocessor/stringize.hpp>
#include <boost/static_assert.hpp>
#define CHECK_ENUM_N(Z,N,seq) \
BOOST_PP_IF( N , \
BOOST_STATIC_ASSERT_MSG( \
ENUM_VALUE_N(BOOST_PP_DEC(N),seq) < ENUM_VALUE_N(N,seq) , \
BOOST_PP_STRINGIZE( ENUM_NAME_N(BOOST_PP_DEC(N),seq) ) " not < " BOOST_PP_STRINGIZE( ENUM_NAME_N(N,seq) ) ) \
, ) ;
#define CHECK_ENUM(name) \
namespace { void BOOST_PP_CAT(check_enum_,name) () { \
BOOST_PP_REPEAT( ENUM_SIZE(name) , CHECK_ENUM_N , BOOST_PP_CAT(name,_Sequence) ) } }
// enum OK
CHECK_ENUM(CAPI_SUBTYPE_E)
#define Bad_Enum_Sequence \
(one)(1)\
(five)(5)\
(seven)(7)\
(three)(3)
// enum not OK : enum_iter.cpp(81): error C2338: seven not < three
CHECK_ENUM(Bad_Enum)
You cannot iterate over arbitrary enum in C++. For iterating, values should be put in some container. You can automate maintaining such a container using 'enum classes' as described here: http://www.drdobbs.com/when-enum-just-isnt-enough-enumeration-c/184403955http://www.drdobbs.com/when-enum-just-isnt-enough-enumeration-c/184403955
Use Higher Order macros
Here's the technique we've been using in our projects.
Concept:
The idea is to generate a macro called LISTING which contains the definition of name-value pairs and it takes another macro as an argument. In the example below I defined two such helper macros. 'GENERATE_ENUM' to generate the enum and 'GENERATE_ARRAY' to generate an iteratable array. Of course this can be extended as necessary. I think this solution gives you the most bang for the buck.
Conceptually it's very similar to iammilind's solution.
Example:
// helper macros
#define GENERATE_ENUM(key,value) \
key = value \
#define GENERATE_ARRAY(name,value) \
name \
// Since this is C++, I took the liberty to wrap everthing in a namespace.
// This done mostly for aesthetic reasons, you don't have to if you don't want.
namespace CAPI_SUBTYPES
{
// I define a macro containing the key value pairs
#define LISTING(m) \
m(NONE, 0), /* Note: I can't use NULL here because it conflicts */
m(DIAG_DFD, 1), \
m(DIAG_ERD, 2), \
...
m(DD_ALL, 13), \
m(DD_COUPLE, 14), \
...
m(DIAG_SAD, 51), \
m(DIAG_ASG, 59), \
typedef enum {
LISTING(GENERATE_ENUM)
} Enum;
const Enum At[] = {
LISTING(GENERATE_ARRAY)
};
const unsigned int Count = sizeof(At)/sizeof(At[0]);
}
Usage:
Now in code you can refer to the enum like this:
CAPI_SUBTYPES::Enum eVariable = CAPI_SUBTYPES::DIAG_STD;
You can iterate over the enumeration like this:
for (unsigned int i=0; i<CAPI_SUBTYPES::Count; i++) {
...
CAPI_SUBTYPES::Enum eVariable = CAPI_SUBTYPES::At[i];
...
}
Note:
If memory serves me right, C++11 enums live in their own namespaces (like in Java or C#) , therefore the above usage wouldn't work. You'd have to refer to the enum values like this CAPI_SUBTYPES::Enum::FooBar.
Put them into an array or other container and iterate over that. If you modify the enum, you will have to update the code that puts them in the container.
the beginnings of a solution involving no macros and (almost) no runtime overhead:
#include <iostream>
#include <utility>
#include <boost/mpl/vector.hpp>
#include <boost/mpl/find.hpp>
template<int v> using has_value = std::integral_constant<int, v>;
template<class...EnumValues>
struct better_enum
{
static constexpr size_t size = sizeof...(EnumValues);
using value_array = int[size];
static const value_array& values() {
static const value_array _values = { EnumValues::value... };
return _values;
}
using name_array = const char*[size];
static const name_array& names() {
static const name_array _names = { EnumValues::name()... };
return _names;
}
using enum_values = boost::mpl::vector<EnumValues...>;
struct iterator {
explicit iterator(size_t i) : index(i) {}
const char* name() const {
return names()[index];
}
int value() const {
return values()[index];
}
operator int() const {
return value();
}
void operator++() {
++index;
}
bool operator==(const iterator& it) const {
return index == it.index;
}
bool operator!=(const iterator& it) const {
return index != it.index;
}
const iterator& operator*() const {
return *this;
}
private:
size_t index;
};
friend std::ostream& operator<<(std::ostream& os, const iterator& iter)
{
os << "{ " << iter.name() << ", " << iter.value() << " }";
return os;
}
template<class EnumValue>
static iterator find() {
using iter = typename boost::mpl::find<enum_values, EnumValue>::type;
static_assert(iter::pos::value < size, "attempt to find a value which is not part of this enum");
return iterator { iter::pos::value };
}
static iterator begin() {
return iterator { 0 };
}
static iterator end() {
return iterator { size };
}
};
struct Pig : has_value<0> { static const char* name() { return "Pig";} };
struct Dog : has_value<7> { static const char* name() { return "Dog";} };
struct Cat : has_value<100> { static const char* name() { return "Cat";} };
struct Horse : has_value<90> { static const char* name() { return "Horse";} };
struct Monkey : has_value<1000> { static const char* name() { return "Monkey";} };
using animals = better_enum<
Pig,
Dog,
Cat,
Horse
>;
using namespace std;
auto main() -> int
{
cout << "size : " << animals::size << endl;
for (auto v : animals::values())
cout << v << endl;
for (auto v : animals::names())
cout << v << endl;
cout << "full iteration:" << endl;
for (const auto& i : animals())
{
cout << i << endl;
}
cout << "individials" << endl;
auto animal = animals::find<Dog>();
cout << "found : " << animal << endl;
while (animal != animals::find<Horse>()) {
cout << animal << endl;
++animal;
}
// will trigger the static_assert auto xx = animals::find<Monkey>();
return 0;
}
output:
size : 4
0
7
100
90
Pig
Dog
Cat
Horse
full iteration:
{ Pig, 0 }
{ Dog, 7 }
{ Cat, 100 }
{ Horse, 90 }
individials
found : { Dog, 7 }
{ Dog, 7 }
{ Cat, 100 }
Since an enum does not allow iteration you have to create an alternative representation of the enum and its range of values.
The approach that I would take would be a simple table lookup embedded in a class. The problem is that as the API modifies its enum with new entries, you would also need to update the constructor for this class.
The simple class that I would use would be comprised of a constructor to build the table along with a couple of methods to iterate over the table. Since you also want to know if there is a problem with table size when adding items, you could use an assert () macro that would issue an assert() in debug mode. In the source example below I use the preprocessor to test for debug compile or not and whether assert has been included or not so as to provide a mechanism for basic consistency checking.
I have borrowed an idea I saw from P. J. Plauger in his book Standard C Library of using a simple lookup table for ANSI character operations in which the character is used to index into a table.
To use this class you would do something like the following which uses a for loop to iterate over the set of values in the table. Within the body of the loop you would do whatever you want to do with the enum values.
CapiEnum myEnum;
for (CAPI_SUBTYPE_E jj = myEnum.Begin(); !myEnum.End(); jj = myEnum.Next()) {
// do stuff with the jj enum value
}
Since this class enumerates over the values, I have arbitrarily chosen to return the value of CAPI_SUBTYPE_NULL in those cases where we have reached the end of the enumeration. So the return value in the case of a table lookup error is in the valid range however it can not be depended upon. There fore a check on the End() method should be done to see if the end of an iteration has been reached. Also after doing the construction of the object one can check the m_bTableError data member to see if there was an error during construction.
The source example for the class follows. You would need to update the constructor with the enum values for the API as they change. Unfortunately there is not much that can be done to automate the check on an update enum however we do have tests in place in a debug compile to check that the table is large enough and that the value of the enum being put into the table is within range of the table size.
class CapiEnum {
public:
CapiEnum (void); // constructor
CAPI_SUBTYPE_E Begin (void); // method to call to begin an iteration
CAPI_SUBTYPE_E Next (void); // method to get the next in the series of an iteration
bool End (void); // method to indicate if we have reached the end or not
bool Check (CAPI_SUBTYPE_E value); // method to see if value specified is in the table
bool m_TableError;
private:
static const int m_TableSize = 256; // set the lookup table size
static const int m_UnusedTableEntry = -1;
int m_iIterate;
bool m_bEndReached;
CAPI_SUBTYPE_E m_CapiTable[m_TableSize];
};
#if defined(_DEBUG)
#if defined(assert)
#define ADD_CAPI_ENUM_ENTRY(capi) (((capi) < m_TableSize && (capi) > m_UnusedTableEntry) ? (m_CapiTable[(capi)] = (capi)) : assert(((capi) < m_TableSize) && ((capi) > m_UnusedTableEntry)))
#else
#define ADD_CAPI_ENUM_ENTRY(capi) (((capi) < m_TableSize && (capi) > m_UnusedTableEntry) ? (m_CapiTable[(capi)] = (capi)) : (m_TableError = true))
#endif
#else
#define ADD_CAPI_ENUM_ENTRY(capi) (m_CapiTable[(capi)] = (capi))
#endif
CapiEnum::CapiEnum (void) : m_bEndReached(true), m_iIterate(0), m_TableError(false)
{
for (int iLoop = 0; iLoop < m_TableSize; iLoop++) m_CapiTable[iLoop] = static_cast <CAPI_SUBTYPE_E> (m_UnusedTableEntry);
ADD_CAPI_ENUM_ENTRY(CAPI_SUBTYPE_NULL);
// .....
ADD_CAPI_ENUM_ENTRY(CAPI_SUBTYPE_DIAG_ASG);
}
CAPI_SUBTYPE_E CapiEnum::Begin (void)
{
m_bEndReached = false;
for (m_iIterate = 0; m_iIterate < m_TableSize; m_iIterate++) {
if (m_CapiTable[m_iIterate] > m_UnusedTableEntry) return m_CapiTable[m_iIterate];
}
m_bEndReached = true;
return CAPI_SUBTYPE_NULL;
}
CAPI_SUBTYPE_E CapiEnum::Next (void)
{
if (!m_bEndReached) {
for (m_iIterate++; m_iIterate < m_TableSize; m_iIterate++) {
if (m_CapiTable[m_iIterate] > m_UnusedTableEntry) return m_CapiTable[m_iIterate];
}
}
m_bEndReached = true;
return CAPI_SUBTYPE_NULL;
}
bool CapiEnum::End (void)
{
return m_bEndReached;
}
bool CapiEnum::Check (CAPI_SUBTYPE_E value)
{
return (value >= 0 && value < m_TableSize && m_CapiTable[value] > m_UnusedTableEntry);
}
And if you like you could add an additional method to retrieve the current value of the iteration. Notice that rather than incrementing to the next, the Current() method uses whatever the iteration index is currently at and starts searching from the current position. So if the current position is a valid value it just returns it otherwise it will find the first valid value. Alternatively, you could make this just return the current table value pointed to by the index and if the value is invalid, set an error indicator.
CAPI_SUBTYPE_E CapiEnum::Current (void)
{
if (!m_bEndReached) {
for (m_iIterate; m_iIterate < m_TableSize; m_iIterate++) {
if (m_CapiTable[m_iIterate] > m_UnusedTableEntry) return m_CapiTable[m_iIterate];
}
}
m_bEndReached = true;
return CAPI_SUBTYPE_NULL;
}
Here's one more approach. One bonus is that your compiler may warn you if you omit an enum value in a switch:
template<typename T>
void IMP_Apply(const int& pSubtype, T& pApply) {
switch (pSubtype) {
case CAPI_SUBTYPE_NULL :
case CAPI_SUBTYPE_DIAG_DFD :
case CAPI_SUBTYPE_DIAG_ERD :
case CAPI_SUBTYPE_DIAG_STD :
case CAPI_SUBTYPE_DIAG_STC :
case CAPI_SUBTYPE_DIAG_DSD :
case CAPI_SUBTYPE_SPEC_PROCESS :
case CAPI_SUBTYPE_SPEC_MODULE :
case CAPI_SUBTYPE_SPEC_TERMINATOR :
case CAPI_SUBTYPE_DD_ALL :
case CAPI_SUBTYPE_DD_COUPLE :
case CAPI_SUBTYPE_DD_DATA_AREA :
case CAPI_SUBTYPE_DD_DATA_OBJECT :
case CAPI_SUBTYPE_DD_FLOW :
case CAPI_SUBTYPE_DD_RELATIONSHIP :
case CAPI_SUBTYPE_DD_STORE :
case CAPI_SUBTYPE_DIAG_PAD :
case CAPI_SUBTYPE_DIAG_BD :
case CAPI_SUBTYPE_DIAG_UCD :
case CAPI_SUBTYPE_DIAG_PD :
case CAPI_SUBTYPE_DIAG_COD :
case CAPI_SUBTYPE_DIAG_SQD :
case CAPI_SUBTYPE_DIAG_CD :
case CAPI_SUBTYPE_DIAG_SCD :
case CAPI_SUBTYPE_DIAG_ACD :
case CAPI_SUBTYPE_DIAG_CPD :
case CAPI_SUBTYPE_DIAG_DPD :
case CAPI_SUBTYPE_DIAG_PFD :
case CAPI_SUBTYPE_DIAG_HIER :
case CAPI_SUBTYPE_DIAG_IDEF0 :
case CAPI_SUBTYPE_DIAG_AID :
case CAPI_SUBTYPE_DIAG_SAD :
case CAPI_SUBTYPE_DIAG_ASG :
/* do something. just `applying`: */
pApply(static_cast<CAPI_SUBTYPE_E>(pSubtype));
return;
}
std::cout << "Skipped: " << pSubtype << '\n';
}
template<typename T>
void Apply(T& pApply) {
const CAPI_SUBTYPE_E First(CAPI_SUBTYPE_NULL);
const CAPI_SUBTYPE_E Last(CAPI_SUBTYPE_DIAG_ASG);
for (int idx(static_cast<int>(First)); idx <= static_cast<int>(Last); ++idx) {
IMP_Apply(idx, pApply);
}
}
int main(int argc, const char* argv[]) {
class t_apply {
public:
void operator()(const CAPI_SUBTYPE_E& pSubtype) const {
std::cout << "Apply: " << static_cast<int>(pSubtype) << '\n';
}
};
t_apply apply;
Apply(apply);
return 0;
}
I'm using this type of constructions to define my own enums:
#include <boost/unordered_map.hpp>
namespace enumeration
{
struct enumerator_base : boost::noncopyable
{
typedef
boost::unordered_map<int, std::string>
kv_storage_t;
typedef
kv_storage_t::value_type
kv_type;
typedef
std::set<int>
entries_t;
typedef
entries_t::const_iterator
iterator;
typedef
entries_t::const_iterator
const_iterator;
kv_storage_t const & kv() const
{
return storage_;
}
const char * name(int i) const
{
kv_storage_t::const_iterator it = storage_.find(i);
if(it != storage_.end())
return it->second.c_str();
return "empty";
}
iterator begin() const
{
return entries_.begin();
}
iterator end() const
{
return entries_.end();
}
iterator begin()
{
return entries_.begin();
}
iterator end()
{
return entries_.end();
}
void register_e(int val, std::string const & desc)
{
storage_.insert(std::make_pair(val, desc));
entries_.insert(val);
}
protected:
kv_storage_t storage_;
entries_t entries_;
};
template<class T>
struct enumerator;
template<class D>
struct enum_singleton : enumerator_base
{
static enumerator_base const & instance()
{
static D inst;
return inst;
}
};
}
#define QENUM_ENTRY(K, V, N) K, N register_e((int)K, V);
#define QENUM_ENTRY_I(K, I, V, N) K = I, N register_e((int)K, V);
#define QBEGIN_ENUM(NAME, C) \
enum NAME \
{ \
C \
} \
}; \
} \
#define QEND_ENUM(NAME) \
}; \
namespace enumeration \
{ \
template<> \
struct enumerator<NAME>\
: enum_singleton< enumerator<NAME> >\
{ \
enumerator() \
{
QBEGIN_ENUM(test_t,
QENUM_ENTRY(test_entry_1, "number uno",
QENUM_ENTRY_I(test_entry_2, 10, "number dos",
QENUM_ENTRY(test_entry_3, "number tres",
QEND_ENUM(test_t)))))
int _tmain(int argc, _TCHAR* argv[])
{
BOOST_FOREACH(int x, enumeration::enumerator<test_t>::instance())
std::cout << enumeration::enumerator<test_t>::instance().name(x) << "=" << x << std::endl;
return 0;
}
Also you can replace storage_ type to boost::bimap to have bidirectional correspondance int <==> string
There are a lot of answers to this question already, but most of them are either very complicated or inefficient in that they don't directly address the requirement of iterating over an enum with gaps. Everyone so far has said that this is not possible, and they are sort of correct in that there is no language feature to allow you to do this. That certainly does not mean you can't, and as we can see by all the answers so far, there are many different ways to do it. Here is my way, based on the enum you have provided and the assumption that it's structure won't change much. Of course this method can be adapted as needed.
typedef enum {
CAPI_SUBTYPE_NULL = 0, /* Null subtype. */
CAPI_SUBTYPE_DIAG_DFD = 1, /* Data Flow diag. */
CAPI_SUBTYPE_DIAG_ERD = 2, /* Entity-Relationship diag. */
CAPI_SUBTYPE_DIAG_STD = 3, /* State Transition diag. */
CAPI_SUBTYPE_DIAG_STC = 4, /* Structure Chart diag. */
CAPI_SUBTYPE_DIAG_DSD = 5, /* Data Structure diag. */
CAPI_SUBTYPE_SPEC_PROCESS = 6, /* Process spec. */
CAPI_SUBTYPE_SPEC_MODULE = 7, /* Module spec. */
CAPI_SUBTYPE_SPEC_TERMINATOR = 8, /* Terminator spec. */
CAPI_SUBTYPE_DD_ALL = 13, /* DD Entries (All). */
CAPI_SUBTYPE_DD_COUPLE = 14, /* DD Entries (Couples). */
CAPI_SUBTYPE_DD_DATA_AREA = 15, /* DD Entries (Data Areas). */
CAPI_SUBTYPE_DD_DATA_OBJECT = 16, /* DD Entries (Data Objects). */
CAPI_SUBTYPE_DD_FLOW = 17, /* DD Entries (Flows). */
CAPI_SUBTYPE_DD_RELATIONSHIP = 18, /* DD Entries (Relationships). */
CAPI_SUBTYPE_DD_STORE = 19, /* DD Entries (Stores). */
CAPI_SUBTYPE_DIAG_PAD = 35, /* Physical architecture diagram. */
CAPI_SUBTYPE_DIAG_BD = 36, /* Behaviour diagram. */
CAPI_SUBTYPE_DIAG_UCD = 37, /* UML Use case diagram. */
CAPI_SUBTYPE_DIAG_PD = 38, /* UML Package diagram. */
CAPI_SUBTYPE_DIAG_COD = 39, /* UML Collaboration diagram. */
CAPI_SUBTYPE_DIAG_SQD = 40, /* UML Sequence diagram. */
CAPI_SUBTYPE_DIAG_CD = 41, /* UML Class diagram. */
CAPI_SUBTYPE_DIAG_SCD = 42, /* UML State chart. */
CAPI_SUBTYPE_DIAG_ACD = 43, /* UML Activity chart. */
CAPI_SUBTYPE_DIAG_CPD = 44, /* UML Component diagram. */
CAPI_SUBTYPE_DIAG_DPD = 45, /* UML Deployment diagram. */
CAPI_SUBTYPE_DIAG_PFD = 47, /* Process flow diagram. */
CAPI_SUBTYPE_DIAG_HIER = 48, /* Hierarchy diagram. */
CAPI_SUBTYPE_DIAG_IDEF0 = 49, /* IDEF0 diagram. */
CAPI_SUBTYPE_DIAG_AID = 50, /* AID diagram. */
CAPI_SUBTYPE_DIAG_SAD = 51, /* SAD diagram. */
CAPI_SUBTYPE_DIAG_ASG = 59 /* ASG diagram. */
} CAPI_SUBTYPE_E;
struct ranges_t
{
int start;
int end;
};
ranges_t ranges[] =
{
{CAPI_SUBTYPE_NULL, CAPI_SUBTYPE_NULL},
{CAPI_SUBTYPE_DIAG_DFD, CAPI_SUBTYPE_DIAG_DSD},
{CAPI_SUBTYPE_SPEC_PROCESS, CAPI_SUBTYPE_SPEC_TERMINATOR},
{CAPI_SUBTYPE_DD_ALL, CAPI_SUBTYPE_DD_STORE},
{CAPI_SUBTYPE_DIAG_PAD, CAPI_SUBTYPE_DIAG_SAD},
{CAPI_SUBTYPE_DIAG_ASG, CAPI_SUBTYPE_DIAG_ASG},
};
int numRanges = sizeof(ranges) / sizeof(*ranges);
for( int rangeIdx = 0; rangeIdx < numRanges; ++rangeIdx )
{
for( int enumValue = ranges[rangeIdx].start; enumValue <= ranges[rangeIdx].end; ++enumValue )
{
processEnumValue( enumValue );
}
}
Or something along those lines.
The only real 'solution' I finally came up with to solve this problem is to create a pre-run script that reads the c/c++ file(s) containing the enums and generates a class file that has a list of all the enums as vectors. This is very much the same way Visual Studio supports T4 Templates. In the .Net world it's pretty common practice but since I can't work in that environment I was forced to do it this way.
The script I wrote is in Ruby, but you could do it in whatever language. If anyone wants the source script, I uploaded it here. It's by no means a perfect script but it fit the bill for my project. I encourage anyone to improve on it and give tips here.
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?
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.
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.
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 ? :)
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;
}
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] ;
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
}
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"
};
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/
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.
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.
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!
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++.
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";
}
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)
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;
}
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
.................
}
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.
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); }
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 )
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.
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.
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
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.
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;
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";
}
#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 ) );
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;
}
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.
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.
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.