Has anyone ever had a use for the __COUNTER__ pre-processor macro?

前端 未结 16 1380
有刺的猬
有刺的猬 2020-11-29 00:37

The __COUNTER__ symbol is provided by VC++ and GCC, and gives an increasing non-negative integral value each time it is used.

I\'m interested to learn w

相关标签:
16条回答
  • 2020-11-29 00:41

    I've never used it for anything but a DEBUG macro. It's convenient to be able to say

    #define WAYPOINT \
        do { if(dbg) printf("At marker: %d\n", __COUNTER__); } while(0);
    
    0 讨论(0)
  • 2020-11-29 00:46

    I've used it in a compile-time assertion macro to have the macro create a name for a typedef that will be unique. See

    • Ways to ASSERT expressions at build time in C

    if you want the gory details.

    0 讨论(0)
  • 2020-11-29 00:46

    It's used in ClickHouse's metrics system.

    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; }
    }
    
    0 讨论(0)
  • 2020-11-29 00:49

    __COUNTER__ is useful anywhere you need a unique name. I have used it extensively for RAII style locks and stacks. Consider:

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

    It gets tedious to name the lock uses, and can even become a source of errors if they're not all declared at the top of a block. How do you know if you're on lock_use4 or lock_use11? It's also needless pollution of the namespace - I never need to refer to the lock use objects by name. So I use __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 );
      // ...
    }
    

    But don't get hung up on the fact I called the objects locks - any function(s) that need to get called in matching pairs fit this pattern. You might even have multiple uses on the same "lock" in a given block.

    0 讨论(0)
  • 2020-11-29 00:51

    It's used in the xCover code coverage library, to mark the lines that execution passes through, to find ones that are not covered.

    0 讨论(0)
  • 2020-11-29 00:52

    I'm interested to learn whether anyone's ever used it,

    Yes, but as you can see from many examples in this Q&A, __LINE__, which is standardized, would also be sufficient in most cases.

    __COUNTER__ is only really necessary in cases where the count must increase by one each time, or it must have continuity over several #include files.

    and whether it's something that would be worth standardising?

    __COUNTER__, unlike __LINE__, is very dangerous because it depends on which header files are included and what order. If two .cpp files (translation units) include a header file that use __COUNTER__, but the header file obtains different count sequences in the different instances, they may use different definitions of the same thing and violate the one-definition rule.

    One-definition rule violations are very difficult to catch and potentially create bugs and security risks. The few use-cases of __COUNTER__ don't really outweigh the downside and lack of scalability.

    Even if you never ship code that uses __COUNTER__, it can be useful when prototyping an enumeration sequence, saving you the trouble of assigning names before the membership is concrete.

    0 讨论(0)
提交回复
热议问题