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

标签 c++ templates metaprogramming state

出于自省(introspection)的目的,有时我想自动为类型分配序列号或类似的东西。

不幸的是,模板元编程本质上是一种函数式语言,因此缺少实现这种计数器的全局变量或可修改状态。

是吗?


请求示例代码:

#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 中的一个错误和标准中的一些不清楚的措辞误导了我相信所有这些功能都是可能的。

但是,至少可以在几乎不使用模板的情况下实现命名空间范围的功能。函数查找可以从声明的函数集中提取数值状态,如下所示。

库代码:

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 ) > )

快速演示(see it run):

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 模板是通过与 constant_index 类型的 ADL 关联找到的。

COUNTER_LINK_NAMESPACE 宏可用于在多个命名空间范围内递增一个计数器。

关于c++ - C++ 是否支持编译时计数器?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/6166337/

相关文章:

c++ - boost::thread 生成线程时出现运行时错误

c# - 在 Windows 8 Metro Windows Store 应用程序中合并声音文件音频和记录

c++ - 从组合类公开模板参数的替代方法

css - 使用 Node.js 提供用户特定的 CSS 自定义

c++ - 当从宏传递到函数时,避免传递 void 表达式

Ruby 元编程,RSpec 的 'should' 是如何工作的?

c++ - 在 MSVC 2010 上使用 bjam 构建 Boost 1.50 时出错

c++ - C++中的 map 与多 map (性能)

c++ - 建立二叉树、打印和搜索的程序 - Node Class C++

rust - 使用rust宏以程序方式调用函数