我正在尝试使用可变参数模板和std::bitset
实现packed_bits
类。如何访问packed_bits <>作为std :: bitset <>的切片?
特别是,我遇到了编写get
函数的问题,该函数返回对包含所有打包位的成员m_bits
子集的引用。该功能应类似于std::get
对于std::tuple
。
它应该充当参考覆盖,所以我可以操纵packed_bits
的子集。
例如,
using my_bits = packed_bits<8,16,4>;
my_bits b;
std::bitset<8>& s0 = get<0>(b);
std::bitset<16>& s1 = get<1>(b);
std::bitset<4>& s2 = get<2>(b);
UPDATE
下面是根据Yakk's recommendations below已被重写的代码。我卡在他最后一段的时候:不知道如何将粘合在一起将个人引用合并为一个bitset-like参考。现在思考/研究最后一部分。
更新2
好了,我的新方法将是让bit_slice<>
做大量的工作:
- 它意味着是短命
- 它会公共子类
std::bitset<length>
,作为临时缓冲区 - 正在施工中,它将从
packed_bits<>& m_parent;
复制 - 上破坏,将会写入
m_parent
- 除了经由
m_parent
基准,它也必须知道偏移,长度 get<>
将成为自由函数,它接受一个packet_bits<>
和值而不是返回一个bit_slice<>
bitset<>
通过引用
存在各种不足之处,以这种方法:
bit_slice<>
已经是相对短暂的,以避免混淆的问题,因为我们只在建设和破坏- 我们必须避免在编码(是否螺纹与否)
- 我们就容易切片,如果我们尝试多个重叠的引用更新当我们有一个子类的实例时持有指向基类的指针
但我认为这将足以满足我的需求。完成后我会发布完成的代码。
更新3
与编译器的战斗后,我想我有一个基本的版本工作。不幸的是,我无法正确编译自由浮动的::get()
:BROKEN
显示了这个位置。否则,我认为它工作。
非常感谢Yakk的建议:根据他的评论,下面的代码约为90%+。
UPDATE 4
自由浮动::get()
固定。
UPDATE 5
至于建议的Yakk,我已经消除了复制。 bit_slice<>
将在get_value()
上阅读并在set_value()
上书写。反正我的电话可能有90%是通过这些接口进行的,所以不需要继承/复制。
没有更多的肮脏。
CODE
#include <cassert>
#include <bitset>
#include <iostream>
// ----------------------------------------------------------------------------
template<unsigned... Args>
struct Add { enum { val = 0 }; };
template<unsigned I,unsigned... Args>
struct Add<I,Args...> { enum { val = I + Add<Args...>::val }; };
template<int IDX,unsigned... Args>
struct Offset { enum { val = 0 }; };
template<int IDX,unsigned I,unsigned... Args>
struct Offset<IDX,I,Args...> {
enum {
val = IDX>0 ? I + Offset<IDX-1,Args...>::val : Offset<IDX-1,Args...>::val
};
};
template<int IDX,unsigned... Args>
struct Length { enum { val = 0 }; };
template<int IDX,unsigned I,unsigned... Args>
struct Length<IDX,I,Args...> {
enum {
val = IDX==0 ? I : Length<IDX-1,Args...>::val
};
};
// ----------------------------------------------------------------------------
template<unsigned... N_Bits>
struct seq
{
static const unsigned total_bits = Add<N_Bits...>::val;
static const unsigned size = sizeof...(N_Bits);
template<int IDX>
struct offset
{
enum { val = Offset<IDX,N_Bits...>::val };
};
template<int IDX>
struct length
{
enum { val = Length<IDX,N_Bits...>::val };
};
};
// ----------------------------------------------------------------------------
template<unsigned offset,unsigned length,typename PACKED_BITS>
struct bit_slice
{
PACKED_BITS& m_parent;
bit_slice(PACKED_BITS& t) :
m_parent(t)
{
}
~bit_slice()
{
}
bit_slice(bit_slice const& rhs) :
m_parent(rhs.m_parent)
{ }
bit_slice& operator=(bit_slice const& rhs) = delete;
template<typename U_TYPE>
void set_value(U_TYPE u)
{
for (unsigned i=0; i<length; ++i)
{
m_parent[offset+i] = u&1;
u >>= 1;
}
}
template<typename U_TYPE>
U_TYPE get_value() const
{
U_TYPE x = 0;
for (int i=length-1; i>=0; --i)
{
if (m_parent[offset+i])
++x;
if (i!=0)
x <<= 1;
}
return x;
}
};
template<typename SEQ>
struct packed_bits :
public std::bitset<SEQ::total_bits>
{
using bs_type = std::bitset<SEQ::total_bits>;
using reference = typename bs_type::reference;
template<int IDX> using offset = typename SEQ::template offset<IDX>;
template<int IDX> using length = typename SEQ::template length<IDX>;
template<int IDX> using slice_type =
bit_slice<offset<IDX>::val,length<IDX>::val,packed_bits>;
template<int IDX>
slice_type<IDX> get()
{
return slice_type<IDX>(*this);
}
};
template<int IDX,typename T>
typename T::template slice_type<IDX>
get(T& t)
{
return t.get<IDX>();
};
// ----------------------------------------------------------------------------
int main(int argc, char* argv[])
{
using my_seq = seq<8,16,4,8,4>;
using my_bits = packed_bits<my_seq>;
using my_slice = bit_slice<8,16,my_bits>;
using slice_1 =
bit_slice<my_bits::offset<1>::val,my_bits::length<1>::val,my_bits>;
my_bits b;
my_slice s( b);
slice_1 s1(b);
assert(sizeof(b)==8);
assert(my_seq::total_bits==40);
assert(my_seq::size==5);
assert(my_seq::offset<0>::val==0 );
assert(my_seq::offset<1>::val==8 );
assert(my_seq::offset<2>::val==24);
assert(my_seq::offset<3>::val==28);
assert(my_seq::offset<4>::val==36);
assert(my_seq::length<0>::val==8 );
assert(my_seq::length<1>::val==16);
assert(my_seq::length<2>::val==4 );
assert(my_seq::length<3>::val==8 );
assert(my_seq::length<4>::val==4 );
{
auto s2 = b.get<2>();
}
{
auto s2 = ::get<2>(b);
s2.set_value(25); // 25==11001, but only 4 bits, so we take 1001
assert(s2.get_value<unsigned>()==9);
}
return 0;
}
好的 - 我已根据您的建议实施了一切。我只是停留在最后一段:我不知道如何将单个的bitset <> :: reference绑定成某种类型的bitset <>&' - 现在想到这个问题。 – kfmfe04 2013-02-16 09:25:55
@ kfmfe04所以你有一个'bit_slice',可以从'bitset'中提取一系列的位,并显示它的'operator []'视图?从'referenceset'中构造'bitset'比从'bitset'提取参考片段少。 – Yakk 2013-02-16 14:18:37
是的,因为'bit_slice'派生自'std :: bitset <>',所以我可以访问'bit :: bitset <>'中的所有功能。通过分解数据(制作副本),我会使用更多的空间+还有其他锯齿危险,但如果我小心的话,它会好的。刚刚抓住'bitset :: reference'的问题是,一次一位一次并不像一堆位一样方便。出于我的目的,我正在填充几个表示多重索引的std :: bitset <>',我使用它作为一个紧凑键。例如,在操作切片时,将它们视为无符号的短片非常方便。 – kfmfe04 2013-02-16 14:29:35