在C中,给定一个变量参数列表,如何使用它们构建函数调用?

pep*_*ico 16 c assembly ffi libffi

假设有一个以某种方式存储的参数列表,例如,在数组中.

给定一个函数指针,我如何调用它来传递存储的参数列表?

我不是试图将数组作为参数传递好.你明白了,好吗?我想将每个元素作为参数传递.一个数组只是为了说明,我可以将参数存储在一些元组结构中.另外,看看我手头有一个函数指针,并且可能有字符串格式的签名.我不是只想定义一个能够处理可变列表的函数.

我看到如何做到这一点的唯一方法是使用汇编(由__asm push等人)或这个:

void (*f)(...);

int main()
{
    f = <some function pointer>;
    int args[]; <stored in a array, just to illustrate>
    int num_args = <some value>;

    switch(num_args)
    {
        case 0:
            f();
        break;

        case 1:
            f(args[0]);
        break;

        case 2:
            f(args[0], args[1]);
        break;

        /* etc */
    }

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我不太喜欢这种做法......

还有另一种便携式和更短的形式吗?

有几种脚本语言可以调用C函数.

像Python或Ruby这样的脚本语言是如何做到的?他们如何以便携方式实现它?他们最近只是为几个平台或上面的平台使用组件吗?

看看我真的不是在询问参数封送的细节以及从脚本语言到C的其他内容,我只对内部如何构建脚本语言对C函数的调用感兴趣.

编辑

我将保留问题的标题,但我认为更好的方式是:

如何调用C函数,其指针和签名仅在运行时可用?

UPDATE

来自外国接口的PLT计划:

呼出是正常的函数调用.在动态设置中,我们创建一个"调用接口"对象,它指定(二进制)输入/输出类型; 此对象可以与任意函数指针和输入值数组一起使用,以执行函数的调用并检索其结果.这样做需要操纵堆栈并知道如何调用函数,这些是libffi处理的细节.

感谢@AnttiHaapala搜索,查找和指向libffi.这就是我所寻找的,它被一堆脚本语言所使用,它是一个可移植的库,在多个架构和编译器中实现.

Ant*_*ala 12

您询问使用给定数量的参数调用任何函数指针的可移植方式是什么.正确的答案是没有这样的方法.

例如,python能够通过ctypes模块调用C函数,但只要您知道确切的原型和调用约定,它就是可移植的.在C中,实现相同的最简单方法是在编译时知道函数指针的原型.

更新

对于python/ctypes示例,在每个启用了ctypes模块的平台上,python都知道如何为给定的参数集编写调用堆栈.例如,在Windows上,python知道2个标准调用约定 - 在堆栈上具有C顺序参数的cdecl,以及具有"pascal样式排序"的stdcall.在Linux上,它确实需要担心是否调用32或64位共享对象,等等.如果python被编译到另一个平台,那么ctypes也需要改变; ctypes模块中的C代码本身不是可移植的.

更新2

对于Python,魔术在这里:ctypes源代码.值得注意的是,它似乎链接了http://sourceware.org/libffi/,这可能正是您所需要的.


pep*_*ico 6

@AnttiHaapala指出了libffi.以下是有关它的一些信息:

什么是libffi?

某些程序在编译时可能不知道要将哪些参数传递给函数.例如,可以在运行时告知解释器关于用于调用给定函数的参数的数量和类型.'libffi'可用于此类程序,以提供从解释程序到编译代码的桥梁.

'libffi'库为各种调用约定提供了可移植的高级编程接口.这允许程序员在运行时调用由调用接口描述指定的任何函数.

FFI代表外部功能接口.外部函数接口是接口的流行名称,它允许用一种语言编写的代码调用用另一种语言编写的代码.'libffi'库实际上只提供功能齐全的外部函数接口的最低机器相关层.一个层必须存在于'libffi'之上,它处理两种语言之间传递的值的类型转换.

'libffi'假定您有一个指向要调用的函数的指针,并且您知道要传递它的参数的数量和类型,以及函数的返回类型.


历史背景

libffi最初由Anthony Green(SO用户:anthony-green)开发,受到Silicon Graphics的Gencall库的启发.Gencall由Gianni Mariani开发,然后由SGI雇用,目的是允许通过地址调用函数并为特定调用约定创建调用框架.安东尼格林改进了这个想法并将其扩展到其他架构和调用约定以及开源libffi.


用libffi调用pow

#include <stdio.h>
#include <math.h>
#include <ffi.h>

int main()
{
  ffi_cif     call_interface;
  ffi_type    *ret_type;
  ffi_type    *arg_types[2];

  /* pow signature */
  ret_type = &ffi_type_double;
  arg_types[0] = &ffi_type_double;
  arg_types[1] = &ffi_type_double;

  /* prepare pow function call interface */
  if (ffi_prep_cif(&call_interface, FFI_DEFAULT_ABI, 2, ret_type, arg_types) == FFI_OK)
  {
    void *arg_values[2];
    double x, y, z;

    /* z stores the return */
    z = 0;

    /* arg_values elements point to actual arguments */
    arg_values[0] = &x;
    arg_values[1] = &y;

    x = 2;
    y = 3;

    /* call pow */
    ffi_call(&call_interface, FFI_FN(pow), &z, arg_values);

    /* 2^3=8 */
    printf("%.0f^%.0f=%.0f\n", x, y, z);
  }

  return 0;
}
Run Code Online (Sandbox Code Playgroud)

我认为我可以断言libffi是一种可行的方式来做我所要求的,这与Antti Haapala断言没有这样的方式相反.如果我们不能将libffi称为便携式技术,考虑到它在编译器和体系结构中的移植/实现程度,以及哪个接口符合C标准,我们也无法调用C或其他任何可移植的技术.

提取的信息和历史:

https://github.com/atgreen/libffi/blob/master/doc/libffi.info

http://en.wikipedia.org/wiki/Libffi


Ant*_*een 6

我是libffi的作者.它会做你要求的.