C-препроцессора рекурсивный макрос
#define PP_ARG0_(arg0, ...) arg0
#define PP_REST_(arg0, ...) __VA_ARGS__
#define PP_ARG0(args) PP_ARG0_ args
#define PP_REST(args) PP_REST_ args
#define FUNCTION(name) void name();
#define FUNCTION_TABLE(...)
FUNCTION(PP_ARG0((__VA_ARGS__)))
FUNCTION_TABLE(PP_REST((__VA_ARGS__)))
тестовый код:
FUNCTION_TABLE(f1, f2,f3,testA,testB,testC);
очевидно, из-за рекурсивного расширения он будет только объявить void f1();
а остальные не будут расширены:
void f1(); FUNCTION_TABLE(f2,f3,testA,testB,testC);
какой трюк я могу использовать для достижения рекурсивного расширения в этом случае? Проблема в том, что мне нужно поддерживать много аргументов (до 100), и я абсолютно не могу использовать boost.
2 ответов
самое простое решение - использовать итерацию последовательности следующим образом:
#define CAT(x, y) PRIMITIVE_CAT(x, y)
#define PRIMITIVE_CAT(x, y) x ## y
#define FUNCTION(name) void name();
#define FUNCTION_TABLE(seq) CAT(FUNCTION_TABLE_1 seq, _END)
#define FUNCTION_TABLE_1(x) FUNCTION(x) FUNCTION_TABLE_2
#define FUNCTION_TABLE_2(x) FUNCTION(x) FUNCTION_TABLE_1
#define FUNCTION_TABLE_1_END
#define FUNCTION_TABLE_2_END
тогда вы звоните FUNCTION_TABLE
с последовательностью препроцессора вместо аргументов varidiac:
FUNCTION_TABLE((f1)(f2)(f3)(testA)(testB)(testC))
не только это намного проще, но и быстрее (т. е. быстрее компиляция), чем использование рекурсивного решения(например, вы показали или как этот здесь).
вот ответ на случай, если кто-то захочет сделать то же самое.
#define _PP_0(_1, ...) _1 // (a,b,c,d) => a
#define _PP_X(_1, ...) (__VA_ARGS__) // (a,b,c,d) => (b,c,d)
//for each a in __VA_ARGS__ do f(a,x)
//where x is some parameter passed to PP_TRANSFORM
#define PP_TRANSFORM(f,x,...) \
PP_JOIN(PP_TRANSFORM_,PP_NARG(__VA_ARGS__))(f,x,(__VA_ARGS__))
#define PP_TRANSFORM_0(...)
#define PP_TRANSFORM_1( f,x,a) f(_PP_0 a,x) PP_TRANSFORM_0( f,x,_PP_X a)
#define PP_TRANSFORM_2( f,x,a) f(_PP_0 a,x) PP_TRANSFORM_1( f,x,_PP_X a)
...
#define PP_TRANSFORM_51(f,x,a) f(_PP_0 a,x) PP_TRANSFORM_50( f,x,_PP_X a)
...
#define PP_TRANSFORM_99(f,x,a) f(_PP_0 a,x) PP_TRANSFORM_98(f,x,_PP_X a)
#define PP_TRANSFORM_100(f,x,a)f(_PP_0 a,x) PP_TRANSFORM_99(f,x,_PP_X a)
здесь PP_NARG
- это макрос, который подсчитывает количество аргументов и PP_JOIN
- это макрос, который соединяет токены (то есть PP_JOIN(a,b) => ab
). Вам также нужно будет исправить это PP_NARG
Если вы хотите иметь возможность обрабатывать более 64 аргументов.
теперь вернемся к изначальному вопросу. Решение с использованием PP_TRANSFORM
- это:
#define FUNCTION(name, dummy) void name();
#define FUNCTION_TABLE(...) PP_TRANSFORM(FUNCTION,dummy,__VA_ARGS__)
если вы хотите создать реализацию c++ функции, то вы можете использовать этот непрозрачный параметр x PP_TRANSFORM
:
#define FUNCTION_CPP(name, class) void class::name(){}
#define FUNCTION_TABLE_CPP(...) PP_TRANSFORM(FUNCTION_CPP,MyClass,__VA_ARGS__)
все это одинаково хорошо работает с препроцессорами GCC и MSVC; PP_TRANSFORM_NN не использует __VA_ARGS__
чтобы избежать отдельных реализаций 100 определяет для GCC и MSVC