c++c-preprocessorauto-generate

How to use a preprocessor to separate parameters from function signatures in C++?


I am using a C++preprocessor to batch generate function definitions, but there is an issue when passing parameters

#include <iostream>

#define FOR_EACH_FUNC(_) \
    _(int, add, int a, int b) \
    _(int, subtract, int a, int b)

#define FORWARD_FUNC(ret, func, ...) \
    ret func(__VA_ARGS__) { \
        impl->func(__VA_ARGS__); \        // error!
    } \

FOR_EACH_FUNC(FORWARD_FUNC)

int main()
{
    int r1 = add(1, 2);
    int r2 = subtract(1, 2);
    return 0;
}

The result I want is

int add(int a, int b) {
    impl->add(a, b);
}

But the current result is

int add(int a, int b) {
    impl->add(int a, int b);
}

Is there any way to solve this problem? Thanks

I tried using C++'s variable parameter template to solve it, but it was not successful

Supplementary explanation:

We have a library A that needs to be used by a third party, but it relies on many other libraries. In order to clean up the compilation dependencies of others, we have implemented an intermediate library B that calls the methods of A through dlopen/dlsys (library A and its dependencies will be built into our system).

The problem here is: to implement a function, we need to first implement it in A, then define it in B as well, then define the function pointer, find the corresponding function address, and implement a simple logic to forward the request to A. I want to simplify this process


Solution

  • If all your arguments are trivially copyable (which stuff crossing a DLL boundary should be), you can use this:

    #define FOR_EACH_FUNC(_) \
        _(int, add, int(a), int(b)) \
        _(int, subtract, int(a), int(b))
    

    Which will just be a bracketed name in the function declaration but be a functional cast as an expression.

    This won't work with multi-word type names like unsigned long long, but you can use a typedef (using ull = unsigned long long;) or std::type_identity_t<unsigned long long>.


    If you don't mind automatically named parameters (int _0, int _1), you can do this with Boost.Preprocessor:

    #include <boost/preprocessor/cat.hpp>
    #include <boost/preprocessor/comma_if.hpp>
    #include <boost/preprocessor/repeat.hpp>
    #include <boost/preprocessor/seq/for_each_i.hpp>
    #include <boost/preprocessor/variadic/size.hpp>
    #include <boost/preprocessor/variadic/to_seq.hpp>
    
    #define NUMBERED_ARGUMENTS_DECL_IMPL(R, PREFIX, I, TYPE) BOOST_PP_COMMA_IF(I) TYPE BOOST_PP_CAT(PREFIX, I)
    #define NUMBERED_ARGUMENTS_DECL(...) BOOST_PP_SEQ_FOR_EACH_I(NUMBERED_ARGUMENTS_DECL_IMPL, _, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
    // `NUMBERED_ARGUMENTS_DECL(int, char, void*)` -> `int _0, char _1, void* _2`
    
    #define NUMBERED_ARGUMENTS_EXPR_IMPL(Z, I, PREFIX) BOOST_PP_COMMA_IF(I) BOOST_PP_CAT(PREFIX, I)
    #define NUMBERED_ARGUMENTS_EXPR(...) BOOST_PP_REPEAT(BOOST_PP_VARIADIC_SIZE(__VA_ARGS__), NUMBERED_ARGUMENTS_EXPR_IMPL, _)
    // `NUMBERED_ARGUMENTS_DECL(int, char, void*)` -> `_0, _1, _2`
    
    #define FOR_EACH_FUNC(_) \
        _(int, add, int, int) \
        _(int, subtract, int, int)
    
    #define FORWARD_FUNC(ret, func, ...) \
        ret func(NUMBERED_ARGUMENTS_DECL(__VA_ARGS__)) { \
            return impl->func(NUMBERED_ARGUMENTS_EXPR(__VA_ARGS__)); \
        } \
    
    FOR_EACH_FUNC(FORWARD_FUNC)