Кто-нибудь когда-либо использовал макрос препроцессора __COUNTER__? - PullRequest
46 голосов
/ 17 марта 2009

Символ __COUNTER__ предоставляется VC ++ и GCC и дает возрастающее неотрицательное целое значение при каждом его использовании.

Мне интересно узнать, использовал ли кто-нибудь это когда-нибудь и стоит ли это стандартизировать?

Ответы [ 13 ]

45 голосов
/ 18 августа 2009

__COUNTER__ полезен везде, где вам нужно уникальное имя. Я широко использовал его для замков и стеков в стиле RAII. Рассмотрим:

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

Называть используемые блокировки становится утомительно и даже может стать источником ошибок, если они не все объявлены в верхней части блока. Как вы знаете, если вы на lock_use4 или lock_use11? Это также излишнее загрязнение пространства имен - мне никогда не нужно ссылаться на объекты использования блокировки по имени. Поэтому я использую __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 );
  // ...
}

Но не зацикливайтесь на том факте, что я назвал блокировки объектов - любая функция (и), которую нужно вызывать в совпадающих парах, соответствует этому шаблону. Вы можете даже использовать несколько раз одну и ту же «блокировку» в данном блоке.

13 голосов
/ 17 марта 2009

Я использовал его в макросе утверждения времени компиляции, чтобы макрос создавал имя для typedef, которое будет уникальным. Смотри

если хочешь подробностей.

12 голосов
/ 17 марта 2009

Я никогда не использовал его ни для чего, кроме макроса DEBUG. Удобно иметь возможность сказать

#define WAYPOINT \
    do { if(dbg) printf("At marker: %d\n", __COUNTER__); } while(0);
11 голосов
/ 10 мая 2009

Используется в библиотеке покрытия кода xCover , чтобы пометить строки, через которые проходит выполнение, чтобы найти те, которые не охвачены.

7 голосов
/ 25 января 2015

Мне интересно узнать, использовал ли кто-нибудь это когда-либо,

Да, но, как вы можете видеть из многих примеров в этих вопросах и ответах, __LINE__, который стандартизирован, также будет достаточным в большинстве случаев.

__COUNTER__ действительно необходимо только в тех случаях, когда число должно увеличиваться на единицу каждый раз или оно должно иметь непрерывность по нескольким #include файлам.

а стоит ли это что-то стандартизировать?

__COUNTER__, в отличие от __LINE__, очень опасен, поскольку зависит от того, какие заголовочные файлы включены и в каком порядке. Если два .cpp файла (единицы перевода) включают файл заголовка, который использует __COUNTER__, но файл заголовка получает разные последовательности подсчета в разных экземплярах, они могут использовать разные определения одного и того же и нарушать правило одного определения.

Нарушения правил с одним определением очень трудно уловить, что может привести к ошибкам и угрозам безопасности. Несколько вариантов использования __COUNTER__ на самом деле не перевешивают недостатки и отсутствие масштабируемости.

Даже если вы никогда не отправляете код, который использует __COUNTER__, это может быть полезно при создании прототипа последовательности перечисления, избавляя вас от необходимости присваивать имена до того, как членство станет конкретным.

3 голосов
/ 17 марта 2009

Если я правильно понимаю функциональность, мне хотелось бы, чтобы она была у меня, когда я работал в Perl, добавив функцию регистрации событий в существующий графический интерфейс. Я хотел убедиться, что необходимое ручное тестирование (вздох) дало нам полное покрытие, поэтому я записывал каждую контрольную точку в файл, а запись значения __counter__ позволяла легко увидеть, что отсутствовало в покрытии. Как это было, я вручную закодировал эквивалент.

2 голосов
/ 16 июля 2016

Используется Boost.Asio для реализации сопрограмм без стеков.

См. Этот заголовочный файл и примеры .

Результирующие сопрограммы выглядят так:

struct task : coroutine
{
  ...
  void operator()()
  {
    reenter (this)
    {
      while (... not finished ...)
      {
         ... do something ...
         yield;
         ... do some more ...
         yield;
       }
     }
   }
   ...
};
1 голос
/ 27 февраля 2014

Я использовал его для генерации уникальных типов в этой статье: http://www.codeproject.com/Articles/42021/Sealing-Classes-in-C

0 голосов
/ 10 октября 2018

В нашем коде мы забыли добавить тестовые сценарии для некоторых наших продуктов. Теперь я реализовал несколько макросов, чтобы мы могли во время компиляции утверждать, что у нас есть тестовые сценарии для каждого продукта, который мы добавляем или удаляем.

0 голосов
/ 13 декабря 2017

Используется в системе метрик 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; }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...