Reputation: 800
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.
Upvotes: 1
Views: 244
Reputation: 217663
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 *>;
Upvotes: 1
Reputation: 42554
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>;
Upvotes: 1