Reputation: 555
In my project i have functions with different number of input parameters with different types. Since these functions are parts of libraries, I cannot change their definitions or bodies.
void methodA(boolean p1, int p2, long p3){
... some unrelevant code here ...
}
void methodB(int p1, int p2, int p3, long p4){
... some unrelevant code here too ...
}
int methodC(long p4){
...
}
In my project i need to have a method, which would receive the address of one of these functions. Additionally it receives well-formed list of parameters (which fits the function in the first parameter). Then this method has to call the passed function with the passed parameters.
Here is what I have now: (i have simplified the code a bit to make my idea clear)
void intercaller(void* some_func_address, ...){
// VARARGS parameters extractor
va_list listPointer;
va_start( listPointer, some_func_address );
int p1 = va_arg( listPointer, int );
int p2 = va_arg( listPointer, int );
int p3 = va_arg( listPointer, int );
long p4 = va_arg( listPointer, long );
// TODO: THIS IS NOT GENERIC CALL , CANN ONLY CALL METHOD B
((void (*)( int , int , int , long )) some_func_address)( p1 , p2 , p3 , p4 );
va_end( listPointer );
}
My problem is the actual function call. The parameter list in the function call should be generic and should be able to include different number of parameters, sadly i dont know how to do that... I have tried passing varargs list like here:
((void (*)( va_list )) some_func_address)( listPointer);
but this messes up the parameters in the called function...
So my question is: is there a way to call a given function with given parameters in a generic manner? Maybe I need some sort of a typedeff or a wrapper function?
Upvotes: 4
Views: 2466
Reputation: 25526
Now coming from your other question, what about this:
(Side note: referenced question tells (in the comments) the void* pointers are coming from some custom map, so there shouldn't be – as far as I can see – any issue with replacing them by other appropriate pointers/classes – which I am going to do...)
#include <stdarg.h>
class FunctionWrapper
{
public:
virtual ~FunctionWrapper() { }
virtual void operator()(va_list&) = 0;
};
template<typename Result, typename ... Parameters>
class FWrapper : public FunctionWrapper
{
Result (*mFunction)(Parameters...);
template <typename T>
T extract(va_list& list)
{
return va_arg(list, T);
}
public:
FWrapper(Result (*function)(Parameters...))
: mFunction(function)
{ }
virtual void operator()(va_list& list)
{
static_cast<void>(mFunction(extract<Parameters>(list)...));
}
};
// facilitates creating the wrappers:
template<typename Result, typename ... Parameters>
FunctionWrapper* createWrapper(Result (*function)(Parameters...))
{
return new FWrapper<Result, Parameters ...>(function);
}
void f1(int x, int y)
{
std::cout << x << ' ' << y << std::endl;
}
void f2(double x, double y)
{
std::cout << x << ' ' << y << std::endl;
}
// e. g.:
FunctionWrapper* gWrappers[] = { createWrapper(&f1), createWrapper(&f2) };
// from your other question: you'd fill the wrappers into the map you mentioned there:
// map[whatever] = createWrapper(&function);
void interceptor(FunctionWrapper* wrapper, ...)
{
va_list list;
va_start(list, wrapper);
(*wrapper)(list);
va_end(list);
}
int main(int argc, char* argv[])
{
interceptor(gWrappers[0], 7, 7);
interceptor(gWrappers[1], 10.12, 12.10);
return 0;
}
This solves the issue via polymorphism: A function wrapper class template class (we need a non-template base class to be able to place all the template instances into an array or a map; this is what your original – but actually illegal – void* pointer served for), resolving the va_list into arguments and calling the original function with...
Upvotes: 1
Reputation: 25526
Would this help you out?
#include <stdarg.h>
template <typename T>
T extract(va_list& list)
{
return va_arg(list, T);
}
template<typename Result, typename ... Parameters>
Result call(Result(*function)(Parameters...), va_list& list)
{
return function(extract<Parameters>(list)...);
}
void f1(int x, int y)
{
std::cout << x << ' ' << y << std::endl;
}
void f2(double x, double y)
{
std::cout << x << ' ' << y << std::endl;
}
void interceptor(void* f, ...)
{
va_list list;
va_start(list, f);
if(f == &f1)
{
call(f1, list);
}
else if(f == f2)
{
call(f2, list);
}
va_end(list);
}
int main(int argc, char* argv[])
{
interceptor((void*)&f1, 7, 7);
interceptor((void*)&f2, 10.12, 12.10);
return 0;
}
I personally would yet prefer pasing an enum representing the functions to the interceptor function instead of the void* pointer and using switch/case inside.
If you can make the interceptor a template function, it gets even much easier (drop the call
template function entirely):
template<typename Result, typename ... Parameters>
void interceptor(Result(*function)(Parameters...), ...)
{
va_list list;
va_start(list, function);
function(extract<Parameters>(list)...);
va_end(list);
}
int main(int argc, char* argv[])
{
interceptor(&f1, 7, 7);
interceptor(&f2, 10.12, 12.10);
return 0;
}
Upvotes: 1
Reputation: 5729
If you don't have std::invoke
yet, use variadic templates. To treat void
functions nicely, use SFINAE.
template<typename R, typename... Args>
auto call(R(*function)(Args...), Args... args) -> typename std::enable_if<!std::is_same<R, void>::value, R>::type {
return function(args...);
}
template<typename... Args>
void call(void (*function)(Args...), Args... args) {
function(args...);
}
Example:
void a() {
std::cout << 'a';
}
void b(int a) {
std::cout << "b:" << a;
}
int c(int a) {
return a;
}
int main() {
call(a);
call(b, 1);
std::cout << "c:" << call(c, 2);
}
Don't forget to #include <type_traits>
for std::enable_if
and std::is_same
.
Upvotes: 3
Reputation: 2406
va_args are still somewhat black magic to me, but I believe the second arg to va_start should be the first arg to the called function. I don't understand what your "clazz" is. I believe you you should call va_start as:
va_start( listpointer, some_func_address );
instead of:
va_start( listPointer, clazz );
Upvotes: 1