Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Preprocessor variadic FOR_EACH macro compatible with MSVC++10

I've seen a few questions asking for a variation on a variadic FOR_EACH macro. However unfortunately the answers provided are incompatible with VC++10 due to it expanding __VA_ARGS __ as one argument when passed to another macro. Please could someone provide a C++11 compliant (thus forward-compatible) version that still works with VC++10. Perhaps using the "workaround" that is often mentioned, #define EXPAND(x) x, however I don't know where to put this in order to get, for example, the latter generalised part of this answer to work in VC++10.

To clarify, the intended behaviour is for FOR_EACH(x, a, b, ...) to produce x(a) x(b), ..., where x is another macro.

like image 322
Dylan Avatar asked Feb 17 '23 14:02

Dylan


2 Answers

Having now grasped exactly how the VC++10 compiler bug works, I was able to come up with such a macro myself, based on the latter part of this answer.

#define EXPAND(x) x
#define FOR_EACH_1(what, x, ...) what(x)
#define FOR_EACH_2(what, x, ...)\
  what(x);\
  EXPAND(FOR_EACH_1(what,  __VA_ARGS__))
#define FOR_EACH_3(what, x, ...)\
  what(x);\
  EXPAND(FOR_EACH_2(what, __VA_ARGS__))
#define FOR_EACH_4(what, x, ...)\
  what(x);\
  EXPAND(FOR_EACH_3(what,  __VA_ARGS__))
#define FOR_EACH_5(what, x, ...)\
  what(x);\
  EXPAND(FOR_EACH_4(what,  __VA_ARGS__))
#define FOR_EACH_6(what, x, ...)\
  what(x);\
  EXPAND(FOR_EACH_5(what,  __VA_ARGS__))
#define FOR_EACH_7(what, x, ...)\
  what(x);\
  EXPAND(FOR_EACH_6(what,  __VA_ARGS__))
#define FOR_EACH_8(what, x, ...)\
  what(x);\
  EXPAND(FOR_EACH_7(what,  __VA_ARGS__))
#define FOR_EACH_NARG(...) FOR_EACH_NARG_(__VA_ARGS__, FOR_EACH_RSEQ_N())
#define FOR_EACH_NARG_(...) EXPAND(FOR_EACH_ARG_N(__VA_ARGS__))
#define FOR_EACH_ARG_N(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
#define FOR_EACH_RSEQ_N() 8, 7, 6, 5, 4, 3, 2, 1, 0
#define CONCATENATE(x,y) x##y
#define FOR_EACH_(N, what, ...) EXPAND(CONCATENATE(FOR_EACH_, N)(what, __VA_ARGS__))
#define FOR_EACH(what, ...) FOR_EACH_(FOR_EACH_NARG(__VA_ARGS__), what, __VA_ARGS__)

Example usage:

#define callMember(o, f) o.f();
#define callMember_o(f) callMember(o, f)
FOR_EACH(callMember_o, doSomething, doSomethingElse);

is the same as

o.doSomething(); o.doSomethingElse();

This solution is similar to that in the linked answer, except that the zero length variadic argument list in FOR_EACH(what, x, ...) when called with one element caused a spurious comma that makes FOR_EACH_NARG count 2 arguments instead of 1 argument, and the EXPAND macro workaround is used.

The bug in VC++10 is that if __VA_ARGS__ is passed to a macro within the definition of a variadic macro, it is evaluated after substitution into the macro, causing multiple comma separated arguments to be treated as one. To get around this you must delay argument evaluation until after __VA_ARGS__ is substituted, by wrapping the macro call in EXPAND, forcing the macro call to be evaluated as a string, substituting __VA_ARGS__ to do so. Only after the substitution into EXPAND is the macro called, by which point the variadic arguments are already substituted.

P.S. I would be grateful if anyone can suggest a method for compactly producing FOR_EACH_N macros for much larger values of N.

like image 66
Dylan Avatar answered Mar 08 '23 15:03

Dylan


I wrote a python code to generate for_each macro for N times, based on Dylan's answer.

below is the code:

from builtins import *

# number of generated for_each cases.
iter_max = 19

# override this with your own!
prefix = "MY_"  # define your own macro prefix, which ends with under_score if not empty.
internal_prefix = "internal_"

# CODE ------------------------------------------------------------------------------------------------------
INTERNAL_ = f"{internal_prefix}{prefix}"

fmt_numstr = "{}"  # f"{{:0>{max_digit}}}"

EXPAND = f"{INTERNAL_}EXPAND"

FOR_EACH = f"{INTERNAL_}FOR_EACH_"
FOR_EACH_N = f"{FOR_EACH}{fmt_numstr}"

OUT = "#pragma once\n"

OUT += f"#define {EXPAND}(x) x\n"
OUT += f"#define {FOR_EACH_N.format(1)}(what, x, ...) what(x)\n"

for i in range(2, iter_max + 1):
    OUT += f"#define {FOR_EACH_N.format(i)}(what, x, ...) " \
           f"what(x);" \
           f"{EXPAND}({FOR_EACH_N.format(i - 1)}(what, __VA_ARGS__))\n"

FOR_EACH_NARG = f"{INTERNAL_}FOR_EACH_NARG"
FOR_EACH_RSEQ_N = f"{INTERNAL_}FOR_EACH_RSEQ_N"
FOR_EACH_ARG_N = f"{INTERNAL_}FOR_EACH_ARG_N"

OUT += f"#define {FOR_EACH_NARG}(...) {FOR_EACH_NARG}_(__VA_ARGS__, {FOR_EACH_RSEQ_N}())\n"
OUT += f"#define {FOR_EACH_NARG}_(...) {EXPAND}({FOR_EACH_ARG_N}(__VA_ARGS__))\n"

