Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c++

c++11

macros

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.

like image 760
István Csanády Avatar asked Jun 26 '17 11:06

István Csanády


2 Answers

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.

like image 150
Angew is no longer proud of SO Avatar answered Oct 02 '22 08:10

Angew is no longer proud of SO


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`
}
like image 39
HolyBlackCat Avatar answered Oct 02 '22 07:10

HolyBlackCat