C ++是否支持编译时计数器?

为了内省的目的,有时候我想自动分配序列号给types,或类似的东西。

不幸的是,模板元编程基本上是一种function性语言,因此缺乏全局variables或可修改的状态来实现这样的计数器。

还是呢?


请求示例代码:

#include <iostream> int const a = counter_read; counter_inc; counter_inc; counter_inc; counter_inc; counter_inc; int const b = counter_read; int main() { std::cout << a << ' ' << b << '\n'; // print "0 5" counter_inc_t(); counter_inc_t(); counter_inc_t(); std::cout << counter_read << '\n'; // print "8" struct { counter_inc_t d1; char x[ counter_read ]; counter_inc_t d2; char y[ counter_read ]; } ls; std::cout << sizeof ls.x << ' ' << sizeof ls.y << '\n'; // print "9 10" } 

嗯…是的,模板元编程缺乏副作用,因为它是有意的。 我被GCC旧版本中的一个错误所迷惑,标准中的一些不太明确的措辞让我相信所有这些特性都是可能的。

但是,至less名称空间范围function可以通过less量使用模板来实现。 函数查找可以从已声明的函数集中提取数值状态,如下所示。

图书馆编号:

 template< size_t n > // This type returns a number through function lookup. struct cn // The function returns cn<n>. { char data[ n + 1 ]; }; // The caller uses (sizeof fn() - 1). template< typename id, size_t n, size_t acc > cn< acc > seen( id, cn< n >, cn< acc > ); // Default fallback case. /* Evaluate the counter by finding the last defined overload. Each function, when defined, alters the lookup sequence for lower-order functions. */ #define counter_read( id ) \ ( sizeof seen( id(), cn< 1 >(), cn< \ ( sizeof seen( id(), cn< 2 >(), cn< \ ( sizeof seen( id(), cn< 4 >(), cn< \ ( sizeof seen( id(), cn< 8 >(), cn< \ ( sizeof seen( id(), cn< 16 >(), cn< \ ( sizeof seen( id(), cn< 32 >(), cn< 0 \ /* Add more as desired; trimmed for Stack Overflow code block. */ \ >() ).data - 1 ) \ >() ).data - 1 ) \ >() ).data - 1 ) \ >() ).data - 1 ) \ >() ).data - 1 ) \ >() ).data - 1 ) /* Define a single new function with place-value equal to the bit flipped to 1 by the increment operation. This is the lowest-magnitude function yet undefined in the current context of defined higher-magnitude functions. */ #define counter_inc( id ) \ cn< counter_read( id ) + 1 > \ seen( id, cn< ( counter_read( id ) + 1 ) & ~ counter_read( id ) >, \ cn< ( counter_read( id ) + 1 ) & counter_read( id ) > ) 

快速演示( 查看运行 ):

 struct my_cnt {}; int const a = counter_read( my_cnt ); counter_inc( my_cnt ); counter_inc( my_cnt ); counter_inc( my_cnt ); counter_inc( my_cnt ); counter_inc( my_cnt ); int const b = counter_read( my_cnt ); counter_inc( my_cnt ); #include <iostream> int main() { std::cout << a << ' ' << b << '\n'; std::cout << counter_read( my_cnt ) << '\n'; } 

C ++ 11更新

这里是使用C ++ 11 constexpr代替sizeof的更新版本。

 #define COUNTER_READ_CRUMB( TAG, RANK, ACC ) counter_crumb( TAG(), constant_index< RANK >(), constant_index< ACC >() ) #define COUNTER_READ( TAG ) COUNTER_READ_CRUMB( TAG, 1, COUNTER_READ_CRUMB( TAG, 2, COUNTER_READ_CRUMB( TAG, 4, COUNTER_READ_CRUMB( TAG, 8, \ COUNTER_READ_CRUMB( TAG, 16, COUNTER_READ_CRUMB( TAG, 32, COUNTER_READ_CRUMB( TAG, 64, COUNTER_READ_CRUMB( TAG, 128, 0 ) ) ) ) ) ) ) ) #define COUNTER_INC( TAG ) \ constexpr \ constant_index< COUNTER_READ( TAG ) + 1 > \ counter_crumb( TAG, constant_index< ( COUNTER_READ( TAG ) + 1 ) & ~ COUNTER_READ( TAG ) >, \ constant_index< ( COUNTER_READ( TAG ) + 1 ) & COUNTER_READ( TAG ) > ) { return {}; } #define COUNTER_LINK_NAMESPACE( NS ) using NS::counter_crumb; template< std::size_t n > struct constant_index : std::integral_constant< std::size_t, n > {}; template< typename id, std::size_t rank, std::size_t acc > constexpr constant_index< acc > counter_crumb( id, constant_index< rank >, constant_index< acc > ) { return {}; } // found by ADL via constant_index 

http://ideone.com/yp19oo

这些声明应该放在一个名字空间中,除了counter_crumb之外的所有名字都应该是完全限定的。 counter_crumb模板通过ADL与constant_indextypes关联。

COUNTER_LINK_NAMESPACEmacros可用于在多个名称空间范围内增加一个计数器。

