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.

Était-ce utile?

La 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.

Autres conseils

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 *>;
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top