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.