20

Suppose there's a list of arguments stored somehow, in a array for example.

Given a function pointer, how could I make a call to it passing the stored list of arguments?

I'm not trying to pass the array as an argument ok. You got it, ok? I want to pass each of its elements as an argument. An array is just to illustrate, I could be storing the arguments in some tuple structure. Also, look that I have at hand a function pointer and may have a signature in string format. I'm not trying to just define a function that is able to deal with a variadic list.

The only way I see how to do that is by employing assembly (by __asm push et al.) or this:

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

I don't like this approach too much...

Is there another portable and shorter form?

Several script languages are able to call C functions.

How script languages like Python or Ruby do that? How they implement it in a portable way? Does they just use assembly for several platforms or the above in the end?

Look that I'm really not asking about details of parameter marshaling and other stuff from script languages to C, I'm interested only in how, in the end, internally, the call to the C function by the script language is built.

EDIT

I'll keep the question's title but I think a better way for asking it is:

How to call a C function with its pointer and signature available only at runtime?

UPDATE

From Foreign Interface for PLT Scheme:

A call-out is a normal function call. In a dynamic setting, we create a “call-interface” object which specifies (binary) input/output types; this object can be used with an arbitrary function pointer and an array of input values to perform a callout to the function and retrieve its result. Doing this requires manipulating the stack and knowing how a function is called, these are details that libffi deals with.

Thanks @AnttiHaapala for searching, finding and pointing libffi. It's what I was looking for, it's being used by a bunch of script languages, it's a portable library, implemented across several architectures and compilers.

Community
  • 1
  • 1
oblitum
  • 11,380
  • 6
  • 54
  • 120
  • @user1548637 i think he means calling the function with all the arguments stored in the array. something like `call_func(func_pointer, array_of_args);` will call `func_name(int arg1, float arg2, char *arg3)` – Vlad Balmos Sep 04 '12 at 12:49
  • 2
    Languages like Ruby and Python don't need to implement this feature in C, just expose it that way in the interpreted language. Under the hood in Ruby (at least the C-based MRI) arguments are passed as either an array of pointers to Ruby `VALUE`s or as an a single `VALUE` which is a Ruby array object. See here: https://github.com/ruby/ruby/blob/trunk/README.EXT#L317 I assume the implementation is similar in (C)Python. – John Ledbetter Sep 04 '12 at 12:56
  • You asked "what is the portable way to call", the correct answer is: there isn't. – Antti Haapala -- Слава Україні Sep 04 '12 at 13:12
  • @AnttiHaapala: the implementation in the question seems perfectly portable. Why would you say that? – Michael Foukarakis Sep 04 '12 at 13:21
  • I see that you have ruby tagged, even though the question is for C. If you are curious, ruby has the splat operator for arrays that turns them into arguments. If you have an array with 2 elements and a function that takes two arguments, you could say func(*a). – Stephen Garle Sep 04 '12 at 15:15
  • @StephenGarle Thanks. I know of other languages that can do that kind of thing. I've just tagged Ruby and Python to attract implementers hackers to answer for C, how it's done from the internals of its languages. – oblitum Sep 04 '12 at 15:24
  • The internals of Python and Ruby are *completely different* from that of C; it's not possible to make a direct comparison. – Ignacio Vazquez-Abrams Sep 04 '12 at 15:39
  • @IgnacioVazquez-Abrams I don't make any direct comparison of languages. – oblitum Sep 05 '12 at 03:16

4 Answers4

