【问题标题】:Generating BitCount LUT at compile time在编译时生成 BitCount LUT
【发布时间】:2013-07-23 18:40:54
【问题描述】:

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

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

【问题讨论】:

  • 您为什么不想要一个带有硬编码值的简单查找表?这有什么问题?
  • 如果你不喜欢预处理器魔法,你可以试试可变参数模板魔法:stackoverflow.com/q/2978259/819272

标签: c++ templates boost boost-preprocessor


【解决方案1】:

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


PP_ENUM版本

感谢@TemplateRex 提供BOOST_PP_ENUM,正如我所说,我在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 重命名为有意义的名称以避免可能的重新定义。

【讨论】:

  • 关于末尾的逗号it is legal according to the Standard,因此任何编译器不接受它都是非常不合理 ;-)
  • @TemplateRex 我知道这是合法的,但遗憾的是,不符合标准的编译器比符合标准的编译器要多。
  • 同时我得到fatal error C1009: compiler limit : macros nested too deeply。它的大小约为 230。据微软称,The compiler has a limit of 256 levels of nested macros
  • @AlexFarber 更像BOOST_PP_REPEAT_FROM_TO
  • 由于某种原因,使用 BOOST_PP_REPEAT_FROM_TO 两次生成子数组会产生相同的宏嵌套错误。无论如何,我接受这个答案,因为它提供了编译器限制内的方法。谢谢。
【解决方案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 数组。

【讨论】:

  • 谢谢,它看起来类似于 Bartek Banachewicz 之前发布的解决方案。如果您可以详细说明您的评论中提到的可变参数模板解决方案,那就太好了...
  • @AlexFarber 我没有尝试过那个解决方案,但是 Georg Fritzsche 的答案可以粘贴到an online compiler
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-05-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多