2010-08-13 15 views
15

Creo que cada vez que leo un programa en C o C++, esa mitad o más son simplemente macros. Entiendo que las macros pueden ser geniales, pero son difíciles de rastrear, depurar, etc. Sin mencionar que la mayoría de los lenguajes de programación ni siquiera definen algo así como macros (aunque Perl6 tendrá algo así).Sobre la dependencia de las macros

Personalmente siempre he encontrado la manera de escribir mi código sin usar macros, ya sea con plantillas, herencia múltiple, etc. Incluso he sentido que no soy un buen programador porque todos los profesionales usan macros y trato de evitarlos tanto como pueda.

La pregunta es, ¿hay problemas que no se pueden resolver sin macros? ¿Las macros son finalmente una buena/mala práctica? ¿Cuándo debería considerar usar una macro?

+0

Bueno, no puedes usar plantillas en C, esa es una de las razones. – detly

+7

¿Qué código ha estado leyendo si considera que "todos los profesionales usan macros"? Todo el código de C++ que conozco trata de evitarlo. En C, es un poco diferente porque no tienes muchas alternativas. Por cierto, ¿por qué está etiquetado C en absoluto? Su pregunta * suena * bastante específica para C++, mencionando plantillas y herencia. C y C++ son idiomas diferentes, ya sabes. – jalf

+0

Exactamente lo que dijo @jalf: pueden tener algunas similitudes, pero cuando se trata de la generación de códigos, son planetas completamente diferentes. Por ejemplo, la [generación de X-macros/expresiones de tabla] (http://stackoverflow.com/questions/2927245/looking-for-a-good-explanation-of-the-table-generation-macro-idiom) – detly

Respuesta

16

Sí, aquí hay una. Cuando necesite agregar un código de rastreo a su programa de tal manera que una configuración lo contenga y el otro omita por completo, tendrá que usar macros.

Algo así como:

#ifdef WITH_LOGGING 
    #define LOG(x) DoLog(x) 
#else 
    #define LOG(x) 
#endif 

ahora se utiliza de esta manera:

LOG(L"Calling blahblahblah with " + getSomeStringHardToCompute()); 

y en la configuración con WITH_LOGGING que tienen ese código y lo contrario es completamente omitidos - ni siquiera presente en el binario, y por lo tanto

  • no ayuda a otros analizan su programa
  • se obtiene una pequeña binaria
  • el programa no pierde el tiempo fo registro en absoluto
  • el compilador puede producir código mejor optimizado.
+0

También se usa a menudo para agregar código que se ejecuta en modo de depuración pero no en código de modo de lanzamiento. –

+0

Ese es un ejemplo en el que una macro ha resultado útil, pero no es un ejemplo de algo que no podría hacerse sin una macro.Podrías poner un bloque #ifdef alrededor de cada llamada a DoLog() por ejemplo, si entiendes lo que quiero decir. – thomasrutter

+1

Esa es la plantilla o la función en línea. Sin necesidad de macros – Basilevs

-1

La pregunta es, ¿hay problemas que no se pueden resolver sin macros?

son macros en última instancia, una buena práctica volver /? ¿Cuándo debería considerar usar una macro?

En lenguajes que no soportan o el honor la palabra clave inline, las macros son una gran manera de reutilizar código, pero al mismo tiempo evitar la sobrecarga de una llamada a la función en cualquier código que está estrechamente bucle lo suficientemente para eso hacer una gran diferencia.

Probablemente esté justificado su despotricamiento sobre el código que está lleno de macros. De hecho, es difícil de depurar y, en algunos casos, leer. Pero sí resultan útiles en el reducido número de casos en que una optimización como esta está realmente justificada.

Tenga en cuenta que a partir de C99, C ahora puede hacer funciones en línea explícitas utilizando la palabra clave inline, que reduce la necesidad de macros e incluso has advantages over using macros.

+2

Siendo quisquilloso, necesita macros para incluir protecciones y compilación condicional, y en muchos marcos para evitar el costo del registro si el registro está deshabilitado. –

+1

De acuerdo con David. Hay problemas que solo se pueden resolver de manera elástica con macros (elegantemente un término relativo). –

+0

No, porque C++ con y sin macro es Turing completa? :) Pero hay muchos casos en que solo las macros (no las plantillas o las líneas) permiten evitar la duplicación de código. Estoy de acuerdo en que evitar las macros cuando sea posible debería ser una intención, pero la respuesta honesta debe ser "sí". – user396672

8