12

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.

  • The question suggests I can have the prototype in string format at runtime. – oblitum Sep 04 '12 at 14:44
  • Of course you can, in essence you only just need to know exactly what kind of code the C compiler would produce for the exact such platform for calling the function with the given format. However such code cannot not be portable as in that the C program would work on every future system that conforms to current C standards. – Antti Haapala -- Слава Україні Sep 04 '12 at 14:46
  • If it can't be done portable, so comes the following question. How script languages are doing that? – oblitum Sep 04 '12 at 14:48
  • Antti, thanks, I just haven't look at the python, ruby, whatever codebase. My question is just to ask for links, sample code, demonstrating what you are asserting, since there's no portable form. – oblitum Sep 04 '12 at 15:02
  • @Chico, you're pointing me towards `ctypes`, which is clearly a bunch of wrappers to load/use DLLs, and which certainly does it in a non-portable way. – Bruno Sep 04 '12 at 15:02
  • @Bruno Yes, it may be, but what you have talked about till now was just about making trivial calls to imported API. I'm not asking about how to make a trivial call at compilation time in C by just writing down the right call, with correct signature, in C source code. – oblitum Sep 04 '12 at 15:05
  • 1
    Then my suggestion is that you read ctypes module C source code :) – Antti Haapala -- Слава Україні Sep 04 '12 at 15:09
  • I'll probably do that, I just hope someone else beyond me comes here and demonstrate a bunch of cases. – oblitum Sep 04 '12 at 15:11
  • 1
    @Chico, ah fair enough, so you want to know how the likes of `ctypes`, SWIG (or others) implement their abstraction layers. What I'm not sure about is why you think this can be done in a portable way. All these tools have platform-specific implementations, and sometimes their abstractions can't be completely uniform. – Bruno Sep 04 '12 at 15:13
  • @Bruno I've given a sample that seems portable, could some implementation in some script language be using something like that with some limitations, like 30 arguments, a huge switch? It can. I've put all requeriments at the question. I'm looking for a "known" way, if there's one. I'm not only asking about the portable way. – oblitum Sep 04 '12 at 15:26
  • haha Antti, I think you didn't expect to find libffi today =) – oblitum Sep 04 '12 at 15:54
  • I think I can assert libffi is a portable way to do what I asked, contrary to your 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, we too can't call C portable. – oblitum Sep 04 '12 at 23:33
  • The sad part is that I almost got a deja vu with the libffi, I was almost certain today that ctypes had its own source code, damnit :( – Antti Haapala -- Слава Україні Sep 05 '12 at 06:58
  • But considering that in your question you say the choices are either call it in a switch-case, or call by asm, and libffi has asm wrappers, it is fair enough to say it cant be done portably in C (actually you can write all the assembler you want in C as char arrays too), adding that, "however you can use libffi". – Antti Haapala -- Слава Україні Sep 05 '12 at 07:01
8

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

Anthony Green
  • 643
  • 1
  • 4
  • 9
7

@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

Community
  • 1
  • 1
oblitum
  • 11,380
  • 6
  • 54
  • 120
2

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.

cdarke
  • 42,728
  • 8
  • 80
  • 84
  • It seems you are talking about calling script from C. I'm talking about the contrary, calling a C API from script languages, like employing ctypes in python. – oblitum Sep 04 '12 at 13:29
  • I tried to include both. A Python module written in C has the following prototype: `PyObject * func-name(PyObject *self, PyObject *args)`, but there is more to it than that. You have to define the calling convention as well. – cdarke Sep 04 '12 at 13:34
  • `ctypes` adds a wrapper to convert the Python objects into ordinary C variables, which will involve a `pack` operation on call and an `unpack` on return. – cdarke Sep 04 '12 at 13:36
  • Still, it doesn't answer how a call to a given C API is done. PACK/UNPACK each argument does not answer it, Python module prototypes doesn't too. – oblitum Sep 04 '12 at 13:52
  • 1
    @Chico: the call is not a direct one between the script and C, the interpreter executes the call. In Perl there is XSLoader, in Python the python runtime handles the call. I agree that `pack`/`unpack` is perhaps simplifying the procedure. – cdarke Sep 04 '12 at 14:13
  • The question is just that, how the interpreter executes the call, given that in the end it has at hand a bunch of arguments that it will use to call the C function. – oblitum Sep 04 '12 at 14:15
  • @Chico: AFAIK, those parameters lists are not flexible, but done via the wrappers, where the links to the interpreter are defined at compile time. If you want a flexible list, look into manual dynamic linking (not sure what you're trying to do exactly). – Bruno Sep 04 '12 at 14:25
  • 1
    @Chico: OK, having done its conversions it will pass pointers on the parameter stack, the pointers will be copied, but not the values. If the call is variadic then the first parameter will indicate the size and number of following parameters (think `printf`), which is why the C calling convention (cdecl) is right-to-left, so Top-Of-Stack (in the called function) refers to the first parameter passed. – cdarke Sep 04 '12 at 14:29
  • I know all that. Given the requirements I've given at the question, does this helps? no. – oblitum Sep 04 '12 at 14:40