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

4

19

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.

Bair answered 4/9, 2012 at 12:43 Comment(8)
@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)Tabard
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 VALUEs or as an a single VALUE which is a Ruby array object. See here: github.com/ruby/ruby/blob/trunk/README.EXT#L317 I assume the implementation is similar in (C)Python.Labaw
You asked "what is the portable way to call", the correct answer is: there isn't.Almeida
@AnttiHaapala: the implementation in the question seems perfectly portable. Why would you say that?Torey
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).Farica
@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.Interurban
The internals of Python and Ruby are completely different from that of C; it's not possible to make a direct comparison.Lakeesha
@IgnacioVazquez-Abrams I don't make any direct comparison of languages.Interurban
S
7

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

Somewhere answered 4/9, 2012 at 12:43 Comment(0)
G
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.

Gotama answered 4/9, 2012 at 14:43 Comment(14)
The question suggests I can have the prototype in string format at runtime.Interurban
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.Almeida
If it can't be done portable, so comes the following question. How script languages are doing that?Interurban
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.Interurban
@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.Cami
@Cami 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.Interurban
Then my suggestion is that you read ctypes module C source code :)Almeida
I'll probably do that, I just hope someone else beyond me comes here and demonstrate a bunch of cases.Interurban
@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.Cami
@Cami 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.Interurban
haha Antti, I think you didn't expect to find libffi today =)Interurban
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.Interurban
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 :(Almeida
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".Almeida
I
8

@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

Ironworks answered 4/9, 2012 at 12:43 Comment(0)
S
7

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

Somewhere answered 4/9, 2012 at 12:43 Comment(0)
P
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.

Prokopyevsk answered 4/9, 2012 at 13:26 Comment(9)
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.Interurban
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.Prokopyevsk
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.Prokopyevsk
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.Interurban
@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.Prokopyevsk
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.Interurban
@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).Cami
@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.Prokopyevsk
I know all that. Given the requirements I've given at the question, does this helps? no.Interurban

© 2022 - 2024 — McMap. All rights reserved.