Можно просто енумы генерировать через макрос, который сгенерирует и энум, и inline-функцию принимающую данный enum и возвращающую строку. Проблема потом, как эту функцию вызвать. Например ENUMNAME_tostring(value). Ведь вместо ENUMNAME можно http://caxapa.ru/954915.html
вписать OTHERENUM_tostring(value). И оно проканает и ошибку можно не заметить. Я вообще ссылку раньше давал, но никто кроме SciFi не оценил.
Можно выкрутиться через заворачивание enum в структуру, что образует новый тип, но такой вариант меняет синтаксис (нужно .val дописывать):
вариант 1, со структурами:
https://coliru.sta …com/a/6186205fe2b2fa3c
Или можно без структуры, через указатели, тогда будут по крайней мере варнинги и синтаксис не меняется (но всегда нужно lvalue):
вариант 2, с указателями:
https://coliru.sta …com/a/3974a5a72c5a849d
Я считаю второй вариант наиболее адекватным, приведу его здесь:
#include <stdio.h>
#define EVAL0(...) __VA_ARGS__
#define EVAL1(...) EVAL0(EVAL0(EVAL0(__VA_ARGS__)))
#define EVAL2(...) EVAL1(EVAL1(EVAL1(__VA_ARGS__)))
#define EVAL3(...) EVAL2(EVAL2(EVAL2(__VA_ARGS__)))
#define EVAL4(...) EVAL3(EVAL3(EVAL3(__VA_ARGS__)))
#define EVAL(...) EVAL4(EVAL4(EVAL4(__VA_ARGS__)))
#define MAP_END(...)
#define MAP_OUT
#define MAP_COMMA ,
#define MAP_GET_END2() 0, MAP_END
#define MAP_GET_END1(...) MAP_GET_END2
#define MAP_GET_END(...) MAP_GET_END1
#define MAP_NEXT0(test, next, ...) next MAP_OUT
#define MAP_NEXT1(test, next) MAP_NEXT0(test, next, 0)
#define MAP_NEXT(test, next) MAP_NEXT1(MAP_GET_END test, next)
#define MAP0(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP1)(f, peek, __VA_ARGS__)
#define MAP1(f, x, peek, ...) f(x) MAP_NEXT(peek, MAP0)(f, peek, __VA_ARGS__)
#define MAP_LIST_NEXT1(test, next) MAP_NEXT0(test, MAP_COMMA next, 0)
#define MAP_LIST_NEXT(test, next) MAP_LIST_NEXT1(MAP_GET_END test, next)
#define MAP_LIST0(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST1)(f, peek, __VA_ARGS__)
#define MAP_LIST1(f, x, peek, ...) f(x) MAP_LIST_NEXT(peek, MAP_LIST0)(f, peek, __VA_ARGS__)
#define MAP(f, ...) EVAL(MAP1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
#define MAP_LIST(f, ...) EVAL(MAP_LIST1(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
#define CASE_STRINGIFY(v) case v: return #v;
#define SUPER_ENUM(_name_, ...) \
typedef enum _name_ { __VA_ARGS__ } _name_; \
const char *_name_##_tostr(const _name_ *_v_) { \
switch (*_v_) { \
MAP(CASE_STRINGIFY, __VA_ARGS__); \
default: return "unknown"; \
} \
}
/* Hint: add option "-E" to command line (at bottom of page) and
* press "Compile..." to see the code after unwinding macro. */
SUPER_ENUM(Numbers, zero, first, second, third, fourth, fifth, sixth, seventh, eighth, ninth, tenth)
SUPER_ENUM(Colors, red, green, blue)
int main()
{
Numbers i;
for (i = zero; i<= tenth; i++)
printf("%-3d", i), puts(Numbers_tostr(&i));
Colors c = red;
puts(Colors_tostr(&c));
puts(Numbers_tostr(&c)); // this gives only warning
return 0;
}