underscore_sequence = ""
inverse_sequence = ""
for i in range(1, iter_max + 1):
    underscore_sequence += f"_{i}{',' if i < iter_max else ''}"

for i in range(0, iter_max + 1):
    inverse_sequence += f"{fmt_numstr}{',' if i < iter_max else ''} ".format(iter_max - i)

OUT += f"#define {FOR_EACH_ARG_N}({underscore_sequence}, N, ...) N\n"
OUT += f"#define {FOR_EACH_RSEQ_N}() {inverse_sequence}\n"

CONCATENATE = f"{INTERNAL_}CONCATENATE"

OUT += f"#define {CONCATENATE}(x,y) x##y\n"

INTERNAL_FOR_EACH = f"{INTERNAL_}FOR_EACH_"

OUT += f"#define {INTERNAL_FOR_EACH}(N, what, ...) {EXPAND}({CONCATENATE}({FOR_EACH}, N)(what, __VA_ARGS__))\n"
OUT += f"#define {prefix}FOR_EACH(what, ...) {INTERNAL_FOR_EACH}({FOR_EACH_NARG}(__VA_ARGS__), what, __VA_ARGS__)\n"

print(OUT)

Execution result is:

#pragma once
#define internal_MY_EXPAND(x) x
#define internal_MY_FOR_EACH_1(what, x, ...) what(x)
#define internal_MY_FOR_EACH_2(what, x, ...) what(x);internal_MY_EXPAND(internal_MY_FOR_EACH_1(what, __VA_ARGS__))
#define internal_MY_FOR_EACH_3(what, x, ...) what(x);internal_MY_EXPAND(internal_MY_FOR_EACH_2(what, __VA_ARGS__))
#define internal_MY_FOR_EACH_4(what, x, ...) what(x);internal_MY_EXPAND(internal_MY_FOR_EACH_3(what, __VA_ARGS__))
#define internal_MY_FOR_EACH_5(what, x, ...) what(x);internal_MY_EXPAND(internal_MY_FOR_EACH_4(what, __VA_ARGS__))
#define internal_MY_FOR_EACH_6(what, x, ...) what(x);internal_MY_EXPAND(internal_MY_FOR_EACH_5(what, __VA_ARGS__))
#define internal_MY_FOR_EACH_7(what, x, ...) what(x);internal_MY_EXPAND(internal_MY_FOR_EACH_6(what, __VA_ARGS__))
#define internal_MY_FOR_EACH_8(what, x, ...) what(x);internal_MY_EXPAND(internal_MY_FOR_EACH_7(what, __VA_ARGS__))
#define internal_MY_FOR_EACH_9(what, x, ...) what(x);internal_MY_EXPAND(internal_MY_FOR_EACH_8(what, __VA_ARGS__))
#define internal_MY_FOR_EACH_10(what, x, ...) what(x);internal_MY_EXPAND(internal_MY_FOR_EACH_9(what, __VA_ARGS__))
#define internal_MY_FOR_EACH_11(what, x, ...) what(x);internal_MY_EXPAND(internal_MY_FOR_EACH_10(what, __VA_ARGS__))
#define internal_MY_FOR_EACH_12(what, x, ...) what(x);internal_MY_EXPAND(internal_MY_FOR_EACH_11(what, __VA_ARGS__))
#define internal_MY_FOR_EACH_13(what, x, ...) what(x);internal_MY_EXPAND(internal_MY_FOR_EACH_12(what, __VA_ARGS__))
#define internal_MY_FOR_EACH_14(what, x, ...) what(x);internal_MY_EXPAND(internal_MY_FOR_EACH_13(what, __VA_ARGS__))
#define internal_MY_FOR_EACH_15(what, x, ...) what(x);internal_MY_EXPAND(internal_MY_FOR_EACH_14(what, __VA_ARGS__))
#define internal_MY_FOR_EACH_16(what, x, ...) what(x);internal_MY_EXPAND(internal_MY_FOR_EACH_15(what, __VA_ARGS__))
#define internal_MY_FOR_EACH_17(what, x, ...) what(x);internal_MY_EXPAND(internal_MY_FOR_EACH_16(what, __VA_ARGS__))
#define internal_MY_FOR_EACH_18(what, x, ...) what(x);internal_MY_EXPAND(internal_MY_FOR_EACH_17(what, __VA_ARGS__))
#define internal_MY_FOR_EACH_19(what, x, ...) what(x);internal_MY_EXPAND(internal_MY_FOR_EACH_18(what, __VA_ARGS__))
#define internal_MY_FOR_EACH_NARG(...) internal_MY_FOR_EACH_NARG_(__VA_ARGS__, internal_MY_FOR_EACH_RSEQ_N())
#define internal_MY_FOR_EACH_NARG_(...) internal_MY_EXPAND(internal_MY_FOR_EACH_ARG_N(__VA_ARGS__))
#define internal_MY_FOR_EACH_ARG_N(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19, N, ...) N
#define internal_MY_FOR_EACH_RSEQ_N() 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0 
#define internal_MY_CONCATENATE(x,y) x##y
#define internal_MY_FOR_EACH_(N, what, ...) internal_MY_EXPAND(internal_MY_CONCATENATE(internal_MY_FOR_EACH_, N)(what, __VA_ARGS__))
#define MY_FOR_EACH(what, ...) internal_MY_FOR_EACH_(internal_MY_FOR_EACH_NARG(__VA_ARGS__), what, __VA_ARGS__)

You may customize macro prefix and internal prefixes for your own use.

like image 41
Seungwoo Kang Avatar answered Mar 08 '23 15:03

Seungwoo Kang