El preprocesador C es temido y rechazado por la comunidad C ++. Las funciones en línea, los consts y las plantillas suelen ser una alternativa más segura y superior a a #define
.
La siguiente macro:
#define SUCCEEDED(hr) ((HRESULT)(hr) >= 0)
de ninguna manera es superior al tipo seguro:
inline bool succeeded(int hr) { return hr >= 0; }
Pero las macros tienen su lugar, enumere los usos que encuentra para las macros que no puede hacer sin el preprocesador.
Ponga cada caso de uso en una respuesta separada para que se pueda votar y si sabe cómo lograr una de las respuestas sin que el preprosesador indique cómo en los comentarios de esa respuesta.
c++
c-preprocessor
Motti
fuente
fuente
Respuestas:
Como contenedores para funciones de depuración, para pasar automáticamente cosas como
__FILE__
,__LINE__
, etc:fuente
__FILE__
y__LINE__
también requiere el preprocesador. Usarlos en su código es como un vector de infección para el preprocesador.Los métodos siempre deben ser completos, código compilable; Las macros pueden ser fragmentos de código. Por lo tanto, puede definir una macro foreach:
Y úsalo así:
Desde C ++ 11, esto es reemplazado por el bucle for basado en rango .
fuente
for_each
era algo desagradable, porque el código por el que se ejecutaba cada elemento no era local al punto de llamada.foreach
, (y recomiendo encarecidamente enBOOST_FOREACH
lugar de una solución enrollada a mano), mantenga el código cerca del sitio de iteración, para que sea más legible. Dicho esto, una vez que se despliegue lambda,for_each
podría ser una vez más el camino a seguir.Los protectores de archivos de encabezado necesitan macros.
¿Hay otras áreas que requieren macros? No muchos (si los hay).
¿Hay otras situaciones que se benefician de las macros? ¡¡¡SI!!!
Un lugar donde uso macros es con código muy repetitivo. Por ejemplo, al envolver el código C ++ para usarlo con otras interfaces (.NET, COM, Python, etc.), necesito detectar diferentes tipos de excepciones. Así es como hago eso:
Tengo que poner estas capturas en cada función de contenedor. En lugar de escribir los bloques completos cada vez, simplemente escribo:
Esto también facilita el mantenimiento. Si alguna vez tengo que agregar un nuevo tipo de excepción, solo hay un lugar donde necesito agregarlo.
También hay otros ejemplos útiles: muchos de los cuales incluyen las macros
__FILE__
y__LINE__
preprocesador.De todos modos, las macros son muy útiles cuando se usan correctamente. Las macros no son malas, su mal uso es malo.
fuente
#pragma once
estos días, por lo que dudo que los guardias sean realmente necesarios#pragma once
se rompe en muchos sistemas de construcción comunes.void handleExceptions(){ try { throw } catch (::mylib::exception& e) {....} catch (::std::exception& e) {...} ... }
. Y en el lado funcional:void Foo(){ try {::mylib::Foo() } catch (...) {handleExceptions(); } }
Principalmente:
__LINE__
y__FILE__
)fuente
Dentro de la compilación condicional, para superar problemas de diferencias entre compiladores:
fuente
close
funciones o métodos. Luego, cuando incluye el encabezado de esta biblioteca y el encabezado con esta macro, entonces tiene un gran problema, no puede usar la API de la biblioteca.#ifdef WE_ARE_ON_WIN32
por favor :)Cuando desea hacer una cadena de una expresión, el mejor ejemplo para esto es
assert
(#x
convierte el valor dex
en una cadena).fuente
Las constantes de cadena a veces se definen mejor como macros, ya que puede hacer más con literales de cadena que con a
const char *
.por ejemplo, los literales de cadena se pueden concatenar fácilmente .
Si
const char *
se usara a, entonces se tendría que usar algún tipo de clase de cadena para realizar la concatenación en tiempo de ejecución:fuente
Cuando desea cambiar el flujo del programa (
return
,break
ycontinue
) el código en una función se comporta de manera diferente que el código que está en línea en la función.fuente
-1
oNULL
. Entonces, una macro puede reducir en gran medida el código repetitivo allí.Los obvios incluyen guardias
fuente
No puede realizar un cortocircuito de los argumentos de llamada de función utilizando una llamada de función regular. Por ejemplo:
fuente
Digamos que ignoraremos cosas obvias como los protectores de cabecera.
A veces, desea generar código que el precompilador debe copiar / pegar:
que le permite codificar esto:
Y puede generar mensajes como:
Tenga en cuenta que mezclar plantillas con macros puede conducir a resultados aún mejores (es decir, generar automáticamente los valores lado a lado con sus nombres de variables)
Otras veces, necesita el __FILE__ y / o la __LINE__ de algún código, para generar información de depuración, por ejemplo. El siguiente es un clásico para Visual C ++:
Como con el siguiente código:
genera mensajes como:
Otras veces, debe generar código utilizando los operadores de concatenación # y ##, como generar captadores y establecedores para una propiedad (esto es para casos bastante limitados, a través de).
Otras veces, generará código que no se compilará si se usa a través de una función, como:
Que se puede usar como
(Aún así, solo vi este tipo de código utilizado correctamente una vez )
Por último, pero no menos importante, los famosos
boost::foreach
!!!(Nota: copia de código / pegado desde la página de inicio de boost)
Que es (en mi humilde opinión) mucho mejor que
std::for_each
.Por lo tanto, las macros siempre son útiles porque están fuera de las reglas normales del compilador. Pero encuentro que la mayoría de las veces que veo uno, son efectivamente restos de código C que nunca se traducen a C ++ adecuado.
fuente
#include <sstream> #include <iostream> using namespace std; void trace(char const * file, int line, ostream & o) { cerr<<file<<":"<<line<<": "<< static_cast<ostringstream & >(o).str().c_str()<<endl; } struct Oss { ostringstream s; ostringstream & lval() { return s; } }; #define TRACE(ostreamstuff) trace(__FILE__, __LINE__, Oss().lval()<<ostreamstuff) int main() { TRACE("Hello " << 123); return 0; }
esa manera, la macro es mucho más corta.Los marcos de prueba de unidad para C ++ como UnitTest ++ giran en torno a las macros de preprocesador. Algunas líneas de código de prueba de unidad se expanden en una jerarquía de clases que no sería divertido escribir manualmente. Sin algo como UnitTest ++ y su magia de preprocesador, no sé cómo escribiría eficientemente pruebas unitarias para C ++.
fuente
Temer al preprocesador C es como temer a las bombillas incandescentes solo porque obtenemos bombillas fluorescentes. Sí, el primero puede ser {electricidad | tiempo de programador} ineficiente. Sí, puedes quemarte (literalmente) por ellos. Pero pueden hacer el trabajo si lo manejas adecuadamente.
Cuando programa sistemas embebidos, C suele ser la única opción aparte del ensamblador de formularios. Después de programar en el escritorio con C ++ y luego cambiar a objetivos más pequeños e integrados, aprenderá a dejar de preocuparse por las "inelegancias" de tantas funciones C desnudas (macros incluidas) y simplemente tratar de descubrir el uso mejor y seguro que puede obtener de esas caracteristicas.
Alexander Stepanov dice :
fuente
Utilizamos las macros
__FILE__
y__LINE__
para fines de diagnóstico en el lanzamiento, captura y registro de excepciones con gran cantidad de información, junto con escáneres automáticos de archivos de registro en nuestra infraestructura de control de calidad.Por ejemplo, una macro de lanzamiento
OUR_OWN_THROW
podría usarse con parámetros de tipo de excepción y constructor para esa excepción, incluida una descripción textual. Me gusta esto:Por supuesto, esta macro arrojará la
InvalidOperationException
excepción con la descripción como parámetro constructor, pero también escribirá un mensaje en un archivo de registro que consta del nombre del archivo y el número de línea donde ocurrió el lanzamiento y su descripción textual. La excepción lanzada obtendrá una identificación, que también se registra. Si la excepción alguna vez se detecta en otro lugar del código, se marcará como tal y el archivo de registro indicará que se ha manejado esa excepción específica y que, por lo tanto, no es probable que sea la causa de un bloqueo que pueda registrarse más adelante. Nuestra infraestructura automatizada de control de calidad puede detectar fácilmente las excepciones no controladas.fuente
Código de repetición.
Echa un vistazo para impulsar la biblioteca de preprocesadores , es una especie de meta-meta-programación. En tema-> motivación puedes encontrar un buen ejemplo.
fuente
Algunas cosas muy avanzadas y útiles aún se pueden construir usando un preprocesador (macros), lo cual nunca se podría hacer usando las "construcciones de lenguaje" de c ++, incluidas las plantillas.
Ejemplos:
Hacer algo tanto un identificador C como una cadena
Manera fácil de usar variables de tipos de enumeración como cadena en C
Impulsar la metaprogramación del preprocesador
fuente
stdio.h
ysal.h
archivavc12
para entender mejor.Ocasionalmente uso macros para poder definir información en un solo lugar, pero la uso de diferentes maneras en diferentes partes del código. Es solo un poco malvado :)
Por ejemplo, en "field_list.h":
Luego, para una enumeración pública, se puede definir simplemente usando el nombre:
Y en una función init privada, todos los campos se pueden usar para llenar una tabla con los datos:
fuente
Un uso común es detectar el entorno de compilación, para el desarrollo multiplataforma puede escribir un conjunto de código para Linux, digamos, y otro para Windows cuando ya no exista una biblioteca multiplataforma para sus propósitos.
Entonces, en un ejemplo aproximado, un mutex multiplataforma puede tener
Para las funciones, son útiles cuando desea ignorar explícitamente la seguridad de tipos. Tales como los muchos ejemplos arriba y abajo para hacer ASSERT. Por supuesto, como muchas características de C / C ++, puedes dispararte en el pie, pero el lenguaje te brinda las herramientas y te permite decidir qué hacer.
fuente
Algo como
Para que pueda, por ejemplo, tener
y obtenga el nombre del archivo fuente y el número de línea del problema impreso en su registro si n es falso.
Si usa una llamada de función normal como
en lugar de la macro, todo lo que puede obtener es el número de línea de su función de aserción impresa en el registro, lo que sería menos útil.
fuente
<cassert>
laassert()
macro, que volca la información de archivo / línea / función? (en todas las implementaciones que he visto, de todos modos)A diferencia de la solución de plantilla 'preferida' discutida en un hilo actual, puede usarla como una expresión constante:
fuente
template<typename T, std::size_t size> constexpr std::size_t array_size(T const (&)[size]) { return size; }
Puede usar #defines para ayudar con los escenarios de depuración y prueba unitaria. Por ejemplo, cree variantes de registro especiales de las funciones de memoria y cree un memlog_preinclude.h especial:
Compila tu código usando:
Un enlace en tu memlog.o a la imagen final. Ahora puede controlar malloc, etc., tal vez con fines de registro o para simular fallas de asignación para pruebas unitarias.
fuente
Cuando toma una decisión en tiempo de compilación sobre el comportamiento específico del Compilador / SO / Hardware.
Le permite configurar su interfaz para las características específicas de Comppiler / OS / Hardware.
fuente
Utilizo macros para definir fácilmente Excepciones:
donde DEF_EXCEPTION es
fuente
Los compiladores pueden rechazar su solicitud en línea.
Las macros siempre tendrán su lugar.
Algo que encuentro útil es #define DEBUG para el seguimiento de depuración: puede dejarlo 1 mientras depura un problema (o incluso dejarlo encendido durante todo el ciclo de desarrollo) y luego apagarlo cuando sea el momento de enviar.
fuente
En mi último trabajo, estaba trabajando en un escáner de virus. Para facilitarme la depuración, tuve un montón de registros atascados por todas partes, pero en una aplicación de alta demanda como esa, el costo de una llamada de función es demasiado costoso. Entonces, se me ocurrió esta pequeña Macro, que todavía me permitía habilitar el registro de depuración en una versión de lanzamiento en el sitio de un cliente, sin el costo de una llamada de función verificaría el indicador de depuración y simplemente regresaría sin registrar nada, o si estaba habilitado , haría el registro ... La macro se definió de la siguiente manera:
Debido a VA_ARGS en las funciones de registro, este fue un buen caso para una macro como esta.
Antes de eso, usé una macro en una aplicación de alta seguridad que necesitaba decirle al usuario que no tenía el acceso correcto, y que les diría qué bandera necesitaban.
La Macro (s) definida como:
Luego, podríamos rociar las comprobaciones por toda la interfaz de usuario, y le diría qué roles tenían permitido realizar la acción que intentó hacer, si aún no tenía ese rol. La razón para dos de ellos era devolver un valor en algunos lugares, y regresar de una función nula en otros ...
De todos modos, así es como los he usado, y no estoy seguro de cómo esto podría haber sido ayudado con plantillas ... Aparte de eso, trato de evitarlos, a menos que sea REALMENTE necesario.
fuente
Otra macros foreach. T: tipo, c: contenedor, i: iterador
Uso (concepto que muestra, no real):
Mejores implementaciones disponibles: Google "BOOST_FOREACH"
Buenos artículos disponibles: Amor condicional: FOREACH Redux (Eric Niebler) http://www.artima.com/cppsource/foreach.html
fuente
Quizás el mayor uso de las macros se encuentra en el desarrollo independiente de la plataforma. Piense en casos de inconsistencia de tipo: con macros, simplemente puede usar diferentes archivos de encabezado, como: --WIN_TYPES.H
--POSIX_TYPES.h
--programa.h
Muy legible que implementarlo de otras maneras, en mi opinión.
fuente
Parece que VA_ARGS solo se han mencionado indirectamente hasta ahora:
Al escribir código genérico C ++ 03, y necesita un número variable de parámetros (genéricos), puede usar una macro en lugar de una plantilla.
Nota: en general, el nombre de verificación / lanzamiento también podría incorporarse a la
get_op_from_name
función hipotética . Esto es solo un ejemplo. Puede haber otro código genérico que rodea la llamada VA_ARGS.Una vez que obtengamos plantillas variadas con C ++ 11, podemos resolver esto "correctamente" con una plantilla.
fuente
Creo que este truco es un uso inteligente del preprocesador que no se puede emular con una función:
Entonces puedes usarlo así:
También puede definir una macro RELEASE_ONLY.
fuente
Puede
#define
constantes en la línea de comando del compilador usando la opción-D
o/D
. Esto a menudo es útil cuando se realiza una compilación cruzada del mismo software para múltiples plataformas porque puede hacer que sus archivos MAKE controlen qué constantes se definen para cada plataforma.fuente