Has estado buscando un código C++ incorrecto.Los lugares que usar macros se limitan a:

  • guardias de cabecera
  • muy ocasional compilación condicional
  • una excepción general tirar macro
  • una depuración en general/salida registro macro

Pongo No creo que esos cuatro puedan evitarse.

+3

Especialmente porque para iniciar sesión/lanzar generalmente desea extraer automáticamente el nombre del archivo y el número de línea. –

-4

Las macros del lenguaje de programación son buenas para todas las macros: evitar escribir las mismas cosas una y otra vez. Entonces, si te encuentras escribiendo códigos idénticos en muchos lugares, ¿por qué no crear una macro? Especialmente si está escribiendo una biblioteca, usar macros puede hacer la vida más fácil para alguien que intenta usar esa biblioteca. Eche un vistazo a casi cualquier kit de herramientas GUI (Qt es un ejemplo). Todos hacen un uso extenso de macros.

+2

Es por eso que usamos funciones, no una razón para usar macros. –

+0

No es una buena razón para macros hay soluciones más elegantes que son mucho más seguras que resuelven el mismo problema (funciones) –

+0

Es cuando una función no puede hacer eso, cuando debe alcanzar las macros de pre procesadores – TerryP

9

Directamente de Myer de Scott efectiva C++ -> 1

Teniendo en cuenta la disponibilidad de consts y inlines, su necesidad de que el preprocesador se reduce, pero no se elimina por completo. El día está lejos de estar cerca cuando puedes abandonar #include, y # ifdef/# ifndef continúan desempeñando papeles importantes en el control de la compilación. Aún no es el momento de retirar el preprocesador, pero definitivamente debe planear comenzar a darle vacaciones más largas y frecuentes.

1

Las macros, por supuesto, también son útiles cuando se quiere generar código durante el preprocesamiento. Si bien esto se puede evitar utilizando plantillas (consulte esta pregunta y discusión de SO - Are C++ Templates just Macros in disguise?), puede usar macros si facilita la vida de sus usuarios: vea cómo el proyecto 'googletest' (https://github.com/google/googletest/) usa macros de manera efectiva. Obviamente, no desea utilizar macros para generar código que necesite depuración, use plantillas en su lugar.

4

El comportamiento de depuración se puede controlar con marcas de constante o funciones de depuración. Así que aquí está mi lista de inevitables:

  • Protección de inclusión múltiple.
  • Las macros son la única forma de stringification de símbolos. afirmar macro, realización compacta de cadena const & stringify (valor de categoría enum);

Ejemplo:

const char* stringify(enum category value) 
{ 
    #define c(x) case x: return #x; 
    switch(value) { 
     c(CIRCLE) 
     c(RECTANGLE) 
     c(TRIANGLE) 
     default: return "UNKNOWN"; 
    } 
    #undef c // the most important part 
} 
0

creo que las plantillas de C++ s y funciones en línea hacen que las macros más o menos evitable.

La omnipresencia de las macros se debe probablemente al hecho de que hay muchos programadores de C++ que solían ser programadores en C. Dichas personas probablemente sean competentes en el uso de macros (porque a veces es realmente la mejor o única solución en C pura) y es posible que no vean ningún punto en el aprendizaje de las características más complicadas de C++ si ya saben cómo resolver el problema. Al menos en el mundo de la fuente abierta, hay muchos C convertidos, por lo que naturalmente cumple con los paradigmas C. No creo que seas un mal programador si evitas esa característica, mucha gente lo hace, al igual que GOTO.

C (y por lo tanto C++) es un lenguaje de programación extremadamente flexible. Esto es genial, porque cada uno puede desarrollar su propio estilo y resolver la mayoría de los problemas de diferentes maneras. Esto, sin embargo, también puede considerarse un problema.En mi opinión no es un problema que deba ser resuelto por el idioma, sino por el establecimiento de convenciones.

Hay muchas funciones en C++ que se pueden ignorar de forma segura. Tal vez hay ocasiones especiales extraños donde esta característica sería realmente el mejor enfoque, pero en la mayoría de los casos, se puede vivir sin:

  • clases Amigo
  • Macros
  • GOTOS y más.

IMO, un programador senior de C++ debe ser capaz de leerlos al menos con fluidez, sin embargo, espero que un buen programador considere cuidadosamente cuándo y si utilizar una característica infame.

0

Hay muchos problemas que no puedo resolver sin macros. Por ejemplo, la serialización/deserialización de algunas estructuras

 
#define STRUCT_DESCRIPTION structname(MyStruct) member(int,a) member(double,b) member(long, c) 
#include "declare_serializable_struct.h" // declares struct itself and generates serialization/deserializaton code 
#undef STRUCT_DESCRIPTION 

(BOOST_PP_SEQUENCE también se puede usar) Otro ejemplo - el envío de un mensajes de usar el mapa de mensaje, es decir la generación de interruptor como esto:

 
switch(message_type) 
{ 
case msg1: on_msg1(msg); break; 
case msg2: on_msg2(msg); break; 
... 
} 

y generar Manejar las declaraciones del método on_msgX (msg) al mismo tiempo usando alguna tabla de descripción de mensaje ("map")

Personalmente, intento evitar las macros cuando sea posible, pero no tuve éxito n de esta manera.

Sin embargo, lambdas en C++ 0x permite código en línea arbitraria en "declaraciones LANGUGE-biblioteca definidos por el usuario, o" bucles foreach un ejemplo, por lo macro ámbito perder una parte significativa :)

