Pregunta Cómo pasar funciones simples y anónimas como parámetros en C


Estoy seguro de que se ha preguntado alguna variación de esta pregunta antes, pero todas las demás preguntas similares sobre SO parecen ser mucho más complejas, que involucran pasar matrices y otras formas de datos. Mi escenario es mucho más simple, así que espero que haya una solución simple / elegante.

¿Hay alguna manera de que pueda crear una función anónima o pasar una línea de código como un puntero a otra función?

En mi caso, tengo una serie de operaciones diversas. Antes y después de cada línea de código, hay tareas que quiero realizar, que nunca cambian. En lugar de duplicar el código de inicio y el código de fin, me gustaría escribir una función que toma un puntero de función como parámetro y ejecuta todo el código en el orden necesario.

Mi problema es que no vale la pena definir 30 funciones para cada operación, ya que cada una es una línea de código. Si no puedo crear una función anónima, ¿hay alguna manera de simplificar mi código C?

Si mi solicitud no está del todo clara. Aquí hay un poco de pseudocódigo para aclarar. Mi código es mucho más significativo que esto, pero el código que aparece a continuación muestra el punto.

void Tests()
{
  //Step #1
  printf("This is the beginning, always constant.");
  something_unique = a_var * 42;  //This is the line I'd like to pass as an anon-function.
  printf("End code, never changes");
  a_var++;

  //Step #2
  printf("This is the beginning, always constant.");
  a_diff_var = "arbitrary";  //This is the line I'd like to pass as an anon-function.
  printf("End code, never changes");
  a_var++;

  ...
  ...

  //Step #30
  printf("This is the beginning, always constant.");
  var_30 = "Yup, still executing the same code around a different operation.  Would be nice to refactor...";  //This is the line I'd like to pass as an anon-function.
  printf("End code, never changes");
  a_var++;
}

5
2017-08-10 16:02


origen


Respuestas:


No en el sentido tradicional de funciones anónimas, pero puede macro:

#define do_something(blah) {\
    printf("This is the beginning, always constant.");\
    blah;\
    printf("End code, never changes");\
    a_var++;\
}

Entonces se vuelve

do_something(something_unique = a_var * 42)

9
2017-08-10 16:06



No, no puedes. Las funciones anónimas solo están disponibles en idiomas funcionales (y en idiomas con subconjuntos funcionales), y como todos sabemos, c es disfuncional; ^)


4
2017-08-10 16:05



En C y pre-0x C ++, no.

En C ++ 0x, sí, usando funciones lambda.


2
2017-08-10 16:04



La mejor manera de simplificar su código probablemente sería poner un bucle for alrededor de una instrucción switch.

int a_var;
for ( a_var = 0; a_var <= 30; a_var++ )
{
    starteroperations();
    switch (a_var)
    {
         case 0:
             operation0(); break;
         case ...:
             operationx(); break;
         case 30:
             ...
    }
    closingoperations();
 }

2
2017-08-10 16:11



Si puede usar Clang, puede aprovechar los bloques. Para aprender bloques, puede usar Documentación de Apple, Especificación del lenguaje de bloques de Clang y notas de implementacióny Propuesta de Apple para el grupo de trabajo ISO C para agregar bloques al lenguaje C estándar, así como un montón de publicaciones de blog.

Usando bloques, podrías escribir:

/* Block variables are declared like function pointers
 * but use ^ ("block pointer") instead of * ("normal pointer"). */
void (^before)(void) = void ^(void) { puts("before"); };

/* Blocks infer the return type, so you don't need to declare it
 * in the block definition. */
void (^after)(void) = ^(void) { puts("after"); };

/* The default arguments are assumed to be void, so you could even
 * just define after as
 *
 *     ^{ puts("after"); };
 */

before();
foo = bar + baz*kablooie;
after();

Este ejemplo proporciona los nombres de bloques anónimos mediante la asignación a una variable de bloque. También puede definir y llamar un bloque directamente:

   ^{ puts("!"); } ();
/*|  definition   | invocation of anonymous function |*/

Esto también hace que la definición de "objetos-estructura" (OOP en C usando estructuras) sea muy simple.

Soporte de Clang y GCC funciones internas / anidadas como una extensión del estándar C. Esto le permitiría definir la función inmediatamente antes de tomar su dirección, lo que podría ser una alternativa si la estructura del flujo de control lo permite: los punteros de función interna no pueden escapar de su alcance inmediato. Como dicen los doctores:

Si intenta llamar a la función anidada a través de su dirección después de que la función contenedora haya salido, todo el infierno se desatará. Si intentas llamar después de que ha salido un nivel de ámbito que lo contiene, y si se refiere a algunas de las variables que ya no están dentro del alcance, puede que tengas suerte, pero no es prudente correr el riesgo. Sin embargo, si la función anidada no se refiere a nada que haya salido del alcance, debe estar seguro.

Usando funciones anidadas, podrías escribir:

 /* Nested functions are defined just like normal functions.
  * The difference is that they are not defined at "file scope"
  * but instead are defined inside another function. */
 void before(void) { puts("before"); };
 void after(void) { puts("after"); };

 before();
 foo = bar + baz*kablooie;
 after();

0
2017-08-10 16:09



O vas al case de la forma sugerida por @dcpomero, o haces lo siguiente:

typedef void job(int);
job test1; void test1(int a_var) { something_unique = a_var * 42; }
job test2; void test2(int a_var) { a_diff_var = "arbitrary"; }
job test3; void test3(int a_var) { var_30 = "Yup, still executing the same code around a different operation.  Would be nice to refactor..."; }

job * tests[] = { test1, test2, test3, testn };

void Tests()
{
    int i;
    for (i=0; i < sizeof tests/sizeof tests[0]; i++) {
        printf("This is the beginning, always constant.");
        tests[i](a_var);
        printf("End code, never changes");
        a_var++;
    }
}

0
2017-08-10 16:25