10

I would like to create a magical macro, or anything, that would generate a something like this:

MAGICAL_MACRO(return_type, method_name, ...)

should work like this:

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

->

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

Is this possible? I am afraid it is not.

István Csanády
  • 415
  • 5
  • 13
  • How would you forward the arguments without the types? – István Csanády Jun 26 '17 at 11:09
  • No, that is NOT the same question. I do know how to use perfect forwarding, that is NOT the question, please read the question carefully. – István Csanády Jun 26 '17 at 11:10
  • 1
    @πάντα ῥεῖ: I don't think it is a duplicate. He want's not only to forward the arguments which would be easy with a variadic macro. He also want to use the names of the macro for passing and the type + name in the definition. – Andre Kampling Jun 26 '17 at 11:11
  • The 2nd duplicate may help you better. – πάντα ῥεῖ Jun 26 '17 at 11:11
  • Thank you. Indeed. – István Csanády Jun 26 '17 at 11:12
  • https://stackoverflow.com/questions/25512630/passing-variadic-template-arguments-to-a-variadic-function then? – πάντα ῥεῖ Jun 26 '17 at 11:14
  • @πάνταῥεῖ Not really, this doesn't seem limited to what C variadics allow. – Angew is no longer proud of SO Jun 26 '17 at 11:14
  • No, template methods can not be virtual. This questions is not about c++11 forwarding. – István Csanády Jun 26 '17 at 11:15
  • For the record, your proposed syntax is brittle: `MAGICAL_MACRO(void, Foo, std::map a)` would be impossible to handle. – Quentin Jun 26 '17 at 11:30
  • That's just a rough idea, I am open to any suggestions, see below @Angew's answer – István Csanády Jun 26 '17 at 11:34
  • It is generally a bad idea to use macros in order to create variable or function declarations. There might be far more elegant ways to achieve the same thing. What is the problem you are trying to solve with this? – Lundin Jun 26 '17 at 13:31
  • 1. To annotate all the methods, in order to automagically generate python wrappers to these specific classes 2. Prevent calling these methods directly, since they have some very specific behavior that has to be strongly distinguished from any other method 3. Being able to alter the behavior of these method calls automatically, without having to modify all the methods. Eg. I would like to automatically create a log of these method calls, with all the arguments, and play back all the calls for debug purposes. – István Csanády Jun 26 '17 at 13:32

3 Answers3

6

Two questions: Are you open to a slightly different syntax for the arguments of MAGIC_MACRO? And can you use the Boost.Preprocessor header-only 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 looks like this:

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

[Live example]

The %% used in the macro definitions is just my way of indicating "this value is not used." You could use pretty much anything else there (unless it contains a comma).

The above solution will work as long as the types involved are not spelled with a comma. If they are, introduce a type alias for them (typedef or using). Note that it is possible to get around this within the preprocessor magic itself, but it complicates already ugly code.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
4

If you don't mind changing the syntax for the macro arguments, you could use following trick which abuses declaration syntax:

#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))

That will expand to:

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

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

The extra casts (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/PODs, but also for non-POD classes even if their copy constructors have side effects:

#include <iostream>

struct A
{
    int x;
    A(int value) : x(value) {}
    A(const A &o)
    {
        x = o.x;
        std::cout << "copy";
    }
};

void func(A a)
{
    std::cout << a.x << '\n';
}

void func1(A a)
{
    func(a);
}
void func2(A a)
{
    func(A(a));
}

int main()
{
    func1(1); // prints `copy1`
    func2(2); // prints `copy2`
}
HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
4

The code below is working for what you've asked for with up to 1024 arguments and without using additional stuff like boost. It defines an EVAL(...) and also a MAP(m, first, ...) macro to do recursion and to use for each iteration the macro m with the next parameter first.

It is mostly copied from C Pre-Processor Magic. It is also great explained there. You can also download these helper macros like EVAL(...) at this git repository, there are also a lot of explanation in the actual code. It is variadic so it takes the number of arguments you want.

But I changed the FIRST and the SECOND macro as it uses a Gnu extension like it is in the source I've copied it from.

To split arguments like 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 the other macros needed, note that type splitting need to be defined per macro here:

/* 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) A B
#define SPLIT(D) EVAL1(CAT(SPLIT_, D))
#define TYPE_NAME(D) CALL(SECOND,(SPLIT(D)))
Andre Kampling
  • 5,476
  • 2
  • 20
  • 47
  • Wow! But this will require to define SPLIT macros to every type, right? – István Csanády Jun 26 '17 at 12:33
  • @IstvánCsanády: Yeah right if you want to use that feature to call it like `MAGICAL_MACRO(void, FOO, int a, double b, char`); and not `MAGICAL_MACRO(void, FOO, int, a, double, b, char, c);` Also you may want to return a value if teh type is not void. I will integrate that too and extend my answer. – Andre Kampling Jun 26 '17 at 12:39
  • @IstvánCsanády: Now the macro also recognizes, if you want to return a value, type other than void. If so it uses a return statement. But there are limitations as void causes trouble. Maybe I will find a solution for that. Also the handling of pointer in the arguments list is not what you want? – Andre Kampling Jun 26 '17 at 13:23
  • You are missing semicolon after return statement. – Zereges Jun 26 '17 at 13:23
  • 1
    And you don't need to switch on return type. You can use `return foo();` in `void` method if `foo` also returns `void`. – Zereges Jun 26 '17 at 13:26
  • @Zereges: Thank you, now the macro easily always return something and added the missing semicolon. – Andre Kampling Jun 26 '17 at 13:33
  • @IstvánCsanády: Does any answer here meets your requirements or is here no answer that's working for you? – Andre Kampling Jul 09 '17 at 09:34