我相信,MSVC和GCC都支持一个__COUNTER__预处理器标记,它在其位置上具有单调递增的值。

我想在相当长一段时间内解决这个问题,并提出了一个非常简洁的解决scheme。 至less我应该得到一个upvote来尝试这一点。 :))

以下库代码实现了名称空间级别的function。 即我成功实现了counter_readcounter_inc ; 但不是counter_inc_t (在函数内部增加,因为template类不允许在函数内)

 template<unsigned int NUM> struct Counter { enum { value = Counter<NUM-1>::value }; }; template<> struct Counter<0> { enum { value = 0 }; }; #define counter_read Counter<__LINE__>::value #define counter_inc template<> struct Counter<__LINE__> { enum { value = Counter<__LINE__-1>::value + 1}; } 

此技术使用模板元编程并利用__LINE__macros。 从您的答案查看代码的结果。

您可以使用Boost.Preprocessor中的BOOST_PP_COUNTER

优点:它甚至可以用于macros

缺点:整个项目只有一个“反”的types,但是这个机制可能会被专门的柜台重新实现

这是另一个替代实现。 https://stackoverflow.com/a/6174263/1190123可能是更好的,但即使手动工作通过在纸上几个增量我仍然不完全理解math/过滤。;

这使用constexpr函数recursion来计算非模板声明的Highest函数的数量。 __COUNTER__被用作__COUNTER__机制来阻止Highest __COUNTER__自我recursion的新声明。

这只是为我编纂(3.3)。 我不确定它是否合规,但我很有希望。 由于某些未实现的function(根据错误),g ++ 4.8失败。 英特尔编译器13也失败,由于constexpr错误。

256级计数器

每个计数器的最大计数是250(CounterLimit)。 CounterLimit可以增加到256,除非你实现下面的LCount的东西。

履行

 #include <iostream> #include <type_traits> constexpr unsigned int CounterLimit = 250; template <unsigned int ValueArg> struct TemplateInt { constexpr static unsigned int Value = ValueArg; }; template <unsigned int GetID, typename, typename TagID> constexpr unsigned int Highest(TagID, TemplateInt<0>) { return 0; } template <unsigned int GetID, typename, typename TagID, unsigned int Index> constexpr unsigned int Highest(TagID, TemplateInt<Index>) { return Highest<GetID, void>(TagID(), TemplateInt<Index - 1>()); } #define GetCount(...) \ Highest<__COUNTER__, void>(__VA_ARGS__(), TemplateInt<CounterLimit>()) #define IncrementCount(TagID) \ template <unsigned int GetID, typename = typename std::enable_if<(GetID > __COUNTER__ + 1)>::type> \ constexpr unsigned int Highest( \ TagID, \ TemplateInt<GetCount(TagID) + 1> Value) \ { \ return decltype(Value)::Value; \ } 

testing

 struct Counter1 {}; struct Counter2 {}; constexpr unsigned int Read0 = GetCount(Counter1); constexpr unsigned int Read1 = GetCount(Counter1); IncrementCount(Counter1); constexpr unsigned int Read2 = GetCount(Counter1); IncrementCount(Counter1); constexpr unsigned int Read3 = GetCount(Counter1); IncrementCount(Counter1); constexpr unsigned int Read4 = GetCount(Counter1); IncrementCount(Counter1); IncrementCount(Counter2); constexpr unsigned int Read5 = GetCount(Counter1); constexpr unsigned int Read6 = GetCount(Counter2); int main(int, char**) { std::cout << "Ending state 0: " << Highest<__COUNTER__, void>(Counter1(), TemplateInt<0>()) << std::endl; std::cout << "Ending state 1: " << Highest<__COUNTER__, void>(Counter1(), TemplateInt<1>()) << std::endl; std::cout << "Ending state 2: " << Highest<__COUNTER__, void>(Counter1(), TemplateInt<2>()) << std::endl; std::cout << "Ending state 3: " << Highest<__COUNTER__, void>(Counter1(), TemplateInt<3>()) << std::endl; std::cout << "Ending state 4: " << Highest<__COUNTER__, void>(Counter1(), TemplateInt<4>()) << std::endl; std::cout << "Ending state 5: " << Highest<__COUNTER__, void>(Counter1(), TemplateInt<5>()) << std::endl; std::cout << Read0 << std::endl; std::cout << Read1 << std::endl; std::cout << Read2 << std::endl; std::cout << Read3 << std::endl; std::cout << Read4 << std::endl; std::cout << Read5 << std::endl; std::cout << Read6 << std::endl; return 0; } 

产量

 Ending state 0: 0 Ending state 1: 1 Ending state 2: 2 Ending state 3: 3 Ending state 4: 4 Ending state 5: 4 0 0 1 2 3 4 1 

250 * 250的水平计数器

如果你想要比256更高的值,我想你可以组合计数器。 我做了250 * 250(虽然我没有真正testing过去2)。 对于编译器编译时recursion限制,CounterLimit必须降低到250左右。 只要注意,这花了很多时间来编译我。

