使用varargs参数调用C ++泛型函数

时间:2017-05-04 15:25:44

标签: c++ generics typedef

在我的项目中,我有不同类型的输入参数数量不同的函数。由于这些函数是库的一部分,我无法更改它们的定义或主体。

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或包装函数?

4 个答案:

答案 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}}。

Try it online!

答案 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解析为参数并使用...

调用原始函数