C ++ or macro magic to generate a method and forward arguments

I would like to create a magic macro or something else that will create something like this:

MAGICAL_MACRO(return_type, method_name, ...) 

should work as follows:

 MAGICAL_MACRO(void, Foo, int a, int b) 

->

 virtual void Foo(int a, int b) { _obj->Foo(a, b); } 

Is it possible? I am afraid that this is not so.

+5
source share
3 answers

Two questions: do you open a slightly different argument syntax MAGIC_MACRO ? And can you use the Boost.Preprocessor header library ?

If both answers are yes, I have a solution for you:

 #define MAGICAL_MACRO(Type, Name, ...) \ virtual Type Name(MAGICAL_GENERATE_PARAMETERS(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))) {\ _obj->Name(MAGICAL_GENERATE_ARGUMENTS(BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))); \ } #define MAGICAL_GENERATE_PARAMETERS(Args) \ BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(MAGICAL_MAKE_PARAMETER, %%, Args)) #define MAGICAL_GENERATE_ARGUMENTS(Args) \ BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(MAGICAL_MAKE_ARGUMENT, %%, Args)) #define MAGICAL_MAKE_PARAMETER(s, Unused, Arg) \ BOOST_PP_TUPLE_ELEM(2, 0, Arg) BOOST_PP_TUPLE_ELEM(2, 1, Arg) #define MAGICAL_MAKE_ARGUMENT(s, Unused, Arg) \ BOOST_PP_TUPLE_ELEM(2, 1, Arg) 

Usage is as follows:

 MAGICAL_MACRO(void, Foo, (int, a), (int, b)) 

[Live example]

%% used in macro definitions is just my way of saying "this value is not used." You could use almost anything (except a comma).

The above solution will work until the types being used are written with a comma. If they are, enter a type alias for them ( typedef or using ). Note that it is possible to get around this inside the preprocessor magic itself, but it complicates the already ugly code.

+5
source

If you don't mind changing the syntax of the macro arguments, you can use the following trick that overrides the syntax of the declaration:

 #define MAGICAL_MACRO(return_type, method_name, ...) \ virtual return_type method_name(__VA_ARGS__) { \ _obj->method_name(__VA_ARGS__); \ } MAGICAL_MACRO(void, foo, int(a), int(b)) 

This will expand to:

 virtual void foo(int(a), int(b)) { _obj->foo(int(a), int(b)); } 

Where void func(int(a), int(b)) completely equivalent to void func(int a, int b) .

Additional roles (or constructor calls depending on argument types) are ugly, but both GCC and Clang (with -O0 ) seem to ignore them not only for primitive types / POD, but also for classes other than POD, even if their copy constructors have side effects:

 #include <iostream> struct A { int x; A(int value) : x(value) {} A(const A &o) { x = ox; std::cout << "copy"; } }; void func(A a) { std::cout << ax << '\n'; } void func1(A a) { func(a); } void func2(A a) { func(A(a)); } int main() { func1(1); // prints `copy1` func2(2); // prints `copy2` } 
+2
source

The code below works on what you requested with up to 1024 arguments and without , using additional stuff like boost. It defines the EVAL(...) macro, as well as the MAP(m, first, ...) macro, to perform recursion, and use the m macro for each iteration with the following first parameter.

It is mainly copied from C Pre-Processor Magic . It is also well explained. You can also download these helper macros, such as EVAL(...) in this git repository , there are many explanations in the actual code. It is variable, so the number of arguments you want is required.

But I changed the macro first and SECOND , because it uses the Gnu extension, similar to the one in the source from which I copied it.

To separate arguments of type int a into int and a , I used this answer from SO .

Your macro will be:

 #define MAGICAL_MACRO(return_type, method_name, ...) \ virtual return_type method_name(__VA_ARGS__) \ { \ return _obj->method_name(EVAL(MAP(TYPE_NAME, __VA_ARGS__))); \ } 

Examples and limitations:

 MAGICAL_MACRO(void, FOO, int a, double b, char c); --> virtual void FOO(int a, double b, char c) { return _obj->FOO(a , b , c); }; MAGICAL_MACRO(int, FOO, int a, double b, char c); --> virtual int FOO(int a, double b, char c) { return _obj->FOO(a , b , c); } ; MAGICAL_MACRO(void, FOO, int* a, double* b, char* c); --> virtual void* FOO(int* a, double* b, char* c) { return _obj->FOO(* a , * b , * c); }; /* maybe not what you want: pointer are dereferenced */ 

All other macros are needed; note that for each macro it is necessary to define a type partition:

 /* Define all types here */ #define SPLIT_int int COMMA #define SPLIT_char char COMMA #define SPLIT_float float COMMA #define SPLIT_double double COMMA #define FIRST_(a, ...) a #define SECOND_(a, b, ...) b #define FIRST(...) FIRST_(__VA_ARGS__,) #define SECOND(...) SECOND_(__VA_ARGS__,) #define EMPTY() #define EVAL(...) EVAL1024(__VA_ARGS__) #define EVAL1024(...) EVAL512(EVAL512(__VA_ARGS__)) #define EVAL512(...) EVAL256(EVAL256(__VA_ARGS__)) #define EVAL256(...) EVAL128(EVAL128(__VA_ARGS__)) #define EVAL128(...) EVAL64(EVAL64(__VA_ARGS__)) #define EVAL64(...) EVAL32(EVAL32(__VA_ARGS__)) #define EVAL32(...) EVAL16(EVAL16(__VA_ARGS__)) #define EVAL16(...) EVAL8(EVAL8(__VA_ARGS__)) #define EVAL8(...) EVAL4(EVAL4(__VA_ARGS__)) #define EVAL4(...) EVAL2(EVAL2(__VA_ARGS__)) #define EVAL2(...) EVAL1(EVAL1(__VA_ARGS__)) #define EVAL1(...) __VA_ARGS__ #define DEFER1(m) m EMPTY() #define DEFER2(m) m EMPTY EMPTY()() #define DEFER3(m) m EMPTY EMPTY EMPTY()()() #define DEFER4(m) m EMPTY EMPTY EMPTY EMPTY()()()() #define IS_PROBE(...) SECOND(__VA_ARGS__, 0) #define PROBE() ~, 1 #define CAT(a,b) a ## b #define NOT(x) IS_PROBE(CAT(_NOT_, x)) #define _NOT_0 PROBE() #define BOOL(x) NOT(NOT(x)) #define IF_ELSE(condition) _IF_ELSE(BOOL(condition)) #define _IF_ELSE(condition) CAT(_IF_, condition) #define _IF_1(...) __VA_ARGS__ _IF_1_ELSE #define _IF_0(...) _IF_0_ELSE #define _IF_1_ELSE(...) #define _IF_0_ELSE(...) __VA_ARGS__ #define HAS_ARGS(...) BOOL(FIRST(_END_OF_ARGUMENTS_ __VA_ARGS__)()) #define _END_OF_ARGUMENTS_() 0 #define MAP(m, first, ...) \ m(first) \ IF_ELSE(HAS_ARGS(__VA_ARGS__))( \ COMMA DEFER2(_MAP)()(m, __VA_ARGS__) \ )( \ /* Do nothing, just terminate */ \ ) #define _MAP() MAP #define COMMA , #define CALL(A,B) AB #define SPLIT(D) EVAL1(CAT(SPLIT_, D)) #define TYPE_NAME(D) CALL(SECOND,(SPLIT(D))) 
+2
source

Source: https://habr.com/ru/post/1269218/


All Articles