0

Tal vez sea una poco fuera de tema, pero estaba leyendo un post sobre cómo disminuir el tiempo de compilación.

Y el autor observó que incluir cada línea de código en un único archivo con una macro (sí, es feo) disminuyó tanto el tiempo de compilación como el tamaño en una proporción fuerte (~ 70% para ambos parámetros).

¿Alguna explicación?

+0

El autor de eso publicación tiene algunas explicaciones, pero lo atribuiría en gran medida a: (1) malas prácticas de codificación: ¿por qué cada archivo fuente parece incluir cada encabezado y no solo los relevantes para él? y (2) el acceso al sistema de archivos en Windows es atrozmente lento. –

+0

Sí, mi pregunta no era realmente precisa, pero lo que más me asombra es la disminución en el tamaño del archivo binario. ¿Por qué incluir demasiados encabezados de los necesarios afecta el tamaño del archivo? – Nielk

0

Las macros son una solución para la compilación condicional (por ifdef y ifndef). Aquí es los ejemplos:

1)

#ifndef MY_HEADER_HPP 
#define MY_HEADER_HPP 

//... 

#endif 

2)

#ifdef __cplusplus 
#define BEGIN extern "C" { 
#define END } 
#define NULL (0); 
#else 
#define BEGIN 
#define END 
#define NULL ((void*)0); 
#endif 

//------------------------- 

BEGIN 

void my_function(char* str); 

END 

//------------------------- 

void my_function(char* str) 
{ 
    if(str != NULL) 
    { 
     //... 
    } 
} 

Pero funciones en línea y plantillas sustituye a otros usos de macros en C++.

0

Tiendo a evitar el uso de macros tanto como sea posible debido a sus obvios problemas de seguridad/depuración, sin embargo, hay momentos en que las macros ofrecen algo que ninguna otra facilidad dentro del lenguaje hace tan elegantemente, en cuyo caso prefiero usar un macro solo porque hace que mi vida (y la de mis compañeros desarrolladores) sea más fácil.

Por ejemplo, he creado una clase Enum, que envuelve una enumeración en un (alcance) struct y añade algunas funciones:

  • posibilidad de iteración (lo que implica un orden de los valores)
  • conversión a/desde cadena (útil para leer/escribir en un archivo, escribir en registros)

Para crear la enumeración, uso una macro que generará automáticamente el convertidor (hacia y desde) y el vector para la iteración.

Por supuesto que podría prescindir de uno, después de todo, la macro solo sirve para la generación de código. Pero prescindir de uno significaría violar DRY, y en mis pequeñas preferencias propias "DRY"> "No usar macros". Porque una vez depurada, la macro es segura, mientras que una violación DRY es una pesadilla para el mantenimiento.

Ahora, estoy a favor de abandonar esta macro tan pronto como encuentre la forma de no violar DRY. Las ideas son obviamente bienvenidas ... y una secuencia de comandos externa NO es mejor;)

Mis 2 centavos.

0

Intento evitar las macros también, pero para expandir la depuración, no encontré la forma de imprimir el nombre del archivo, el nombre de la función y el número de línea cuando se depura.

que normalmente tienen un archivo de cabecera llamada DebugLog.h con la siguiente macro

#define DEBUG(debugMessage) \ 
    printf("%s | %s [%d] - %s\n", __FILE__, __PRETTY_FUNCTION___, debugMessage); 

Usando: de depuración ("Test") de salida voluntad algo como:

main.cpp | foo(void)[20] - Test 

Puede ajustar la macro para C++ y otras instrucciones de depuración. También es posible modificar la macro para enviar la cadena resultante a un registrador.

Cuestiones relacionadas