首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >位包装和拆包

位包装和拆包
EN

Code Review用户
提问于 2014-04-04 17:42:05
回答 2查看 9.8K关注 0票数 6

我需要一个变量函数来将位打包和解压缩为整数类型。这是我第一次尝试:

代码语言:javascript
运行
复制
template<typename T>
constexpr T pack(bool b)
{
    return b;
}

template<typename T, typename... Types>
constexpr T pack(bool b, Types... args)
{
    return (b << sizeof...(Types)) | pack<T>(args...);
}

template<typename T, typename... Types>
void unpack(T packed, bool& b1)
{
    b1 = packed & 1;
}

template<typename T, typename... Types>
void unpack(T packed, bool& b1, Types&... args)
{
    b1 = packed & (1 << sizeof...(Types));
    unpack(packed, args...);
}

用法示例:

代码语言:javascript
运行
复制
int main(void)
{
    std::cout << pack<int>(1, 0, 0, 1, 0, 1, 1, 0) << std::endl; // 150
    std::cout << pack<int>(1, 0, 1) << std::endl; // 5
    int val = pack<int>(1, 0, 1);
    bool b1, b2, b3;
    unpack(val, b1, b2, b3);
    std::cout << b1 << " " << b2 << " " << b3 << std::endl; // 1 0 1
}

这段代码是否包含任何bug,可以改进,以及在这里是否适当地使用了各种模板(我仍然不掌握它们的语法)?

EN

回答 2

Code Review用户

回答已采纳

发布于 2014-04-04 17:53:26

缺失溢出检查

我在这段代码中看到的一个严重缺陷是,用户需要确定unpack的参数数。对于输出依赖于运行时输入的函数,这是“否”。更糟糕的是,没有任何检查可以告诉用户,实际数字溢出了要解压缩的位数。

一个可能的解决方案是返回一个std::vector<bool>以适应整个数字。

另一种解决方案是保留当前接口,但引入错误通知(异常或返回代码)。

不必要的模板参数

关于unpack函数的另一件事是:在递归结束时,您似乎不需要Types参数,因此它变成了:

代码语言:javascript
运行
复制
template<typename T>
void unpack(T packed, bool& b1)
{
    b1 = packed & 1;
}

命名

函数参数名称可能更好。我假设b1只适用于函数的顶层调用。我现在不能决定一个更好的名称,但它应该表明这是当前的位。同样,args应该被命名为remainingBits或类似的东西。

票数 5
EN

Code Review用户

发布于 2014-04-04 18:38:10

我想你知道性病::位集,你有自己的理由去做这件事。您的pack看起来不错,但是unpack要求用户设置特定数量的bool变量,这是不方便的。另一种选择可以是对输出使用std::array

代码语言:javascript
运行
复制
template<size_t N>
using size = std::integral_constant<size_t, N>;

template<size_t N, typename T>
void unpack(size<N>, array<bool, N>& a, T) { }

template<size_t I, size_t N, typename T>
void unpack(size<I>, array<bool, N>& a, T packed)
{
    a[N-I-1] = (packed & (1 << I)) != 0;
    unpack(size<I+1>(), a, packed);
}

template<typename T>
std::array<bool, 8*sizeof(T)>
unpack(T packed)
{
    std::array<bool, 8*sizeof(T)> a = {};
    unpack(size<0>(), a, packed);
    return a;
}

它可以像这样使用

代码语言:javascript
运行
复制
int val = pack<int>(1, 0, 1);
std::cout << unpack(val) << std::endl;
// prints 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1

假设一个简单的流运算符

代码语言:javascript
运行
复制
template<typename S, typename T, size_t N>
S& operator<<(S& s, const std::array<T, N>& a)
{
    for (auto i : a)
        s << i << " ";
    return s;
}

这样,您就可以确定输出中没有溢出。

现在,std::vector是输出的另一种选择,它可能受益于其紧凑的std::vector<bool>专门化(当然还有std::bitset )。另一方面,使用std::array可以获得真正的constexpr版本的unpack,而无需递归。pack也可能是非递归的,剩余的constexpr。如果你喜欢这个潜力,我可以详细说明。

更新:constexpr版本

实际上,pack很难实现非递归和constexpr,但正如承诺的那样,这里有一个非递归的constexpr版本的unpack (完整的,实例化的):

代码语言:javascript
运行
复制
template <size_t... N>
struct sizes { using type = sizes <N...>; };

template<size_t N, size_t... I, typename T>
constexpr std::array<bool, N>
unpack(sizes<I...>, T packed)
{
    return std::array<bool, N>{{(packed & (1 << (N-I-1))) != 0 ...}};
}

template<size_t N, typename T>
constexpr std::array<bool, N>
unpack(T packed)
{
    return unpack<N>(typename Range<N>::type(), packed);
}

template<typename T, size_t N = 8*sizeof(T)>
constexpr std::array<bool, N>
unpack(T packed) { return unpack<N>(packed); }

其中Range<N>包含序列0,...N-1,并定义为这里。现在您可以这样使用它:

代码语言:javascript
运行
复制
int val = pack<int>(1, 0, 1);

// 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0 1
std::cout << unpack(val) << std::endl;

// 0 0 0 0 0 1 0 1
std::cout << unpack<8>(val) << std::endl;

第一个版本返回表示给定(整)类型的精度所需的整个数组。第二个允许为数组指定较短的长度;其余(最左边)位被丢弃。

票数 5
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/46303

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档