在编译时生成BitCount LUT

时间:2013-07-24 10:32:00

标签: c++ templates boost boost-preprocessor

假设我需要创建一个包含预先计算的位计数值(数字中的1位数)的LUT,用于0 ... 255个值:

int CB_LUT[256] = {0, 1, 1, 2, ... 7, 8};

如果我不想使用硬编码值,我可以使用漂亮的模板解决方案How to count the number of set bits in a 32-bit integer?

template <int BITS>
int CountBits(int val) 
{
    return (val & 0x1) + CountBits<BITS-1>(val >> 1);
}

template<>
int CountBits<1>(int val) 
{
    return val & 0x1;
}

int CB_LUT[256] = {CountBits<8>(0), CountBits<8>(1) ... CountBits<8>(255)}; 

此数组在编译时完全计算。有没有办法避免长列表,并使用某种模板甚至宏生成这样的数组(抱歉!),如:

Generate(CB_LUT, 0, 255);  // array declaration
...
cout << CB_LUT[255];       // should print 8

备注即可。这个问题不是关于计算一个数字中的1位,而是仅用作示例。我想在代码中完全生成这样的数组,而不使用外部代码生成器。必须在编译时生成数组。

修改即可。 为了克服编译器限制,我找到了以下解决方案,基于 Bartek Banachewicz`代码:

#define MACRO(z,n,text) CountBits<8>(n)
int CB_LUT[] = {
    BOOST_PP_ENUM(128, MACRO, _)
};
#undef MACRO

#define MACRO(z,n,text) CountBits<8>(n+128)
int CB_LUT2[] = {
    BOOST_PP_ENUM(128, MACRO, _)
};
#undef MACRO

for(int i = 0; i < 256; ++i)   // use only CB_LUT
{
    cout << CB_LUT[i] << endl;
}

我知道这可能是UB ......

2 个答案:

答案 0 :(得分:5)

使用宏(我最近为我的代码重新发现)Boost.Preprocessor会很容易 - 我不确定它是否属于“不使用外部代码生成器”。


PP_ENUM版本

感谢@ BOOST_PP_ENUM的@TemplateRex,正如我所说,我对PP的经验还不够:)

#include <boost/preprocessor/repetition/enum.hpp>

// with ENUM we don't need a comma at the end
#define MACRO(z,n,text) CountBits<8>(n)
int CB_LUT[256] = {
    BOOST_PP_ENUM(256, MACRO, _)
};
#undef MACRO

PP_ENUM的主要区别在于它会在每个元素后自动添加逗号并删除最后一个元素。


PP_REPEAT版本

#include <boost/preprocessor/repetition/repeat.hpp>

#define MACRO(z,n,data) CountBits<8>(n),
int CB_LUT[256] = {    
    BOOST_PP_REPEAT(256, MACRO, _)
};
#undef MACRO

说明

它实际上非常简单易用,但由您来决定是否接受宏。我个人在使用Boost.MPL和模板技术方面苦苦挣扎,发现PP解决方案易于阅读,简短而强大,特别是对于那些枚举。 PP相对于TMP的另一个重要优势是编译时间。

至于最后的逗号,所有合理的编译器都应该支持它,但是如果你没有,只需将重复次数改为255并手动添加最后一个案例。

您可能还想将MACRO重命名为有意义的内容,以避免可能的重新定义。

答案 1 :(得分:2)

我喜欢这样做:

#define MYLIB_PP_COUNT_BITS(z, i, data) \
        CountBits< 8 >(i)

int CB_LUT[] = {
        BOOST_PP_ENUM(256, MYLIB_PP_COUNT_BITS, ~)
};

#undef MYLIB_PP_COUNT_BITS
  • BOOST_PP_REPEAT的区别在于BOOST_PP_ENUM生成以逗号分隔的值序列,因此无需担心逗号和最后的行为。
  • 此外,建议使用NAMESPACE_PP_FUNCTION命名方案使您的宏非常响亮和令人讨厌。
  • 一个小配置是在数组大小中省略[256]以支持[],以便您以后可以更轻松地修改它。
  • 最后,我建议您制作CountBit函数模板constexpr,以便初始化const数组。