3

I want to create a lookup up table from a constant value to a constant name

struct char TABLE[128][32] = { [CONST_1] = "CONST_1",[CONST_2] = "CONST_2", ... };

I defined two macros for that

#define TBL(N, S, ...) static char N[S][1 << 5] = { __VA_ARGS__ };
#define V(T) [T] = #T

So I can define my table like that,

TBL (CODES, LastExtensionError,
       V (Success), V (BadRequest), V (BadValue), ... );

Is it possible to avoid wrapping each constant with V and write something like?

TBL (CODES, LastExtensionError,
       Success, BadRequest, BadValue, ...);

andreoss
  • 1,570
  • 1
  • 10
  • 25
  • It is possible if you can loop over arguments of a variadic macro, something like `FOREACH(M, __VA_ARGS__)` where `M` is a function-like macro. Looping is doable if you restrict the maximal number of inerations beforehand. There are quite a few implementations of `FOREACH` around. – n. m. could be an AI Jul 04 '23 at 17:08
  • https://stackoverflow.com/questions/5093460/how-to-convert-an-enum-type-variable-to-a-string might be helpful. – Shawn Jul 04 '23 at 17:18
  • There seems to be a [`__VA_OPT__`](https://www.scs.stanford.edu/~dm/blog/va-opt.html) in C++20. How about it? – gthanop Jul 04 '23 at 17:25

2 Answers2

2

Is it possible to avoid wrapping each constant with V and write something like?

This is C preprocessor, one way or the other you have to enumerate all possible iterations. You can write a foreach macro and apply it on each element.

I see no value in hiding a simple static char N[S][1 << 5] = behind a macro. Just write static char CODES[LastExtensionError][1 << 5] =. If you do want to repeat [1 << 5], consider making a structure.

#define FOREACH_1(f, _1) f(_1)
#define FOREACH_2(f, _1, ...) f(_1) FOREACH_1(f, __VA_ARGS__)
#define FOREACH_3(f, _1, ...) f(_1) FOREACH_2(f, __VA_ARGS__)
#define FOREACH_4(f, _1, ...) f(_1) FOREACH_3(f, __VA_ARGS__)
#define FOREACH_5(f, _1, ...) f(_1) FOREACH_4(f, __VA_ARGS__)
/* etc. add more cases as many as you need */
#define FOREACH_N(_5,_4,_3,_2,_1,N,...)  FOREACH##N
#define FOREACH(f, ...)  FOREACH_N(__VA_ARGS__,_5,_4,_3,_2_,_1)(f, __VA_ARGS__)

#define TBL_FOREACH_CB(a)  [a] = #a,
#define TBL_INIT(...)  { FOREACH(TBL_FOREACH_CB, __VA_ARGS__) }

static char CODES[LastExtensionError][1 << 5] = TBL_INIT(
    Success,
    BadRequest,
    BadValue
)

You could use a library like BOOST_PP_FOR or P99_FOR if you do not want to write it yourself.

Also, it sounds odd that this is static char CODES[N][1<<5] and not like static const char *const CODES[N]. It's odd that values are mutable, and all strings have constant length, sounds like wasted memory.

KamilCuk
  • 120,984
  • 8
  • 59
  • 111
1

Here is a solution that scales exponentially with the number of lines:

// supports 782 iterations ((5^n + 3)/4)
#define CM__0(P,f,...) CM_P1(CM_P1(CM_P1(CM_P1(CM_P1(CM_##f(,P##__VA_ARGS__))))))
#define CM__1(P,f,...) CM_P2(CM_P2(CM_P2(CM_P2(CM_P2(CM_##f(,P##__VA_ARGS__))))))
#define CM__2(P,f,...) CM_P3(CM_P3(CM_P3(CM_P3(CM_P3(CM_##f(,P##__VA_ARGS__))))))
#define CM__3(P,f,...) CM_P4(CM_P4(CM_P4(CM_P4(CM_P4(CM_##f(,P##__VA_ARGS__))))))
#define CM__4(P,f,...) CM_##f(,P##__VA_ARGS__)
#define CM_P1(x) CM__1 x
#define CM_P2(x) CM__2 x
#define CM_P3(x) CM__3 x
#define CM_P4(x) CM__4 x
#define CM_SCAN(...) __VA_ARGS__
#define CM_EAT(...)
#define CM_LPAREN (
#define CM(...) CM_SCAN(CM_EAT CM_LPAREN CM__0(__VA_ARGS__))

#define TUPLE_AT_1(a,b,...) b
#define CHECK(...) TUPLE_AT_1(__VA_ARGS__,)

#define TBL_0end_0end ,TBL_END
#define CM_0tbl(P,x,...) CHECK(TBL_0end_##x, TBL_NEXT)(,x,P##__VA_ARGS__)
#define TBL_END(...) )
#define TBL_NEXT(P,x,...) (,0tbl,P##__VA_ARGS__) [x] = #x,

#define TBL(...) CM(,0tbl,__VA_ARGS__,0end)

struct char TABLE[128][32] = {
    TBL(CONST_1, CONST_2, CONST_3)
    // [CONST_3] = "CONST_3", [CONST_2] = "CONST_2", [CONST_1] = "CONST_1",
};
camel-cdr
  • 643
  • 4
  • 8