how from diferencias code c++ c

c++ - from - ¿Alguien ha tenido alguna vez un uso para la macro preprocesadora__COUNTER__?



extern c (13)

Me interesa saber si alguien lo ha usado alguna vez.

Sí, pero como se puede ver en muchos ejemplos de estas preguntas y respuestas, __LINE__ , que está estandarizado, también sería suficiente en la mayoría de los casos.

__COUNTER__ solo es realmente necesario en los casos en que el conteo debe aumentar en uno cada vez, o debe tener continuidad sobre varios archivos #include .

¿Y si es algo que valdría la pena estandarizar?

__COUNTER__ , a diferencia de __LINE__ , es muy peligroso porque depende de qué archivos de encabezado están incluidos y de qué orden. Si dos archivos .cpp (unidades de traducción) incluyen un archivo de encabezado que usa __COUNTER__ , pero el archivo de encabezado obtiene diferentes secuencias de conteo en las diferentes instancias, pueden usar diferentes definiciones de la misma cosa y violar la regla de una definición.

Las infracciones de las reglas de una definición son muy difíciles de detectar y posiblemente crean errores y riesgos de seguridad. Los pocos casos de uso de __COUNTER__ realmente no superan la desventaja y la falta de escalabilidad.

Incluso si nunca envía el código que usa __COUNTER__ , puede ser útil al crear un prototipo de una secuencia de enumeración, lo que le ahorra la molestia de asignar nombres antes de que la membresía sea concreta.

El símbolo __COUNTER__ es proporcionado por VC++ y GCC, y da un valor integral no negativo en aumento cada vez que se usa.

Me interesa saber si alguien lo ha usado alguna vez y si es algo que valdría la pena estandarizar.


En nuestro código, nos olvidamos de añadir testcases para algunos de nuestros productos. Implementé ahora algunas macros para que podamos afirmar en tiempo de compilación que tenemos casos de prueba para cada producto que estamos agregando o eliminando.


Es utilizado por Boost.Asio para implementar coroutines sin pila.

Ver este archivo de cabecera y examples .

Las coroutinas resultantes se ven así:

struct task : coroutine { ... void operator()() { reenter (this) { while (... not finished ...) { ... do something ... yield; ... do some more ... yield; } } } ... };




Nunca lo he usado para nada más que una macro DEBUG. Es conveniente poder decir.

#define WAYPOINT / do { if(dbg) printf("At marker: %d/n", __COUNTER__); } while(0);


Se garantiza que __COUNTER__ sea ​​único a diferencia de __LINE__ . Algunos compiladores permiten que __LINE__ se reinicie. Los archivos #include también restablecerán __LINE__ .


Se usa en la biblioteca de cobertura de códigos de xCover , para marcar las líneas por las que pasa la ejecución, para encontrar las que no están cubiertas.


Se utiliza en el sistema de métricas de ClickHouse.

namespace CurrentMetrics { #define M(NAME) extern const Metric NAME = __COUNTER__; APPLY_FOR_METRICS(M) #undef M constexpr Metric END = __COUNTER__; std::atomic<Value> values[END] {}; /// Global variable, initialized by zeros. const char * getDescription(Metric event) { static const char * descriptions[] = { #define M(NAME) #NAME, APPLY_FOR_METRICS(M) #undef M }; return descriptions[event]; } Metric end() { return END; } }


Si estoy entendiendo la funcionalidad correctamente, desearía tener esa funcionalidad cuando estaba trabajando en Perl, agregando una función de registro de eventos en una GUI existente. Quería asegurarme de que las pruebas manuales necesarias (suspiros) nos dieran una cobertura completa, así que registré cada punto de prueba en un archivo, y el registro de un valor de __counter__ hizo que fuera fácil ver qué faltaba en la cobertura. Tal como estaba, codifiqué a mano el equivalente.


Tengo la intención de usar __COUNTER__ para dar a cada archivo en nuestro código base un identificador único, de modo que ese código único pueda usarse para registrar ASSERTs en un sistema integrado.

Este método es mucho más eficiente que usar cadenas para almacenar nombres de archivos (usando __FILE__ ), especialmente en un sistema incrustado con ROM pequeña. Pensé en la idea mientras leía este artículo: Afírmese en Embedded.com. Aunque es una pena que solo funcione con compiladores basados ​​en GCC.


Un uso está en la macro REGISTER_KERNEL_BUILDER de TensorFlow . Cada operación TensorFlow podría tener uno o más núcleos como sus implementaciones. Estos núcleos están registrados con un registrador. El registro de un núcleo se realiza mediante la definición de una variable global: el constructor de la variable puede realizar el registro. Aquí, los autores usan __COUNTER__ para dar a cada variable global un nombre único.

#define REGISTER_KERNEL_BUILDER(kernel_builder, ...) / REGISTER_KERNEL_BUILDER_UNIQ_HELPER(__COUNTER__, kernel_builder, __VA_ARGS__) #define REGISTER_KERNEL_BUILDER_UNIQ_HELPER(ctr, kernel_builder, ...) / REGISTER_KERNEL_BUILDER_UNIQ(ctr, kernel_builder, __VA_ARGS__) #define REGISTER_KERNEL_BUILDER_UNIQ(ctr, kernel_builder, ...) / static ::tensorflow::kernel_factory::OpKernelRegistrar / registrar__body__##ctr##__object( / SHOULD_REGISTER_OP_KERNEL(#__VA_ARGS__) / ? ::tensorflow::register_kernel::kernel_builder.Build() / : nullptr, / #__VA_ARGS__, [](::tensorflow::OpKernelConstruction* context) / -> ::tensorflow::OpKernel* { / return new __VA_ARGS__(context); / });


__COUNTER__ es útil en cualquier lugar donde necesite un nombre único. Lo he usado extensivamente para bloqueos y pilas de estilo RAII. Considerar:

struct TLock { void Lock(); void Unlock(); } g_Lock1, g_Lock2; struct TLockUse { TLockUse( TLock &lock ):m_Lock(lock){ m_Lock.Lock(); } ~TLockUse(){ m_Lock.Unlock(); } TLock &m_Lock; }; void DoSomething() { TLockUse lock_use1( g_Lock1 ); TLockUse lock_use2( g_Lock2 ); // ... }

Se vuelve tedioso nombrar los usos de bloqueo, e incluso puede convertirse en una fuente de errores si no están todos declarados en la parte superior de un bloque. ¿Cómo sabes si estás en lock_use4 o lock_use11 ? También es una contaminación innecesaria del espacio de nombres: nunca necesito referirme a los objetos de uso de bloqueo por su nombre. Entonces uso __COUNTER__ :

#define CONCAT_IMPL( x, y ) x##y #define MACRO_CONCAT( x, y ) CONCAT_IMPL( x, y ) #define USE_LOCK( lock ) TLockUse MACRO_CONCAT( LockUse, __COUNTER__ )( lock ) void DoSomething2() { USE_LOCK( g_Lock1 ); USE_LOCK( g_Lock2 ); // ... }

Pero no se preocupe por el hecho de que llamé los bloqueos de los objetos, ya que cualquier función que deba llamarse en pares coincidentes se ajusta a este patrón. Incluso puede tener múltiples usos en el mismo "bloqueo" en un bloque dado.