Funciones anónimas usando expresiones de statement GCC

Esta pregunta no es terriblemente específica; es realmente para mi propio enriquecimiento en C y espero que otros también lo puedan encontrar útil.

Descargo de responsabilidad: Sé que muchos tendrán el impulso de responder con “si estás tratando de hacer FP, entonces simplemente usa un lenguaje funcional”. Trabajo en un entorno incrustado que necesita vincularse a muchas otras bibliotecas C, y no tiene mucho espacio para muchas bibliotecas compartidas más grandes y no admite muchos tiempos de ejecución de idiomas. Además, la asignación de memoria dinámica está fuera de discusión. También soy realmente curioso.

Muchos de nosotros hemos visto esta ingeniosa macro C para expresiones lambda:

#define lambda(return_type, function_body) \ ({ \ return_type __fn__ function_body \ __fn__; \ }) 

Y un ejemplo de uso es:

 int (*max)(int, int) = lambda (int, (int x, int y) { return x > y ? x : y; }); max(4, 5); // Example 

Usando gcc -std=c89 -E test.c , el lambda se expande a:

 int (*max)(int, int) = ({ int __fn__ (int x, int y) { return x > y ? x : y; } __fn__; }); 

Entonces, estas son mis preguntas:

  1. ¿Qué hace exactamente la línea int (* X)? ¿declarar? Por supuesto, int * X; es un puntero a un número entero, pero ¿cómo difieren estos dos?

  2. Echando un vistazo a la macro exagerada, ¿qué demonios hace el último __fn__ ? Si escribo una prueba test void test() { printf("hello"); } test; void test() { printf("hello"); } test; – que inmediatamente arroja un error. No entiendo esa syntax.

  3. ¿Qué significa esto para la depuración? (Estoy planeando experimentar con esto y con gdb, pero las experiencias u opiniones de otros serían geniales). ¿Esto arruinaría los analizadores estáticos?

Esta statement (en el scope del bloque):

 int (*max)(int, int) = ({ int __fn__ (int x, int y) { return x > y ? x : y; } __fn__; }); 

no es C pero es válido GNU C.

Hace uso de dos extensiones gcc :

  1. funciones anidadas
  2. expresiones de statement

Ambas funciones anidadas (que definen una función dentro de un enunciado compuesto) y las expresiones de enunciado ( ({}) , básicamente un bloque que produce un valor) no están permitidas en C y proceden de GNU C.

En una expresión de statement, la última statement de expresión es el valor de la construcción. Esta es la razón por la cual la función anidada __fn__ aparece como una statement de expresión al final de la expresión de la statement. Un designador de función ( __fn__ en la última statement de expresión) en una expresión se convierte en un puntero a una función por las conversiones habituales. Este es el valor utilizado para inicializar la función puntero max .

Su macro lambda explota dos características funky. Primero utiliza funciones anidadas para definir realmente el cuerpo de su función (por lo que su lambda no es realmente anónima, solo usa una variable __fn__ implícita (que debe ser renombrada a otra cosa, ya que los nombres de doble guion bajo están reservados para el comstackdor) , entonces tal vez algo como yourapp__fn__ sería mejor).

Todo esto se realiza en una statement compuesta de GCC (ver http://gcc.gnu.org/onlinedocs/gcc/Statement-Exprs.html#Statement-Exprs ), cuyo formato básico es algo así como:

 ({ ...; retval; }) 

la última statement de la statement compuesta es la dirección de la función recién declarada. Ahora, int (*max)(int,int) simplemente se asigna el valor de la instrucción compuesta, que ahora es el puntero a la función ‘anónimo’ recién declarada.

Las macros de depuración son un dolor real, por supuesto.

En cuanto a la razón por la test; .. al menos aquí, recibo la ‘prueba redeclarada como un tipo diferente de símbolo’, lo que supongo significa que GCC lo está tratando como una statement y no como una expresión (inútil). Debido a que las variables sin tipo son predeterminadas a int y porque ya has declarado test como una función (esencialmente, void (*)(void) ) obtienes eso … pero podría estar equivocado al respecto.

Sin embargo, esto no es portátil por ningún tramo de la imaginación.

  1. int (*max)(int, int) es el tipo de variable que está declarando. Se define como un puntero de función llamado max que devuelve int, y toma dos ints como parámetros.

  2. __fn__ refiere al nombre de la función, que en este caso es max.

  3. No tengo una respuesta allí. Me imagino que puede recorrerlo si lo ha ejecutado a través del preprocesador.

Respuesta parcial: No es int (* X) lo que le interesa. Es int (* X) (y, z). Ese es un puntero a la función llamada X que toma (y, z) y devuelve int.

Para la depuración, esto será realmente difícil. La mayoría de los depuradores no pueden rastrear a través de una macro. Lo más probable es que tenga que depurar el ensamblaje.

    Intereting Posts