__COUNTER__
符号由VC++和GCC提供,每次使用时都会增加非负积分值。
我有兴趣了解是否有人曾经使用它,以及它是否值得标准化?
答案 0 :(得分:44)
__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 );
// ...
}
但是不要挂断我调用对象锁的事实 - 任何需要在匹配对中调用的函数都适合这种模式。您甚至可能在给定块中的同一“锁定”上有多种用途。
答案 1 :(得分:13)
答案 2 :(得分:11)
除了DEBUG宏之外,我从未使用它。能够说
很方便#define WAYPOINT \
do { if(dbg) printf("At marker: %d\n", __COUNTER__); } while(0);
答案 3 :(得分:10)
它在xCover代码覆盖库中用于标记执行所经过的行,以查找未覆盖的行。
答案 4 :(得分:7)
我有兴趣了解是否有人使用过它,
是的,但正如您可以从本Q& A中的许多示例中看到的那样,标准化的__LINE__
在大多数情况下也足够了。
__COUNTER__
只有在每次计数必须增加一个的情况下才真正有必要,或者它必须在几个#include
个文件中具有连续性。
与以及它是否值得标准化?
__COUNTER__
不同, __LINE__
非常危险,因为它取决于包含哪些头文件以及顺序。如果两个.cpp
文件(翻译单元)包含使用__COUNTER__
的头文件,但头文件在不同实例中获得不同的计数序列,则它们可能使用相同内容的不同定义并违反 - 定义规则。
单定义规则违规很难捕获并可能产生错误和安全风险。 __COUNTER__
的少数用例确实超过了缺点和缺乏可扩展性。
即使您从未发送使用__COUNTER__
的代码,在对枚举序列进行原型设计时也很有用,这样可以省去在成员资格具体化之前分配名称的麻烦。
答案 5 :(得分:3)
如果我正确理解了这个功能,我希望我在Perl工作时有这个功能,在现有的GUI中添加一个Event Logging功能。我想确保所需的手测试(叹气)给我们完全覆盖,因此我将每个测试点记录到一个文件中,并且记录__counter__
值使得查看覆盖范围中缺少的内容变得容易。事实上,我手工编写了相应的代码。
答案 6 :(得分:2)
Boost.Asio使用它来实现无堆栈协程。
请参阅此header file和examples。
结果协程看起来像这样:
struct task : coroutine
{
...
void operator()()
{
reenter (this)
{
while (... not finished ...)
{
... do something ...
yield;
... do some more ...
yield;
}
}
}
...
};
答案 7 :(得分:2)
我在面向对象的游戏中使用 __COUNTER__
自动生成实体和碰撞体的类型 ID。
这个游戏使用多态来实现它的功能。为了序列化子对象,我必须想出一种方法来存储实体子类型并序列化/反序列化它们以进行场景保存和加载。从保存文件中读取实体(反序列化)时,我需要知道期望读取哪些属性;使用 __COUNTER__
,我为每个实体类都有一个唯一且恒定的 ID,并且可以使用此 ID 作为正确的实体类型加载它们。
这种方法意味着要使新的实体类型可序列化,我只需在构造函数中添加 typeID = __COUNTER__;
以覆盖默认 ID。在雪碧的情况下:
Sprite(/* TODO: Sprite Arguments */) : Entity(/* TODO: Entity Arguments */) {
typeID = __COUNTER__;
}
... 并继续概述其 iostream 重载:
friend std::ostream& operator<<(std::ostream& os, const Sprite& rhs) {
return os << /* TODO: Outline Output */;
}
friend std::istream& operator>>(std::istream& is, Sprite& rhs) {
return is >> /* TODO: Outline Input */;
}
这是为您的类生成类型 ID 的一种非常轻量级的方法,并且避免了一堆复杂的逻辑。作为预处理器命令,它非常基础,但它为某些关键设备提供了有用的工具。
注意:如果您想在调用计数器时将 ID 值重新设置为 0,请在生成第一个 ID 时存储其值,并用该值减去所有后续 ID。
感谢阅读! -YZM
答案 8 :(得分:1)
我在本文中使用它来生成唯一类型: http://www.codeproject.com/Articles/42021/Sealing-Classes-in-C
答案 9 :(得分:1)
__COUNTER__
在运行时对字符串进行加密并且希望每个字符串都有唯一的密钥时非常有用,而无需在加密密钥的某个地方存储计数器,可以使用Counter来确保每个字符串有它自己的唯一密钥!。
我在XorString
1标头库中使用它,该标头库在运行时解密字符串,因此,如果有任何黑客/骇客试图查看我的二进制文件,它们将不会在其中找到字符串,但是当程序运行时运行时,每个字符串都会解密并正常显示。
#pragma once
#include <string>
#include <array>
#include <cstdarg>
#define BEGIN_NAMESPACE( x ) namespace x {
#define END_NAMESPACE }
BEGIN_NAMESPACE(XorCompileTime)
constexpr auto time = __TIME__;
constexpr auto seed = static_cast< int >(time[7]) + static_cast< int >(time[6]) * 10 + static_cast< int >(time[4]) * 60 + static_cast< int >(time[3]) * 600 + static_cast< int >(time[1]) * 3600 + static_cast< int >(time[0]) * 36000;
// 1988, Stephen Park and Keith Miller
// "Random Number Generators: Good Ones Are Hard To Find", considered as "minimal standard"
// Park-Miller 31 bit pseudo-random number generator, implemented with G. Carta's optimisation:
// with 32-bit math and without division
template < int N >
struct RandomGenerator
{
private:
static constexpr unsigned a = 16807; // 7^5
static constexpr unsigned m = 2147483647; // 2^31 - 1
static constexpr unsigned s = RandomGenerator< N - 1 >::value;
static constexpr unsigned lo = a * (s & 0xFFFF); // Multiply lower 16 bits by 16807
static constexpr unsigned hi = a * (s >> 16); // Multiply higher 16 bits by 16807
static constexpr unsigned lo2 = lo + ((hi & 0x7FFF) << 16); // Combine lower 15 bits of hi with lo's upper bits
static constexpr unsigned hi2 = hi >> 15; // Discard lower 15 bits of hi
static constexpr unsigned lo3 = lo2 + hi;
public:
static constexpr unsigned max = m;
static constexpr unsigned value = lo3 > m ? lo3 - m : lo3;
};
template <>
struct RandomGenerator< 0 >
{
static constexpr unsigned value = seed;
};
template < int N, int M >
struct RandomInt
{
static constexpr auto value = RandomGenerator< N + 1 >::value % M;
};
template < int N >
struct RandomChar
{
static const char value = static_cast< char >(1 + RandomInt< N, 0x7F - 1 >::value);
};
template < size_t N, int K, typename Char >
struct XorString
{
private:
const char _key;
std::array< Char, N + 1 > _encrypted;
constexpr Char enc(Char c) const
{
return c ^ _key;
}
Char dec(Char c) const
{
return c ^ _key;
}
public:
template < size_t... Is >
constexpr __forceinline XorString(const Char* str, std::index_sequence< Is... >) : _key(RandomChar< K >::value), _encrypted{ enc(str[Is])... }
{
}
__forceinline decltype(auto) decrypt(void)
{
for (size_t i = 0; i < N; ++i) {
_encrypted[i] = dec(_encrypted[i]);
}
_encrypted[N] = '\0';
return _encrypted.data();
}
};
//--------------------------------------------------------------------------------
//-- Note: XorStr will __NOT__ work directly with functions like printf.
// To work with them you need a wrapper function that takes a const char*
// as parameter and passes it to printf and alike.
//
// The Microsoft Compiler/Linker is not working correctly with variadic
// templates!
//
// Use the functions below or use std::cout (and similar)!
//--------------------------------------------------------------------------------
static auto w_printf = [](const char* fmt, ...) {
va_list args;
va_start(args, fmt);
vprintf_s(fmt, args);
va_end(args);
};
static auto w_printf_s = [](const char* fmt, ...) {
va_list args;
va_start(args, fmt);
vprintf_s(fmt, args);
va_end(args);
};
static auto w_sprintf = [](char* buf, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
vsprintf(buf, fmt, args);
va_end(args);
};
static auto w_sprintf_ret = [](char* buf, const char* fmt, ...) {
int ret;
va_list args;
va_start(args, fmt);
ret = vsprintf(buf, fmt, args);
va_end(args);
return ret;
};
static auto w_sprintf_s = [](char* buf, size_t buf_size, const char* fmt, ...) {
va_list args;
va_start(args, fmt);
vsprintf_s(buf, buf_size, fmt, args);
va_end(args);
};
static auto w_sprintf_s_ret = [](char* buf, size_t buf_size, const char* fmt, ...) {
int ret;
va_list args;
va_start(args, fmt);
ret = vsprintf_s(buf, buf_size, fmt, args);
va_end(args);
return ret;
};
//Old functions before I found out about wrapper functions.
//#define XorStr( s ) ( XorCompileTime::XorString< sizeof(s)/sizeof(char) - 1, __COUNTER__, char >( s, std::make_index_sequence< sizeof(s)/sizeof(char) - 1>() ).decrypt() )
//#define XorStrW( s ) ( XorCompileTime::XorString< sizeof(s)/sizeof(wchar_t) - 1, __COUNTER__, wchar_t >( s, std::make_index_sequence< sizeof(s)/sizeof(wchar_t) - 1>() ).decrypt() )
//Wrapper functions to work in all functions below
#define XorStr( s ) []{ constexpr XorCompileTime::XorString< sizeof(s)/sizeof(char) - 1, __COUNTER__, char > expr( s, std::make_index_sequence< sizeof(s)/sizeof(char) - 1>() ); return expr; }().decrypt()
#define XorStrW( s ) []{ constexpr XorCompileTime::XorString< sizeof(s)/sizeof(wchar_t) - 1, __COUNTER__, wchar_t > expr( s, std::make_index_sequence< sizeof(s)/sizeof(wchar_t) - 1>() ); return expr; }().decrypt()
END_NAMESPACE
答案 10 :(得分:0)
__COUNTER__
不同, __LINE__
保证是唯一的。某些编译器允许重置__LINE__
。 #include文件也会重置__LINE__
。
答案 11 :(得分:0)
用法在TensorFlow's REGISTER_KERNEL_BUILDER
macro。每个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); \
});
答案 12 :(得分:0)
它用于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; }
}
答案 13 :(得分:0)
在我们的代码中,我们忘记为某些产品添加测试用例。我现在实现了一些宏,因此我们可以在编译时断言对于要添加或删除的每个产品都有测试用例。
答案 14 :(得分:0)
我已将其用于驱动程序填充程序层,在此我需要确保至少启用了一个物理驱动程序。
例如:
#if defined( USE_DRIVER1 )
#include "driver1.h"
int xxx1 = __COUNTER__;
#endif
#if defined( USE_DRIVER2 )
#include "driver2.h"
int xxx2 = __COUNTER__;
#endif
#if __COUNTER__ < 1
#error Must enable at least one driver.
#endif
答案 15 :(得分:0)
在此blog post中,它用于模拟C ++ 11中golang的defer
语句。
template <typename F>
struct privDefer {
F f;
privDefer(F f) : f(f) {}
~privDefer() { f(); }
};
template <typename F>
privDefer<F> defer_func(F f) {
return privDefer<F>(f);
}
#define DEFER_1(x, y) x##y
#define DEFER_2(x, y) DEFER_1(x, y)
#define DEFER_3(x) DEFER_2(x, __COUNTER__)
#define defer(code) auto DEFER_3(_defer_) = defer_func([&](){code;})
然后您可以做:
int main()
{
FILE* file = open("file.txt");
defer(fclose(file));
// use the file here
// ....
}
答案 16 :(得分:0)
我打算使用__COUNTER__
为我们的代码库中的每个文件提供唯一标识符,以便该唯一代码可用于在嵌入式系统中记录ASSERT。
此方法比使用字符串存储文件名(使用__FILE__
)更有效,尤其是在具有微小ROM的嵌入式系统上。当我在Embedded.com上阅读这篇文章时,我想到了这个想法 - Assert Yourself。遗憾的是,它只适用于基于GCC的编译器。
答案 17 :(得分:0)
我发现它对于在 UI 中显示步骤很有用。这使得添加、删除或重新排序步骤变得非常容易,而无需担心步骤被错误标记。
#include <iostream>
#define STR_IMPL(s) #s
#define STR(s) STR_IMPL(s)
#define STEP STR(__COUNTER__) ": "
int main()
{
std::cout
<< STEP "foo\n"
<< STEP "bar\n"
<< STEP "qux\n"
;
}
输出:
0: foo
1: bar
2: qux
让它从 1 而不是 0 开始作为练习。