Quelqu’un a-t-il déjà utilisé la macro de pré-processeur __COUNTER__?

Le symbole __COUNTER__ est fourni par VC ++ et GCC et donne une valeur intégrale non négative croissante à chaque utilisation.

Je suis intéressé à savoir si quelqu’un l’a déjà utilisé et si c’est quelque chose qui mériterait d’être standardisé?

Il est utilisé dans la bibliothèque de couverture de code xCover , pour marquer les lignes traversées par l’exécution, pour trouver celles qui ne sont pas couvertes.

__COUNTER__ est utile partout où vous avez besoin d’un nom unique. Je l’ai beaucoup utilisé pour les serrures et les stacks de style RAII. Considérer:

 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 ); // ... } 

Il devient fastidieux de nommer les utilisations du verrou, et peut même devenir une source d’erreurs si elles ne sont pas toutes déclarées en haut d’un bloc. Comment savez-vous si vous êtes sur lock_use4 ou lock_use11 ? C’est aussi une pollution inutile de l’espace de noms – je n’ai jamais besoin de me référer aux objects d’utilisation du verrou par leur nom. Donc j’utilise __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 ); // ... } 

Mais ne vous attardez pas sur le fait que j’ai appelé les verrous des objects – toute fonction devant être appelée dans des paires correspondantes correspond à ce modèle. Vous pourriez même avoir plusieurs utilisations sur le même “verrou” dans un bloc donné.

Je l’ai utilisé dans une macro d’assertion à la compilation pour que la macro crée un nom pour un typedef unique. Voir

  • Façons d’assumer des expressions au moment de la construction en C

si vous voulez les détails gores.

Je ne l’ai jamais utilisé pour autre chose qu’une macro DEBUG. C’est pratique de pouvoir dire

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

Je suis intéressé à savoir si quelqu’un l’a déjà utilisé,

Oui, mais comme vous pouvez le voir dans de nombreux exemples dans cette Q & R, __LINE__ , qui est standardisé, serait également suffisant dans la plupart des cas.

__COUNTER__ n’est vraiment nécessaire que dans les cas où le compte doit augmenter de 1 à chaque fois ou doit avoir une continuité sur plusieurs fichiers #include .

et si c’est quelque chose qui mériterait d’être standardisé?

__COUNTER__ , contrairement à __LINE__ , est très dangereux car il dépend des fichiers d’en-tête inclus et de leur ordre. Si deux fichiers .cpp (unités de traduction) incluent un fichier d’en-tête utilisant __COUNTER__ , mais que le fichier d’en-tête obtient différentes séquences de comptage dans les différentes instances, ils peuvent utiliser différentes définitions de la même chose et violer la règle à une définition.

Les violations de règle à une définition sont très difficiles à détecter et peuvent potentiellement créer des bogues et des risques de sécurité. Les quelques cas d’ __COUNTER__ de __COUNTER__ ne __COUNTER__ pas vraiment les inconvénients et le manque d’évolutivité.

Même si vous __COUNTER__ jamais de code utilisant __COUNTER__ , cela peut être utile lors du prototypage d’une séquence d’énumération, ce qui vous évite d’atsortingbuer des noms avant que l’appartenance ne soit concrète.

Si je comprends bien la fonctionnalité, je souhaitais avoir cette fonctionnalité lorsque je travaillais en Perl, en ajoutant une fonction de journalisation des événements dans une interface graphique existante. Je voulais m’assurer que les tests manuels nécessaires (soupir) nous donnaient une couverture complète, alors j’ai consigné chaque sharepoint test dans un fichier et l’enregistrement d’une valeur __counter__ facilitait la visualisation de ce qui manquait dans la couverture. En l’état, j’ai codé l’équivalent.

Il est utilisé par Boost.Asio pour implémenter des coroutines sans stack.

Voir ce fichier d’en-tête et exemples .

Les coroutines résultantes ressemblent à ceci:

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

Je l’ai utilisé pour générer des types uniques dans cet article: http://www.codeproject.com/Articles/42021/Sealing-Classes-in-C

J’ai l’intention d’utiliser __COUNTER__ pour donner à chaque fichier de notre base de code un identifiant unique, de sorte que ce code unique puisse être utilisé pour consigner les ASSERT dans un système intégré.

Cette méthode est beaucoup plus efficace que l’utilisation de chaînes pour stocker les noms de fichiers (à l’aide de __FILE__ ), en particulier sur un système intégré avec une petite ROM. J’ai réfléchi à cette idée pendant que je lisais cet article – Affirmez-vous sur Embedded.com. Il est dommage que cela fonctionne uniquement avec les compilateurs basés sur GCC.

J’utilise cette variable pour introduire une certaine entropie dans un PRNG (Pseudo Random Number Generator). Chaque fois que j’appelle le PRNG, je peux fournir une entropie en utilisant cette variable. À long terme, si les appels au PRNG sont aléatoires car cela dépend des actions de l’utilisateur, une certaine entropie sera ajoutée au générateur.

__COUNTER__ est garanti unique contrairement à __LINE__ . Certains compilateurs permettent de réinitialiser __LINE__ . Les fichiers #include réinitialiseront également __LINE__ .

Une utilisation est dans la macro REGISTER_KERNEL_BUILDER de TensorFlow . Chaque TensorFlow Op pourrait avoir un ou plusieurs kernelx comme implémentations. Ces kernelx sont enregistrés auprès d’un bureau d’enregistrement. L’enregistrement d’un kernel se fait en définissant une variable globale – le constructeur de la variable peut effectuer l’enregistrement. Ici, les auteurs utilisent __COUNTER__ pour donner à chaque variable globale un nom unique.

 #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); \ }); 

Il est utilisé dans le système de mésortingques de ClickHouse.

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