C++ 是否支持编译时计数器?
出于内省的目的,有时我想自动将序列号分配给类型或类似的东西.
For the purpose of introspection, sometimes I've wanted to automatically assign serial numbers to types, or something similar.
不幸的是,模板元编程本质上是一种函数式语言,因此缺乏实现此类计数器的全局变量或可修改状态.
Unfortunately, template metaprogramming is essentially a functional language, and as such lacks global variables or modifiable state which would implement such a counter.
或者是吗?
请求示例代码:
#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 << '
'; // print "0 5"
counter_inc_t();
counter_inc_t();
counter_inc_t();
std::cout << counter_read << '
'; // 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 << '
'; // print "9 10"
}
推荐答案
嗯……是的,模板元编程没有预期的副作用.我被旧版 GCC 中的一个错误和标准中的一些不清楚的措辞误导,认为所有这些功能都是可能的.
Well… yes, template metaprogramming lacks side effects as it is intended. I was misled by a bug in older versions of GCC and a little unclear wording in the Standard to believe that all those features were possible.
然而,至少可以在几乎不使用模板的情况下实现命名空间范围的功能.函数查找可以从声明的函数集中提取数字状态,如下所示.
However, at least the namespace-scope functionality can be achieved with little use of templates at all. Function lookup can extract numeric state from the set of declared functions, as demonstrated below.
库代码:
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 << '
';
std::cout << counter_read( my_cnt ) << '
';
}
C++11 更新
这是使用 C++11 constexpr
代替 sizeof
的更新版本.
C++11 Update
Here is an updated version using C++11 constexpr
in place of 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
模板是通过与 constant_index
类型的 ADL 关联找到的.
The declarations should be put inside a namespace, and all names used in the macros except counter_crumb
should be fully qualified. The counter_crumb
template is found via ADL association with the constant_index
type.
COUNTER_LINK_NAMESPACE
宏可用于在多个命名空间范围内递增一个计数器.
The COUNTER_LINK_NAMESPACE
macro can be used to increment one counter in the scope of multiple namespaces.
相关文章