I have a macro:

#define WRAP_FUNCTION(wrapper_name, function, ret_type, arg1_type, arg2_type, ...)

And I would like it to define a function like this:

ret_type wrapper_name(arg1_type arg1, arg2_type arg2, ...) {
    return function(arg1, arg2, ...)
}

Except where the ellipses (along with arg1 and arg2) are a dynamically sized list. I could create the function prototype using __VA_ARGS__, but that doesn't give me names for the arguments to pass to the wrapped function.

I assume the usual approach would be std::apply (or to copy-paste the implementation of std::apply, since it's not available in C++11). Unfortunately the argument function may be overloaded or a compiler built-in so it cannot reliably be passed as a function type.

What I have right now is the generation of a variadic template function:

template<class... Args>
ret_type wrapper_name(Args... args) {
    return function(std::forward<Args>(args)...);
}

But this causes a world of problems with conversions because there's a lot that cannot be done with implicit conversions in this form when the wrapped function is overloaded. It also can't wrap function-like macros, but I think addressing that problem may be impossible anyway.


Update: With a bit of fiddling I can make the wrapper function enforce its argument types this way:

#define WRAPPER(name, ret_type, function, ...) \
  template<class... Args> struct name##_impl { static inline \
  ret_type func(Args... args) { return function(std::forward<Args>(args)...); } }; \
  template<class... Args> ret_type name(Args... args) \
  { return name##_impl<__VA_ARGS__>::func(std::forward<Args>(args)...); }

Unfortunately, this leaves me with the problem that I cannot overload the identifier name with different argument lists. My attempts at making the name template a specialisation have resulted in a lot of complaints either about partial specialisation of a function or about not forwarding any template arguments to the generic template.

2

There are 2 best solutions below

0
Remy Lebeau On BEST ANSWER

Using a variadic macro to generate a non-template wrapper, you can use some macro tricks, specifically this trick, to generate your argument list with names so you can call your target function, eg:

// this example supports functions that have 0..3 arguments,
// you can expand this for more arguments if needed...

#define _ARG_PARAMS3(type1, type2, type3) type1 arg1, type2 arg2, type3 arg3
#define _ARG_NAMES3(type1, type2, type3) arg1, arg2, arg3

#define _ARG_PARAMS2(type1, type2) type1 arg1, type2 arg2
#define _ARG_NAMES2(type1, type2) arg1, arg2

#define _ARG_PARAMS1(type1) type1 arg1
#define _ARG_NAMES1(type1) arg1

#define _ARG_PARAMS0()
#define _ARG_NAMES0()

#define _GET_OVERRIDE(_1, _2, _3, _4, NAME, ...) NAME

#define ARG_PARAMS(...) _GET_OVERRIDE("ignored", __VA_ARGS__ __VA_OPT__(,) \
    _ARG_PARAMS3, _ARG_PARAMS2, _ARG_PARAMS1, _ARG_PARAMS0)(__VA_ARGS__)
    
#define ARG_NAMES(...) _GET_OVERRIDE("ignored", __VA_ARGS__ __VA_OPT__(,) \
    _ARG_NAMES3, _ARG_NAMES2, _ARG_NAMES1, _ARG_NAMES0)(__VA_ARGS__)

#define WRAP_FUNCTION(wrapper_name, function, ret_type, ...) \
ret_type wrapper_name(ARG_PARAMS(__VA_ARGS__)) { \
    return function(ARG_NAMES(__VA_ARGS__)); \
}

And then you can use WRAP_FUNCTION like you want:

int funcWith3Args(int arg1, int arg2, int arg3)
{
    ...
}

string funcWithNoArgs()
{
    ...
}

WRAP_FUNCTION(wrapper3Args, funcWith3Args, int, int, int, int)
WRAP_FUNCTION(wrapperNoArgs, funcWithNoArgs, string)

...

wrapper3Args(1, 2, 3);
wrapperNoArgs();

...

Online Demo

With some tweaking, I'm sure you can also generate a template wrapper, too.

0
sh1 On

It turns out it's possible to convert a list of function arguments into a template parameter pack by specialising a template which accepts a function signature:

#define WRAP(NAME, CALLEE, SIGNATURE) \
    using NAME##_sig = SIGNATURE; \
    template<class T> struct NAME##_impl; \
    template<class Ret, class... Args> \
    struct NAME##_impl<Ret(Args...)> { \
        Ret operator()(Args... args) const noexcept \
        { return CALLEE(std::forward<Args>(args)...); }; \
    }; \
    constexpr NAME##_impl<NAME##_sig> NAME;

void example(char *, size_t);
WRAP(test, example, void(char*, size_t))
void fn(char*x, size_t y) { test(x, y); }

So, if a list of macro arguments is what's preferred:

#define WRAP2(NAME, CALLEE, RET, ...) \
    using NAME##_sig = RET(__VA_ARGS__); \
    template<class T> struct NAME##_impl; \
    template<class Ret, class... Args> \
    struct NAME##_impl<Ret(Args...)> { \
        Ret operator()(Args... args) const noexcept \
        { return CALLEE(std::forward<Args>(args)...); }; \
    }; \
    constexpr NAME##_impl<NAME##_sig> NAME;

void example(char *, size_t);
WRAP(test, example, void, char*, size_t)
void fn(char*x, size_t y) { test(x, y); }

This makes it possible to define a functor with the desired signature, and also to address the complete list of arguments within the implementation as args... so that they can all be passed through to the function being wrapped.

Overloading of functors can be implemented by making them all ancestors of a common object:

#define WRAP(NAME, CALLEE, RET, ...) \
    using NAME##_sig = RET(__VA_ARGS__); \
    template<class T> struct NAME##_impl; \
    template<class Ret, class... Args> \
    struct NAME##_impl<Ret(Args...)> { \
        Ret operator()(Args... args) const noexcept \
        { return CALLEE(std::forward<Args>(args)...); }; \
    }; \
    using NAME = NAME##_impl<NAME##_sig>;


void examplea(char *, size_t);
void exampleb(size_t, char *);
WRAP(test_a, examplea, void, char*, size_t)
WRAP(test_b, exampleb, void, size_t, char*)

constexpr struct : test_a, test_b {} test;

void fn(char*x, size_t y) {
    test(x, y);
    test(y, x);
}

With thanks to Artyer for their comments here illustrating these techniques for me.