2012-01-06 16 views
10

Tengo una función que necesito para macroeditar. La función contiene variables temporales y no puedo recordar si existen reglas sobre el uso de variables temporales en sustituciones de macros.¿Puede una macro C contener variables temporales?

long fooAlloc(struct foo *f, long size) 
{ 
    long  i1, i2; 
    double *data[7]; 

    /* do something */ 
    return 42; 
} 

Formulario MACRO:

#define ALLOC_FOO(f, size) \ 
{\ 
    long  i1, i2;\ 
    double *data[7];\ 
\ 
    /* do something */ \ 
} 

Es esto aceptable? (es decir, sin efectos secundarios desagradables, aparte de los usuales: no "escriba seguro", etc.). Por cierto, sé que "las macros son malas", simplemente tengo que usarlas en este caso, no tengo muchas opciones.

+3

¿Por qué necesita macrografiarlo? Si le preocupa el rendimiento, entonces (a) es probable que se esté preocupando innecesariamente (a menos que lo haya medido realmente y haya llegado a la conclusión de que es un cuello de botella significativo) y (b) puede lograr el mismo efecto, en un tipo más de manera segura, al hacer que la función 'en línea' (algunos compiladores anteriores o de Microsoft podrían no ser compatibles con esto). –

+3

@KeithThompson el rendimiento no es el problema aquí. Estoy escribiendo una biblioteca de extensión y necesito una función de contenedor para hacer una clasificación de datos. Las macros simplemente proporcionan una forma menos propensa a errores de generar automágicamente un gran código de "pegamento". –

+0

'return' dentro de una macro no hace lo que parece pensar que hace. –

Respuesta

24

Existen solo dos condiciones bajo las cuales funciona de una manera "razonable".

  1. La macro no tiene una declaración de devolución. Puede usar el truco do while.

    #define macro(x) do { int y = x; func(&y); } while (0) 
    
  2. Solo tiene como destino GCC.

    #define min(x,y) ({ int _x = (x), _y = (y); _x < _y ? _x : _y; }) 
    

Sería de gran ayuda si usted explicar por qué usted tiene que utilizar una macro (no tiene su oficina "lunes macro" o algo así?). De lo contrario, no podemos ayudar.

6

Las macros C son sustitutos textuales (relativamente simples).

Así que la pregunta que tal vez se esté preguntando es: ¿puedo crear bloques (también llamados enunciados compuestos) en una función como en el siguiente ejemplo?

void foo(void) 
{ 
    int a = 42; 
    { 
     int b = 42; 
     { 
      int c = 42; 
     } 
    } 
} 

y la respuesta es sí.

Ahora, como @DietrichEpp indican que en su respuesta, si la macro es una sentencia compuesta, como en el ejemplo, es una buena práctica para encerrar las instrucciones de macros con do { ... } while (0) en lugar de sólo { ... }. El siguiente enlace explica qué situación la do { ... } while (0) en una macro trata de evitar:

http://gcc.gnu.org/onlinedocs/cpp/Swallowing-the-Semicolon.html

También cuando se escribe un macro-función como siempre preguntarse si tiene una verdadera ventaja de hacerlo así porque lo más a menudo escribiendo una la función en cambio es mejor.

0

Sí, debería funcionar como usted usa una estructura de bloque y las variables de temperatura se declaran en el alcance interno de este bloque.

Tenga en cuenta que la última \ after the} es redundante.

0

Ellos pueden. A menudo no deberían.

¿Por qué esta función debe ser una macro? ¿Podrías alinearlo en su lugar?

+1

Excepto que usted encontrará que a medida que su programa crece y surgen problemas de soporte, mantener el código generado por las macros NO es menos propenso a errores. Es un desastre. – Marvo

0

Si usa C++ use en línea o usa -o3 con gcc, incorporará todas las funciones por usted. Todavía no entiendo por qué necesita macrograbar esta función.

+1

C tiene 'en línea', también. –

5

Esto es sobre todo bien, excepto que las macros suelen estar encerrados con do { ... } while(0) (echar un vistazo a this question explicaciones):

#define ALLOC_FOO(f, size) \ 
    do { \ 
     long  i1, i2;\ 
     double *data[7];\ 
     /* do something */ \ 
    } while(0) 

