from - use c lib in c++




Quelqu'un a-t-il déjà utilisé la macro__COUNTER__ pré-processeur? (9)

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é de savoir si quelqu'un l'a déjà utilisé, et si c'est quelque chose qui mériterait d'être normalisé?


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

Oui, mais comme vous pouvez le constater à partir de nombreux exemples dans ce Q & A, __LINE__ , qui est normalisé, serait également suffisant dans la plupart des cas.

__COUNTER__ n'est vraiment nécessaire que dans les cas où le nombre doit augmenter d'une fois ou qu'il doit avoir une continuité sur plusieurs fichiers #include .

et si c'est quelque chose qui mériterait d'être normalisé?

__COUNTER__ , contrairement à __LINE__ , est très dangereux car cela 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 qui utilise __COUNTER__ , mais que le fichier d’en-tête obtient des séquences de comptage différentes dans les différentes instances, ils peuvent utiliser différentes définitions de la même chose et enfreindre la règle de définition unique.

Les violations de règles à définition unique sont très difficiles à détecter et peuvent créer des bogues et des risques pour la sécurité. Les quelques cas d' __COUNTER__ de __COUNTER__ ne l'emportent pas sur les inconvénients et le manque d'évolutivité.

Même si vous ne __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'avoir à assigner des noms avant que l'appartenance ne soit concrète.


Dans notre code, nous avons oublié d'ajouter des cas de test pour certains de nos produits. J'ai maintenant mis en place des macros afin de pouvoir affirmer au moment de la compilation que nous avons des cas de test pour chaque produit que nous ajoutons ou supprimons.


Il est utilisé dans le système de mesures 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; }
}

Il est utilisé par Boost.Asio pour implémenter des routines sans pile.

Voir ce fichier d'en-tête et des examples .

Les coroutines résultantes ressemblent à ceci:

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



Si je comprends bien la fonctionnalité, j'aurais souhaité disposer de cette fonctionnalité lorsque je travaillais dans 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 __counter__ une couverture complète. J'ai donc consigné chaque point de test dans un fichier, et la consignation d'une valeur __counter__ permettait de voir facilement ce qui manquait dans la couverture. En l'état, j'ai codé à la main l'équivalent.


Une utilisation est dans la macro REGISTER_KERNEL_BUILDER de TensorFlow . Chaque implémentation de TensorFlow Op peut avoir un ou plusieurs noyaux. Ces noyaux sont enregistrés auprès d'un registraire. L’enregistrement d’un noyau se fait en définissant une variable globale - le constructeur de la variable peut effectuer l’enregistrement. Ici, les auteurs utilisent __COUNTER__ pour attribuer un nom unique à chaque variable globale.

#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__ est utile partout où vous avez besoin d'un nom unique. Je l'ai largement utilisé pour les serrures et les piles 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 est fastidieux de nommer les verrous utilisés et peut même devenir une source d’erreurs s’ils ne sont pas tous déclarés au sommet d’un bloc. Comment savoir 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 faire référence aux objets d'utilisation des verrous 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 objets verrouillés - toute fonction qui doit être appelée par paires identiques correspond à ce modèle. Vous pouvez même avoir plusieurs utilisations sur le même "verrou" dans un bloc donné.





c