Question

I'm writing a library that uses Function Hooking to inspect the arguments sent from 3rd-party code to a 3rd-party library. The caller and callee are both written in C, and the hooking is accomplished by providing the loader with an array of pairs of function pointers.

Initially, I wrote this in C and duplicated a lot of code for each function hook. After writing a handful of hooks, I looked to C++ templates to lighten the load.

The following example works with a fixed number of template arguments and demonstrates the idea:

#include <cstdio>
#include <iostream>

typedef int (*PrintCallback)(const char *);

int PrintSingle(const char *input)
{
    return printf("%s", input);
}

template<typename Result,
         typename Arg,
         Result(*callback)(Arg)>
Result WrapFunc(Arg arg)
{
    std::cout << "Logging logic goes here..." << std::endl;
    return callback(std::forward<Arg>(arg));
}

int main(int argc, char *argv[])
{
    PrintCallback pc = WrapFunc<int, const char *, PrintSingle>;
    pc("Hello, World!\n");
    return 0;
}

Outputs:

Logging logic goes here...
Hello, World!

My trouble comes when I try to use C++11's Variadic Templates to generalize this solution to a variable number of template arguments:

template<typename Result,
         typename... Args,
         Result(*callback)(Args...)>
Result WrapFunc(Args... args)
{
    std::cout << "Logging logic goes here..." << std::endl;
    return callback(std::forward<Args>(args)...);
}

When I try to instantiate this template, it's not clear whether the last template argument is the "callback" value or a part of "Args". My compiler returns this error message:

variadic.cpp:22:21: error: address of overloaded function 'WrapFunc' does not
      match required type 'int (const char *)'
        PrintCallback pc = WrapFunc<int,const char *, PrintSingle>;
                           ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
variadic.cpp:14:8: note: candidate template ignored: invalid
      explicitly-specified argument for template parameter 'Args'
Result WrapFunc(Args... args)

However, I can't swap the order of "Args" and "callback" in the template because the callback's type is dependent on the definition of Args.

Is there a way to do this properly, so that I can provide a table of C function pointers to the loader? Solutions that rely on returning std::functions at run-time are off the table, since the calling code won't be able to use them.

Was it helpful?

Solution

The problem here is that you can't really put more parameters after a variadic parameter pack: they try to greedily eat everything. You can often work around this by introducing a layer of indirection, template-style:

template <typename Result, typename...Args>
struct Wrap {
    template <Result(*callback)(Args...)>
    static Result Func(Args...args) {
        std::cout << "Logging logic goes here...\n";
        return callback(std::forward<Args>(args)...);
    }
};

which requires a slight modification of your usage syntax to:

PrintCallback pc = Wrap<int, const char *>::Func<PrintSingle>;

See it live at Coliru.

OTHER TIPS

You may use: (https://ideone.com/OaNtMz)

template<typename F, F f, typename... Args>
auto WrapFunc(Args... args) -> decltype(f(std::forward<Args>(args)...))
{
    std::cout << "Logging logic goes here..." << std::endl;
    return f(std::forward<Args>(args)...);
}

And then:

PrintCallback pc = &WrapFunc<decltype(&PrintSingle), &PrintSingle, const char *>;
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top