In C, given a variable list of arguments, how to build a function call using them?

余生颓废 提交于 2019-11-26 19:03:51

I am the author of libffi. It will do what you are asking.

You asked what is the portable way to call any function pointer with given number of arguments. The correct answer is that there is no such way.

For example python is able to call C functions through the ctypes module, but this is portable only for as long as you know the exact prototype and calling conventions. In C the easiest way to achieve the same is to know the prototype of the function pointer at compile time.

Update

For python / ctypes example, on each platform that has the ctypes module enabled, python knows how to write the calling stack for a given set of arguments. On Windows for example, python knows of 2 standard calling conventions - cdecl with C order of parameters on stack, and stdcall with "pascal style ordering". On Linux it does need to worry about whether to call 32 or 64 bit shared objects, and so forth. If python is compiled to another platform, the ctypes needs changes as well; the C code in ctypes module is not, as such, portable.

Update 2

For Python the magic is in here: ctypes source code. Notably it seems to link http://sourceware.org/libffi/ which might be just what you needed.

pepper_chico

@AnttiHaapala pointed out libffi. Here's some information about it:

What is libffi?

Some programs may not know at the time of compilation what arguments are to be passed to a function. For instance, an interpreter may be told at run-time about the number and types of arguments used to call a given function. ‘libffi’ can be used in such programs to provide a bridge from the interpreter program to compiled code.

The ‘libffi’ library provides a portable, high level programming interface to various calling conventions. This allows a programmer to call any function specified by a call interface description at run time.

FFI stands for Foreign Function Interface. A foreign function interface is the popular name for the interface that allows code written in one language to call code written in another language. The ‘libffi’ library really only provides the lowest, machine dependent layer of a fully featured foreign function interface. A layer must exist above ‘libffi’ that handles type conversions for values passed between the two languages.

‘libffi’ assumes that you have a pointer to the function you wish to call and that you know the number and types of arguments to pass it, as well as the return type of the function.


Historic background

libffi, originally developed by Anthony Green (SO user: anthony-green), was inspired by the Gencall library from Silicon Graphics. Gencall was developed by Gianni Mariani, then employed by SGI, for the purpose of allowing calls to functions by address and creating a call frame for the particular calling convention. Anthony Green refined the idea and extended it to other architectures and calling conventions and open sourcing libffi.


Calling pow with libffi

#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;
}

I think I can assert libffi is a portable way to do what I asked, contrary to Antti Haapala's assertion that there isn't such a way. If we can't call libffi a portable technology, given how far it's ported/implemented across compilers and architectures, and which interface complies with C standard, we too can't call C, or anything, portable.

Information and history extracted from:

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

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

For safety you should unpack the variables before they are sent. Using assembler to hack the parameter stack might not be portable between compilers. Calling conventions might vary.

I can't speak for Ruby, but I have written quite a few programs using the C interfaces to Perl and Python. Perl and Python variables are not directly comparible with C variables, they have many more features. For example, a Perl scalar might have dual string and numeric values, only one of which is valid at any one time.

Conversion between Perl/Python variables and C is done using pack and unpack (in the struct module in Python). At the C interface you have to call specific APIs to do the conversion, depending on type. So, it is not just a straight pointer transfer, and it certainly does not involve assembler.

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!