Ответ 1
Он используется в xCover библиотеке покрытия кода, чтобы пометить строки, выполнение которых проходит, чтобы найти те, которые не покрыты.
Символ __COUNTER__
предоставляется VС++ и GCC и дает все возрастающее неотрицательное целочисленное значение каждый раз, когда он используется.
Мне интересно узнать, кто-нибудь когда-либо использовал его, и будет ли это что-то, что стоит стандартизировать?
Он используется в xCover библиотеке покрытия кода, чтобы пометить строки, выполнение которых проходит, чтобы найти те, которые не покрыты.
__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 );
// ...
}
Но не зацикливайтесь на том, что я вызывал блокировки объектов - любая функция (ы), которая должна быть вызвана в соответствующие пары, соответствует этому шаблону. Возможно, у вас может быть несколько применений в одной и той же "блокировке" в данном блоке.
Я использовал его в макросе утверждения времени компиляции, чтобы макрос создал имя для typedef, которое будет уникальным. См.
если вы хотите детали gory.
Я никогда не использовал его ни для чего, кроме макроса DEBUG. Удобно говорить
#define WAYPOINT \
do { if(dbg) printf("At marker: %d\n", __COUNTER__); } while(0);
Мне интересно узнать, кто-нибудь когда-либо использовал его,
Да, но, как вы можете видеть из многих примеров в этом стандарте, Q & A, __LINE__
, также будет достаточным в большинстве случаев.
__COUNTER__
действительно необходим только в случаях, когда счетчик должен увеличиваться на единицу каждый раз или должен иметь непрерывность в нескольких файлах #include
.
и будет ли это что-то, что стоит стандартизировать?
__COUNTER__
, в отличие от __LINE__
, очень опасен, потому что зависит от того, какие файлы заголовков включены и какой порядок. Если два файла .cpp
(единицы перевода) включают заголовочный файл, который использует __COUNTER__
, но заголовочный файл получает разные последовательности отсчетов в разных экземплярах, они могут использовать разные определения одной и той же вещи и нарушать одно правило определения.
Нарушения правил с одним определением очень сложно поймать и потенциально создать ошибки и риски безопасности. Несколько вариантов использования __COUNTER__
на самом деле не перевешивают недостаток и отсутствие масштабируемости.
Даже если вы никогда не отправляете код, который использует __COUNTER__
, он может быть полезен при прототипировании последовательности перечислений, что избавит вас от необходимости назначать имена до того, как членство будет конкретным.
Если я правильно понимаю функциональность, я хотел, чтобы у меня была такая функциональность, когда я работал в Perl, добавив функцию регистрации событий в существующий графический интерфейс. Я хотел убедиться, что необходимое ручное тестирование (вздох) дало нам полный охват, поэтому я зарегистрировал каждую тестовую точку в файле, и запись значения __counter__
позволила легко увидеть, чего не хватает в покрытии. Как бы то ни было, я передал закодированный эквивалент.
Я использовал его для создания уникальных типов в этой статье: http://www.codeproject.com/Articles/42021/Sealing-Classes-in-C
Он используется Boost.Asio для реализации стекированных сопрограмм.
Файл заголовка: http://www.boost.org/doc/libs/1_61_0/boost/asio/coroutine.hpp
Пример можно найти здесь: http://www.boost.org/doc/libs/1_61_0/doc/html/boost_asio/example/cpp03/http/server4/request_parser.cpp
Я намерен использовать __COUNTER__
, чтобы дать каждому файлу в нашей базе кода уникальный идентификатор, чтобы этот уникальный код можно было использовать при регистрации ASSERT во встроенной системе.
Этот метод намного эффективнее, чем использование строк для хранения имен файлов (с использованием __FILE__
), особенно во встроенной системе с маленьким ROM. Я думал об этой идее, пока я читал эту статью - Assert Yourself на Embedded.com. Позор, что он работает только с компиляторами на основе GCC.
Я использую эту переменную, чтобы ввести некоторую энтропию в PRNG (генератор псевдослучайных чисел). Каждый раз, когда я вызываю PRNG, я могу предоставить некоторую энтропию, используя эту переменную. В конечном счете, если вызовы PRNG являются случайными, поскольку они зависят от действий пользователя, в генератор будет добавлена некоторая энтропия.
__COUNTER__
гарантированно будет уникальным, в отличие от __LINE__
. Некоторые компиляторы позволяют __LINE__
быть reset. #include файлы также будут reset __LINE__
.
Использование макрос TensorFlow REGISTER_KERNEL_BUILDER
. Каждый TensorFlow Op может иметь одно или несколько ядер в качестве своих реализаций. Эти ядра регистрируются у регистратора. Регистрация ядра выполняется путем определения глобальной переменной - конструктор переменной может выполнить регистрацию. Здесь авторы используют __COUNTER__
, чтобы дать каждой глобальной переменной уникальное имя.
#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); \
});
Используется в метрической системе 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; }
}