Creating a String List and an Enum List from a C++ MACro

Easy way to use variables of enum types as string in C?

There's no built-in solution. The easiest way is with an array of char* where the enum's int value indexes to a string containing the descriptive name of that enum. If you have a sparse enum (one that doesn't start at 0 or has gaps in the numbering) where some of the int mappings are high enough to make an array-based mapping impractical then you could use a hash table instead.

enum to string in modern C++11 / C++14 / C++17 and future C++20

Magic Enum header-only library provides static reflection for enums (to string, from string, iteration) for C++17.

#include <magic_enum.hpp>

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
};

For more examples check home repository https://github.com/Neargye/magic_enum.

Where is the drawback?

This library uses a compiler-specific hack (based on __PRETTY_FUNCTION__ / __FUNCSIG__), which works on Clang >= 5, MSVC >= 15.3 and GCC >= 9.

Enum value must be in range [MAGIC_ENUM_RANGE_MIN, MAGIC_ENUM_RANGE_MAX].

  • By default MAGIC_ENUM_RANGE_MIN = -128, MAGIC_ENUM_RANGE_MAX = 128.

  • If need another range for all enum types by default, redefine the macro MAGIC_ENUM_RANGE_MIN and MAGIC_ENUM_RANGE_MAX.

  • MAGIC_ENUM_RANGE_MIN must be less or equals than 0 and must be greater than INT16_MIN.

  • MAGIC_ENUM_RANGE_MAX must be greater than 0 and must be less than INT16_MAX.

  • If need another range for specific enum type, add specialization enum_range for necessary enum type.

    #include <magic_enum.hpp>

    enum number { one = 100, two = 200, three = 300 };

    namespace magic_enum {
    template <>
    struct enum_range<number> {
    static constexpr int min = 100;
    static constexpr int max = 300;
    };
    }

How to convert enum names to string in c

One way, making the preprocessor do the work. It also ensures your enums and strings are in sync.

#define FOREACH_FRUIT(FRUIT) \
FRUIT(apple) \
FRUIT(orange) \
FRUIT(grape) \
FRUIT(banana) \

#define GENERATE_ENUM(ENUM) ENUM,
#define GENERATE_STRING(STRING) #STRING,

enum FRUIT_ENUM {
FOREACH_FRUIT(GENERATE_ENUM)
};

static const char *FRUIT_STRING[] = {
FOREACH_FRUIT(GENERATE_STRING)
};

After the preprocessor gets done, you'll have:

enum FRUIT_ENUM {
apple, orange, grape, banana,
};

static const char *FRUIT_STRING[] = {
"apple", "orange", "grape", "banana",
};

Then you could do something like:

printf("enum apple as a string: %s\n",FRUIT_STRING[apple]);

If the use case is literally just printing the enum name, add the following macros:

#define str(x) #x
#define xstr(x) str(x)

Then do:

printf("enum apple as a string: %s\n", xstr(apple));

In this case, it may seem like the two-level macro is superfluous, however, due to how stringification works in C, it is necessary in some cases. For example, let's say we want to use a #define with an enum:

#define foo apple

int main() {
printf("%s\n", str(foo));
printf("%s\n", xstr(foo));
}

The output would be:

foo
apple

This is because str will stringify the input foo rather than expand it to be apple. By using xstr the macro expansion is done first, then that result is stringified.

See Stringification for more information.

How can I avoid repeating myself when creating a C++ enum and a dependent data structure?

There is an old pre-processor trick for this:

Gadget.data

DEFINE_GADGET(First)
DEFINE_GADGET(Second)

Gadget.**

#define QUOTE_VAL(X)  #X

enum Gadget
{
#define DEFINE_GADGET(X) X,
#include "Gadget.data"
#undef DEFINE_GADGET(X)
};

const char* gadget_debug_names[] = {
#define DEFINE_GADGET(X) QUOTE_VAL(X),
#include "Gadget.data"
#undef DEFINE_GADGET(X)
};

Confusion about C macro expansion in enum

The macros are used for compile time checking. This is useful when you write code that will be compiled and run on many different platforms and where some platforms may not be compatible.

If the first parameter to FWTS_ASSERT evaluates to non-zero (true) then !!(e) will evaluate to 1 and the enum will be created with the name FWTS_ASSERT_<second parameter>_in_line_<line>. I suspect that the enum is never actually used.

If the first parameter to FWTS_ASSERT evaluates to 0 (= false) then the compiler will try to compute 1/0 and generate a compiler error where it will hopefully tell which enum member caused the error, in this case FWTS_ASSERT_fwts_register_name_to_long_in_line_4.

And btw, the FWTS_CONCAT_EXPAND(a,b) and FWTS_CONCAT(a, b) seem to be duplicated, why do we need 2 of them?

FTW_CONCAT_EXPAND is done in 2 steps because we want to first expand any macros in the parameters and then perform the concatenation. Doing it in two steps makes the preprocessor do macro expansion of the parameters before it does the string concatenation.

How to generate compile time enum values with a macro function in c#?

According to @PaulF and similar answer from here.

one possible solution could be:

     public sealed class UnixDBCSPages
{
public static readonly uint JIS = MakeUnixDBCS(Convert.ToUInt32(0x0C));
public static readonly uint EUCJP = MakeUnixDBCS(Convert.ToUInt32(0x0D));
public static readonly uint CNS11643_1 = MakeUnixDBCS(3);
public static readonly uint EUC_CNS_1 = MakeUnixDBCS(7) ;
public static readonly uint CNS11643_2 = MakeUnixDBCS(4) ;
public static readonly uint EUC_CNS_2 = MakeUnixDBCS(8);
public static readonly uint KSC1987 = MakeUnixDBCS(6);
public static readonly uint GB2312 = MakeUnixDBCS(5);

private static uint MakeUnixDBCS(uint wCodePage)
{
uint wUnixUnmask = Convert.ToUInt32(Flags.UnixUnmask);
return (wUnixUnmask | wCodePage) << 16;
}
}

Making something both a C identifier and a string?

For your second #define, you need to use the # preprocessor operator, like this:

#define myDefine(a) myFunc(a, #a);

That converts the argument to a string.

Can a C program determine a macro identifier/enum name based on its value?

Not directly, as pointed out by others those identifiers are not available at runtime, but you can use a parallel list of names (an X Macro can help):

#include <stdio.h>

#define ERRS \
X(ERR_SUCCESS) \
X(ERR_BAD_INPUT) \
X(ERR_MORE)

#define X(x) x,
enum err_t {ERRS};
#undef X

#define X(x) #x,
static char *err_name[] = {ERRS};
#undef X

static int foo(void)
{
/* ... */
return ERR_BAD_INPUT;
}

int main(void)
{
printf("%s\n", err_name[foo()]);
return 0;
}

Output:

ERR_BAD_INPUT


Related Topics



Leave a reply



Submit