履行

 template <typename, unsigned int> struct ExtraCounter { }; template <unsigned int GetID, typename, typename TagID> constexpr unsigned int LHighest(TagID) { return Highest<GetID, void>(ExtraCounter<TagID, CounterLimit>(), TemplateInt<CounterLimit>()) * CounterLimit + Highest<GetID, void>( ExtraCounter<TagID, Highest<GetID, void>(ExtraCounter<TagID , CounterLimit>(), TemplateInt<CounterLimit>())>(), TemplateInt<CounterLimit>()); } #define GetLCount(TagID) \ LHighest<__COUNTER__, void>(TagID()) #define LIncrementTag_(TagID) \ typename std::conditional< \ GetCount(ExtraCounter<TagID, GetCount(ExtraCounter<TagID, CounterLimit>)>) == CounterLimit - 1, \ ExtraCounter<TagID, CounterLimit>, \ ExtraCounter<TagID, GetCount(ExtraCounter<TagID, CounterLimit>)>>::type #define IncrementLCount(TagID) \ template <unsigned int GetID, typename = typename std::enable_if<(GetID > __COUNTER__ + 7)>::type> \ constexpr unsigned int Highest( \ LIncrementTag_(TagID), \ TemplateInt<GetCount(LIncrementTag_(TagID)) + 1> Value) \ { \ return decltype(Value)::Value; \ } 

testing

 struct Counter3 {}; constexpr unsigned int Read7 = GetLCount(Counter3); IncrementLCount(Counter3); constexpr unsigned int Read8 = GetLCount(Counter3); 

由于分享是关怀,我花了几个小时摆弄这方面提供的基本示例,我也将发布我的解决scheme。

文章中链接的版本有两个主要缺点。 由于最大recursion深度(通常约为256),它可以计算的最大数量也非常低。 一旦达到了几百个数字,编译所花费的时间是巨大的。

通过执行二进制search来检测是否已经设置了计数器的标志,可以大幅增加最大计数(可通过MAX_DEPTH进行控制),同时还可以缩短编译时间。 =)

用法示例:

 static constexpr int a = counter_id(); static constexpr int b = counter_id(); static constexpr int c = counter_id(); #include <iostream> int main () { std::cout << "Value a: " << a << std::endl; std::cout << "Value b: " << b << std::endl; std::cout << "Value c: " << c << std::endl; } 

完整的代码示例结束:(除了铿锵声,请参阅评论。)

 // Number of Bits our counter is using. Lower number faster compile time, // but less distinct values. With 16 we have 2^16 distinct values. #define MAX_DEPTH 16 // Used for counting. template<int N> struct flag { friend constexpr int adl_flag(flag<N>); }; // Used for noting how far down in the binary tree we are. // depth<0> equales leaf nodes. depth<MAX_DEPTH> equals root node. template<int N> struct depth {}; // Creating an instance of this struct marks the flag<N> as used. template<int N> struct mark { friend constexpr int adl_flag (flag<N>) { return N; } static constexpr int value = N; }; // Heart of the expression. The first two functions are for inner nodes and // the next two for termination at leaf nodes. // char[noexcept( adl_flag(flag<N>()) ) ? +1 : -1] is valid if flag<N> exists. template <int D, int N, class = char[noexcept( adl_flag(flag<N>()) ) ? +1 : -1]> int constexpr binary_search_flag(int, depth<D>, flag<N>, int next_flag = binary_search_flag(0, depth<D-1>(), flag<N + (1 << (D - 1))>())) { return next_flag; } template <int D, int N> int constexpr binary_search_flag(float, depth<D>, flag<N>, int next_flag = binary_search_flag(0, depth<D-1>(), flag<N - (1 << (D - 1))>())) { return next_flag; } template <int N, class = char[noexcept( adl_flag(flag<N>()) ) ? +1 : -1]> int constexpr binary_search_flag(int, depth<0>, flag<N>) { return N + 1; } template <int N> int constexpr binary_search_flag(float, depth<0>, flag<N>) { return N; } // The actual expression to call for increasing the count. template<int next_flag = binary_search_flag(0, depth<MAX_DEPTH-1>(), flag<(1 << (MAX_DEPTH-1))>())> int constexpr counter_id(int value = mark<next_flag>::value) { return value; } static constexpr int a = counter_id(); static constexpr int b = counter_id(); static constexpr int c = counter_id(); #include <iostream> int main () { std::cout << "Value a: " << a << std::endl; std::cout << "Value b: " << b << std::endl; std::cout << "Value c: " << c << std::endl; } 

不幸的是,模板元编程基本上是一种function性语言,因此缺乏全局variables或可修改的状态来实现这样的计数器。

还是呢?

C ++允许编译时间计数器(即,没有__COUNTER____COUNTER__或前面提出的其他方法)以及为每个模板实例分配和定义内部int唯一ID。 请参阅v1解决scheme使用模板元编程使用链接分配的ID和第二个用例的v2实现的计数器。 两种解决scheme都是“如何在编译时生成密集的唯一typesID? 。 但是这个任务对唯一的ID分配器有一个重要的要求。