Además, en cuanto a su función original fooAlloc vuelve long usted tiene que cambiar su macro para almacenar el resultado de alguna otra manera. O, si se utiliza GCC, puede intentar compound statement extensión:

#define ALLOC_FOO(f, size) \ 
    ({ \ 
     long  i1, i2;\ 
     double *data[7];\ 
     /* do something */ \ 
     result; \ 
    }) 

Finalmente usted debe cuidar de los posibles efectos secundarios de la expansión argumento macro. El patrón usual es definir una variable temporal para cada argumento dentro de un bloque y el uso de ellos en su lugar: Respuesta

#define ALLOC_FOO(f, size) \ 
    ({ \ 
     typeof(f) __f = (f);\ 
     typeof(size) __size = (size);\ 
     long  i1, i2;\ 
     double *data[7];\ 
     /* do something */ \ 
     result; \ 
    }) 
+0

mis ojos están sangrando – paulm

2

de Eldar muestra que la mayoría de las trampas de la programación de macros y algunos (pero no estándar) útil extensión gcc.

Si desea seguir el estándar, una combinación de macros (para genericity) y inline funciones (para las variables locales) puede ser útil.

inline 
long fooAlloc(void *f, size_t size) 
{ 
    size_t  i1, i2; 
    double *data[7]; 

    /* do something */ 
    return 42; 
} 


#define ALLOC_FOO(T) fooAlloc(malloc(sizeof(T)), sizeof(T)) 

En tal caso, el uso de sizeof sólo evalúa la expresión para el tipo en tiempo de compilación y no por su valor, por lo que este no sería evaluar F dos veces.

Por cierto, los "tamaños" generalmente deben escribirse con size_t y no con long o similar.

Editar: En cuanto a la pregunta de Jonathan sobre inline funciones, he escrito algo sobre el modelo inline de C99, here.

+0

¿Debería ser 'estático en línea ...'? –

+0

@Jonathan, ¿por qué sería eso? –

+0

Debe tener mucho cuidado con 'inline' en C (IMO). Las tres preguntas más relevantes que encontré en SO son ["¿Definiciones en línea?"] (Http://stackoverflow.com/questions/8454690/inline-definitions), ["¿Está en línea sin estática o externa alguna vez útil en C99?" ] (http://stackoverflow.com/questions/6312597/is-inline-without-static-or-extern-ever-useful-in-c99), y ["extern inline?"] (http: // stackoverflow. com/questions/216510/extern-inline), no en un orden particular. (Y, para que conste, no estoy pontificando sobre el tema, estaba haciendo una pregunta.) –

7

Primero, recomiendo encarecidamente las funciones en línea. Hay muy pocas cosas que las macros pueden hacer y no pueden, y es mucho más probable que hagan lo que usted espera.

Una trampa de las macros, que no vi en otras respuestas, es el sombreado de nombres de variables.
Supongamos que ha definido:

#define A(x) { int temp = x*2; printf("%d\n", temp); } 

Y alguien utiliza de esta manera:

int temp = 3; 
A(temp); 

Después de pre-procesamiento, el código es:

int temp = 3; 
{ int temp = temp*2; printf("%d\n", temp); } 

Esto no funciona, ya que el interior temp sombrea lo externo.
La solución común es llamar a la variable __temp, suponiendo que nadie va a definir una variable con este nombre (lo cual es una suposición extraña, dado que simplemente lo hizo).

0

Una solución no es perfecta: (no funciona con macros recursivas, por ejemplo múltiples bucles dentro de otros)

#define JOIN_(X,Y) X##Y 
#define JOIN(X,Y) JOIN_(X,Y) 
#define TMP JOIN(tmp,__LINE__) 

#define switch(x,y) int TMP = x; x=y;y=TMP 

int main(){ 
    int x = 5,y=6; 
    switch(x,y); 
    switch(x,y); 
} 

se convertirá después de ejecutar el preprocesador:

int main(){ 
    int x=5,y=6; 
    int tmp9 = x; x=y; y=tmp9; 
    int tmp10 = x; x=y; y=tmp10; 
} 
Cuestiones relacionadas