在不知道参数数量的情况下调用函数

时间:2012-03-26 03:10:58

标签: c++ dynamic dll

假设我有一个带有2个函数的dll。dll =“dll1”的名称 f1(int a,int b,int c);

f2(int a);

我的程序会将函数名称,dll名称和参数“列表”作为输入。 我将如何使用适当的参数调用适当的函数。 即, 如果输入是 DLL1 F1 列表(5,8,9)

这需要我用3个参数调用f1 如果输入是 DLL1 F2 列表(8) 它需要我用一个参数调用f2 如何在不事先知道参数数量的情况下调用函数。

进一步澄清: 如何编写将调用 any 的代码 通过动态构建参数列表来运行其所有参数 使用其他一些信息来源

4 个答案:

答案 0 :(得分:3)

由于生成的代码根据参数的数量而有所不同,因此您有两种选择:您可以使用汇编语言编写一些代码来完成工作(基本上遍历参数列表并在调用函数之前将每个代码推送到堆栈上) ,或者您可以创建类似指向函数的指针数组,对于您关心的每个参数数量(例如,0到10)。大多数人发现后者更容易处理(如果只是因为它完全避免使用汇编语言)。

答案 1 :(得分:2)

要解决这个问题,你需要知道:

  1. 调用约定(那些 stdcall cdecl fastcall thiscall (顺便说一句,后两者可以在MSVC ++)等中组合起来,它们控制函数如何接收它们的参数(例如,在特殊寄存器中,在堆栈上,两者中),它们如何返回值(相同)以及它们被允许丢弃的内容(例如某些寄存器)。
  2. 确切的功能原型。
  3. 您只能在编译器生成的符号/调试信息中找到所有这些信息,并且(可能在较小程度上)包含DLL中函数原型的头文件。头文件有一个问题。如果它没有指定调用约定并且函数已经使用非默认调用约定(通过编译器选项)进行编译,那么您可能会有歧义处理。在任何一种情况下,你都需要解析一些东西。

    如果您没有此信息,则剩下的唯一选择是对DLL和/或其用户进行逆向工程。

    为了正确调用任意函数,只知道它的原型并在运行时调用约定,你需要构造类似于编译器在编译时调用此函数时生成的代码。如果您正在解决一般问题,那么您需要一些汇编代码,不一定是手写的,运行时生成的机器代码是一个不错的选择。

    最后但并非最不重要的是,您需要一些代码来生成参数值。这对于数字类型(整数,浮点数等)和它们的数组来说是最微不足道的,而且对于结构,联合和类来说最困难。动态创建后者可能至少与正确调用函数一样困难。不要忘记他们可以使用指针和引用来引用其他对象。

    一般问题是可以解决的,但并不便宜。解决一些简单的特定情况要容易得多,并且可以通过重写函数以获得较少变量的参数和只有一个调用约定或通过编写包装函数来完全避免整个问题。

答案 2 :(得分:0)

如果您的客户在编译时知道,那么可以这样包装:

template<class Args...>
void CallFunctionPointer(void* pf, Args&&... args)
{
    typedef void(*FunctionType)(Args...);
    FunctionType* pf2 = (FunctionType*) pf;
    (*pf2)(forward<Args>(args)...);
}

注意,如果您传递了错误数量的参数或错误的参数类型,则行为未定义。

<强>背景

在C / C ++中,您可以将函数指针强制转换为您想要的任何签名,但如果错误则行为未定义。

在您的情况下,您提到了两个签名:

void (*)(int)

void (*)(int, int, int)

从DLL加载函数时,您有责任确保在调用之前将其转换为正确的签名,并使用正确的参数数量和类型。

如果您可以控制这些函数的设计,我会修改它们以获取可变数量的参数。它的基类型总是int,而不仅仅是将所有函数的签名更改为:

void (*)(int* begin, size_t n);
// begin points to an array of int of n elements

这样您就可以安全地将任何函数绑定到任意数量的参数。

答案 3 :(得分:0)

您可能需要查看Named Parameter Idiom

它使用method chaining来基本达到你想要的效果。

它解决了一个问题,你知道什么是默认的参数集,但你只需要自定义其中一些,而不一定按照它们的声明顺序。