2011-01-25 6 views
7

Sabemos que en línea son favorables, ya que son verificados por el compilador y la misma operación (como ++ x) no evalúa más de una vez cuando pasa como argumento en comparación con macros.Ventaja de macro sobre la línea en C++

Pero en una entrevista me preguntaron las ventajas específicas o las circunstancias cuando una macro es más favorable para en línea en C++.

¿Alguien sabe la respuesta o puede pensar en esta cuestión?

Respuesta

14

Lo único que se me ocurre es que hay algunos trucos que puede hacer con un macro que no se puede hacer con una función en línea. Pegar tokens juntos en tiempo de compilación y ese tipo de hackers.

+11

Conocer automáticamente el nombre del archivo y el número de línea es otro truco útil que solo es posible con las macros. –

4

Algunas veces desea extender el idioma de formas que no son posibles con ningún otro método.

#include <iostream> 

#define CHECK(x) if (x); else std::cerr << "CHECK(" #x ") failed!" << std::endl 

int main() { 
    int x = 053; 
    CHECK(x == 42); 
    return 0; 
} 

Esta impresión CHECK(x == 42) failed!.

+0

No, da un error de compilación en la línea 'return 0' - falta un punto y coma. Y probablemente una advertencia de que el 'else' controla una declaración vacía. –

+0

@Ben: ¡Solucionado! No da ninguna advertencia en mi GCC. – ephemient

+0

Si desea que su macro vaya seguido de un punto y coma, use 'do {...} while (0)'. Su macro actual permite cosas como 'CHECK (0) << 5; ' –

8

Aquí hay una situación específica donde las macros no solo son preferidas, sino que en realidad son la única forma de lograr algo.

Si desea escribir una función de registro que registra no sólo algún mensaje, pero el número de línea del archivo & de donde la instancia se produjo, puede llamar a su función directamente, escribiendo los valores de la línea de archivos & (o macros) directamente:

LogError("Something Bad!", __FILE__, __LINE__); 

... o, si se quiere que funcione automáticamente, debe apoyarse en una macro (advertencia: no compilado):

#define LogErrorEx(ERR) (LogError(ERR, __FILE__, __LINE__)) 
// ... 
LogErrorEx("Something Else Bad!"); 

Esto no se puede lograr utilizando plantillas, por defecto parámetros , construcción predeterminada o cualquier otro dispositivo en C++.

+0

Para el registro también es muy común tener un' if 'declaración que comprueba si el registro para un nivel particular está habilitado. Cuando es falso, los argumentos para el registro no se evalúan en absoluto. –

0

Una macro es como una definición de reemplazo de texto.

Estas diferencias esenciales que vienen a la mente son:

  • No debe ser función similar. Quiero decir que no necesariamente debe contener un conjunto consistente de corchetes, por ejemplo.
  • Se puede usar en otro lado. Me gusta en un alcance de declaración de clase o incluso en el alcance global. Por lo tanto, no debe estar dentro del alcance de otra función.

Debe utilizar si desea llevar a cabo acciones que son imposibles de llevar a cabo utilizando funciones:

  • inicializar tablas complicadas (hace núcleo más legible)
  • declaración facilidad de algunos miembros especiales como ID de eventos o clases de etiquetas (utilizan mucho en MFC IMPLEMENT_DYNAMIC)
  • de compresión repetitivas declaraciones al inicio de funciones
  • el ya mencionado uso de __LINE__, __FILE__, ...para iniciar sesión
1

En C++ específicamente, un uso de MACRO que aparece muy a menudo (excepto la impresión de depuración con archivo y línea) es el uso de MACRO para completar un conjunto de métodos estándar en una clase que no se puede heredar de una clase base. En algunas bibliotecas que crean mecanismos personalizados de RTTI, serialización, plantillas de expresión, etc., a menudo se basan en un conjunto de variables estáticas estáticas y métodos estáticos (y posiblemente una semántica especial para algunos operadores sobrecargados que no se pueden heredar) que casi siempre son lo mismo, pero deben agregarse a cualquier clase que el usuario defina dentro de este marco. En estos casos, los MACRO a menudo se proporcionan de modo que el usuario no tenga que preocuparse por poner todo el código necesario (solo tiene que invocar el MACRO con la información solicitada). Por ejemplo, si hago un sistema simple de RTTI (Run-Time Tipo de identificación), que podría requerir que todas las clases tienen un TypeID y ser dinámicamente moldeable:

class Foo : public Bar { 
    MY_RTTI_REGISTER_CLASS(Foo, Bar, 0xBAADF00D) 
}; 

