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

时间:2011-05-29 06:50:45

标签: c++ templates metaprogramming state

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

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

或者是吗?


请求的示例代码:

#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"
}

8 个答案:

答案 0 :(得分:45)

嗯......是的,模板元编程缺乏预期的副作用。我被旧版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宏可用于在多个名称空间的范围内递增一个计数器。

答案 1 :(得分:20)

我相信MSVC和GCC都支持__COUNTER__预处理程序令牌,该令牌代替了单调递增值。

答案 2 :(得分:18)

我想在很长一段时间内解决这个问题,并提出了一个非常简短的解决方案。至少我应该有一个upvote试试这个。 :))

以下库代码实现了命名空间级功能。即我成功实施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__宏。 有关您答案中的代码,请参阅the result

答案 3 :(得分:6)

您可以使用Boost.Preprocessor中的BOOST_PP_COUNTER

优点:它甚至适用于宏

缺点:整个程序只有一个“计数器类型”,但可以为专用计数器重新实现该机制

答案 4 :(得分:6)

由于共享是关心的,我花了几个小时摆弄基础示例this方提供我也将发布我的解决方案。

文章中链接的版本有两个主要缺点。由于最大递归深度(通常约为256),它可以计数的最大数量也非常低。而且,一旦达到数百的计数,编译所需的时间就很长。

通过实现二进制搜索来检测是否已经设置了计数器的标志,可以大规模地增加最大计数(可通过MAX_DEPTH控制)并同时改善编译时间。 =)

用法示例:

<option value="{{ index //? }}">{{ code }}</option>

最后使用示例完全运行代码:(除了clang。请参阅注释。)

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;
}

答案 5 :(得分:4)

这是另一种替代实施方式。 https://stackoverflow.com/a/6174263/1190123可能更好,但即使在纸上手动完成几个增量后,我仍然不太了解数学/过滤。

这使用constexpr函数递归来计算非模板声明的Highest函数的数量。 __COUNTER__用作生成机制,以防止Highest的新声明进行自递归。

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

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; \
}

测试

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(虽然我没有真正测试过去2)。对于编译器编译时间递归限制,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; \
}

测试

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

答案 6 :(得分:1)

  

不幸的是,模板元编程本质上是一种功能   语言,因此缺乏全局变量或可修改的状态   会实施这样的反击。

     

或者是吗?

C ++允许编译时计数器(即没有__COUNTER____LINE__或前面提出的其他方法),以及为每个模板实例分配和定义内部int唯一ID。有关使用链接分配ID进行模板元编程实现的计数器,请参阅v1解决方案,对于第二个用例,请参见v2。这两个解决方案都是"How can I generate dense unique type IDs at compile time?"的答案。但是该任务对唯一的ID分配器有一个重要的要求。

答案 7 :(得分:0)

我本人经历了整个过程,最终提出了一个似乎符合标准的解决方案(在撰写本文时),并且可以与gcc,clang,msvc和icc一起使用他们的最新版本以及大多数旧版本。

我已经在C++ compile time counters, revisited上的另一篇文章中谈到了整个过程。

然后,我将the solution打包到一个fameta::counter类中,该类可以解决一些剩余的怪癖。

您可以find it on github