在我的项目中,我有不同类型的输入参数数量不同的函数。由于这些函数是库的一部分,我无法更改它们的定义或主体。
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){
...
}
在我的项目中,我需要一个方法,它将接收其中一个函数的地址。此外,它接收格式良好的参数列表(适合第一个参数中的函数)。然后,此方法必须使用传递的参数调用传递的函数。
这就是我现在所拥有的:(我已经简化了一些代码以使我的想法变得清晰)
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 );
}
我的问题是实际的函数调用。函数调用中的参数列表应该是通用的,并且应该能够包含不同数量的参数,遗憾的是我不知道该怎么做...我尝试过像这样传递varargs列表:
((void (*)( va_list )) some_func_address)( listPointer);
但这会扰乱被调用函数中的参数...
所以我的问题是:有没有办法用通用方式调用给定参数的给定函数?也许我需要某种typedeff或包装函数?
答案 0 :(得分:2)
如果您还没有std::invoke
,请使用variadic templates。要很好地处理void
函数,请使用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...);
}
示例:
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);
}
不要忘记#include <type_traits>
和std::enable_if
的{{1}}。
答案 1 :(得分:1)
va_args对我来说仍然有些黑魔法,但我相信va_start的第二个arg应该是被调用函数的第一个arg。我不明白你的&#34; clazz&#34;是。我相信你应该把va_start称为:
va_start( listpointer, some_func_address );
而不是:
va_start( listPointer, clazz );
答案 2 :(得分:1)
这会帮助你吗?
#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;
}
我个人更愿意将表示函数的枚举设置为拦截器函数而不是void *指针并使用switch / case。
如果你可以让拦截器成为模板函数,它会变得更加容易(完全放弃call
模板函数):
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;
}
答案 3 :(得分:1)
现在来自你的其他question,这是怎么回事:
(附注:引用的问题告诉(在评论中)void *指针来自某些自定义地图,所以不应该 - 据我所知 - 任何替换它们的问题适当的指针/类 - 我将要做的......)
#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;
}
这通过多态解决了这个问题:一个函数包装器类模板类(我们需要一个非模板基类,能够将所有模板实例放入一个数组或一个映射中;这就是你原来的 - 但实际上是非法的 - void *指针用于),将va_list解析为参数并使用...
调用原始函数