#define MY_RTTI_REGISTER_CLASS(CLASSNAME,BASECLASS,UNIQUEID) \ 
    public:\ 
    static const int TypeID = UNIQUEID;\ 
    virtual void* CastTo(int aTypeID) {\ 
     if(aTypeID == TypeID)\ 
     return this;\ 
     else\ 
     return BASECLASS::CastTo(aTypeID);\ 
    }; 

Lo anterior no se podía hacer con plantillas o herencia, y hace que la vida del usuario sea más fácil y evita la repetición del código.

Diría que este tipo de uso de MACROs es con mucho el más común en C++.

-1

yo añadiría dos usos:

  1. MIN y MAX, hasta C++ 0x, debido a que el tipo de retorno tuvo que ser declarada por la mano, mezclado min y max como funciones inline habría sido una pesadilla, mientras un simple macro lo hizo en un abrir y cerrar de ojos.
  2. privacidad: siempre puede undef el macro antes de salir de su encabezado, no puede "declarar" una función en línea (u otro símbolo). Esto se debe a la ausencia de una modularidad adecuada en los lenguajes C y C++.
+0

'std :: min' y' std :: max' funcionan bien como funciones de plantilla en este momento. –

+0

@ edA-qa: no, no es así. Requieren que ambos argumentos sean del mismo tipo. Tratar de crear una versión 'std :: min' que funcione para argumentos de diferentes tipos es (antes de C++ 0x) un ejercicio de frustración (si mal no recuerdo, requiere cien líneas de código), mientras se usa la macro, el compilador trata con la promoción de enteros con facilidad. –

+0

Tiene razón, no hace promociones. De hecho, creo que es algo bueno aquí, pero bueno, puede que necesites algo más. Al carecer de decltype, no puede crear la versión en línea de la macro. –

1

Como ya se ha dicho, las macros pueden utilizar directivas de preprocesador: __FILE__, __LINE__ por ejemplo, pero por supuesto #include y #define también puede ser útil para el comportamiento parámetro:

#ifdef __DEBUG__ 
# define LOG(s) std:cout << s << std:endl 
#else 
# define LOG(s) 
#endif 

Dependiendo wether __DEBUG__ se define o no (a través de #define oa través de las opciones del compilador), la macro LOG estará activa o no. Esta es una forma fácil de tener información de depuración en cualquier parte de tu código que se pueda desactivar fácilmente.

También puede pensar en cambiar la forma en que se asigna la memoria (malloc se redefinirá para apuntar a un grupo de memoria en lugar del montón estándar, por ejemplo, etc ...).

+0

¿qué tal: '#ifdef __DEBUG__ - inline void log() {...} - #else - inline void log() {} - # endif'? –

1

Las funciones en línea son, como su nombre indica, restringidas a tareas funcionales, la ejecución de algún código.

Las macros tienen una aplicación mucho más amplia que pueden expandir, por ejemplo, a declaraciones o reemplazar constructos de lenguaje completos. Algunos ejemplos (escritas para C y C++) que no se pueden hacer con las funciones de:

typedef struct POD { double a; unsigned b } POD; 
#declare POD_INITIALIZER { 1.0, 37u } 

POD myPOD = POD_INITIALIZER; 

#define DIFFICULT_CASE(X) case (X)+2 :; case (X)+3 
#define EASY_CASE(X) case (X)+4 :; case (X)+5 

switch (a) { 
    default: ++a; break; 
    EASY_CASE('0'): --a; break; 
    DIFFICULT_CASE('A'): a = helperfunction(a); break; 
} 

#define PRINT_VALUE(X)      \ 
do {           \ 
char const* _form = #X " has value 0x%lx\n"; \ 
fprintf(stderr, _form, (unsigned long)(X)); \ 
} while (false) 

En el contexto de C++, Boost tiene mucha más ejemplos que están más involucrados y más útil.

Pero debido a que tales macros están ampliando el lenguaje (no estrictamente, el preprocesador es parte de él) a muchas personas no les gustan las macros, particularmente en la comunidad C++, un poco menos en la comunidad C.

En cualquier caso, si utiliza tales construcciones, siempre debe tener muy claro lo que debe lograr, documentar bien y luchar contra la tentación de ofuscar su código.

0
#include <stdio.h> 
    #define sq(x) x*x 
    int main() 
    { 
     printf("%d", sq(2+1)); 
     printf("%d", sq(2+5)); 
     return 0; 
    } 

La salida para este código es 5 y 17. Las macros se expanden textualmente. No son como funciones.

explicación de este ejemplo:

cuadrados (2 + 1) = 2 + 1 * 2 + 1 = 2 + 2 + 1 = 5

cuadrados (2 + 5) = 2 + 5 * 2 + 5 = 2 + 10 + 5 = 17

+1

¿Entonces esa es una ventaja de las macros? –

Cuestiones relacionadas