En C, dada una lista variable de argumentos, ¿cómo crear una llamada a función usándolos?

Supongamos que hay una lista de argumentos almacenados de alguna manera, en una matriz, por ejemplo.

Dado un puntero de función , ¿cómo podría hacer una llamada pasando la lista de argumentos almacenados?

No estoy tratando de pasar la matriz como un argumento bien. Lo tienes, ¿está bien? Quiero pasar cada uno de sus elementos como un argumento. Una matriz es solo para ilustrar, podría estar almacenando los argumentos en alguna estructura de tupla. Además, observe que tengo a mano un puntero a la función y puede tener una firma en formato de cadena . No estoy tratando de definir una función que pueda tratar con una lista variadic.

La única forma en que veo cómo hacerlo es mediante el uso de ensamblaje (por __asm push et al.) O esto:

 void (*f)(...); int main() { f = ; int args[];  int num_args = ; switch(num_args) { case 0: f(); break; case 1: f(args[0]); break; case 2: f(args[0], args[1]); break; /* etc */ } return 0; } 

No me gusta demasiado este enfoque …

¿Hay otra forma portátil y más corta?

Varios lenguajes de guiones pueden llamar a las funciones de C.

¿Cómo hacen eso los lenguajes de script como Python o Ruby? ¿Cómo lo implementan de una manera portátil? ¿Simplemente usan ensamblaje para varias plataformas o lo anterior al final?

Fíjate que realmente no estoy preguntando sobre los detalles del cálculo de parámetros y otras cosas desde los lenguajes de script a C, solo me interesa cómo, al final, internamente, se construye la llamada a la función C por el lenguaje de script.

EDITAR

Guardaré el título de la pregunta, pero creo que una mejor manera de preguntar es:

¿Cómo llamar a una función C con su puntero y firma disponibles solo en tiempo de ejecución?

ACTUALIZAR

Desde Foreign Interface for PLT Scheme :

Una llamada es una llamada a función normal. En una configuración dinámica, creamos un objeto “call-interface” que especifica tipos (binarios) de entrada / salida; este objeto se puede usar con un puntero de función arbitrario y una matriz de valores de entrada para realizar una llamada a la función y recuperar su resultado. Hacer esto requiere manipular la stack y saber cómo se llama a una función, estos son los detalles que trata libffi .

Gracias @AnttiHaapala por buscar, encontrar y señalar libffi . Es lo que estaba buscando, está siendo utilizado por un conjunto de lenguajes de script, es una biblioteca portátil, implementada en varias architectures y comstackdores.

Soy el autor de libffi. Hará lo que estás preguntando.

Usted preguntó cuál es la forma portátil de llamar a cualquier puntero de función con una determinada cantidad de argumentos. La respuesta correcta es que no hay tal camino .

Por ejemplo, python puede llamar a las funciones C a través del módulo ctypes, pero esto es portátil solo mientras conozcas el prototipo exacto y las convenciones de llamada. En C, la manera más fácil de lograr lo mismo es conocer el prototipo del puntero a la función en tiempo de comstackción.

Actualizar

Para el ejemplo python / ctypes, en cada plataforma que tiene habilitado el módulo ctypes, python sabe cómo escribir la stack de llamadas para un conjunto dado de argumentos. En Windows, por ejemplo, python sabe de 2 convenciones de llamadas estándar: cdecl con el orden C de los parámetros en la stack, y stdcall con “orden de estilo pascal”. En Linux, necesita preocuparse acerca de si llamar objetos compartidos de 32 o 64 bits, y así sucesivamente. Si python se comstack en otra plataforma, los tipos también necesitan cambios; el código C en el módulo ctypes no es, como tal, portátil.

Actualización 2

Para Python, la magia está aquí: código fuente de ctypes . Cabe destacar que parece vincular http://sourceware.org/libffi/ que podría ser justo lo que necesitabas.

@AnttiHaapala señaló libffi. Aquí hay información sobre esto:

¿Qué es libffi?

Algunos progtwigs pueden no saber en el momento de la comstackción qué argumentos se deben pasar a una función. Por ejemplo, a un intérprete se le puede decir en el tiempo de ejecución sobre el número y los tipos de argumentos utilizados para llamar a una función dada. ‘libffi’ se puede usar en dichos progtwigs para proporcionar un puente desde el progtwig de intérprete hasta el código comstackdo.

La biblioteca ‘libffi’ proporciona una interfaz de progtwigción portátil de alto nivel para varias convenciones de llamadas. Esto permite que un progtwigdor llame a cualquier función especificada por una descripción de interfaz de llamada en tiempo de ejecución.

FFI significa Foreign Function Interface. Una interfaz de función extranjera es el nombre popular de la interfaz que permite que el código escrito en un idioma llame al código escrito en otro idioma. La biblioteca ‘libffi’ realmente solo proporciona la capa más baja dependiente de la máquina de una interfaz de función extranjera con todas las características. Debe existir una capa encima de ‘libffi’ que maneja las conversiones de tipo para los valores pasados ​​entre los dos idiomas.

‘libffi’ asume que tiene un puntero a la función que desea llamar y que conoce el número y los tipos de argumentos para pasarlo, así como el tipo de retorno de la función.


Antecedentes históricos

libffi, originalmente desarrollado por Anthony Green (usuario de SO: anthony-green ), se inspiró en la biblioteca Gencall de Silicon Graphics. Gencall fue desarrollado por Gianni Mariani, entonces empleado por SGI, con el propósito de permitir llamadas a funciones por dirección y crear un marco de llamada para la convención de llamadas particular. Anthony Green refinó la idea y la extendió a otras architectures y convenciones de llamadas y libfi de abastecimiento abierto.


Llamando pow con libffi

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

Creo que puedo afirmar que libffi es una manera portátil de hacer lo que le pedí, contrariamente a la afirmación de Antti Haapala de que no existe tal manera. Si no podemos llamar a libffi una tecnología portátil, dado lo lejos que es portado / implementado a través de comstackdores y architectures, y qué interfaz cumple con el estándar C, tampoco podemos llamar a C, ni a nada, portátil.

Información e historial extraídos de:

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

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

Por seguridad, debe descomprimir las variables antes de enviarlas. El uso de ensamblador para hackear la stack de parámetros puede no ser portátil entre los comstackdores. Las convenciones de llamada pueden variar.

No puedo hablar por Ruby, pero he escrito bastantes progtwigs usando las interfaces C para Perl y Python. Las variables de Perl y Python no son directamente comparables con las variables C, tienen muchas más características. Por ejemplo, un escalar de Perl podría tener cadenas dobles y valores numéricos, de los cuales solo uno es válido en cualquier momento.

La conversión entre las variables Perl / Python y C se realiza usando pack y unpack (en el módulo struct en Python). En la interfaz C, debe llamar a API específicas para realizar la conversión, según el tipo. Por lo tanto, no es solo una transferencia de puntero recto, y ciertamente no involucra ensamblador.