我正在尝试packed_bits
使用可变参数模板和std::bitset
.
特别是,我在编写一个函数时遇到问题,该函数返回对包含所有打包位get
的成员子集的引用。m_bits
该功能应类似于std::get
for 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 );
更新
下面是根据Yakk 的建议重写的代码。我停留在他最后一段的重点:不知道如何将各个引用粘合到一个类似位集的引用中。现在正在考虑/工作最后一部分。
更新 2
好的,我的新方法是让bit_slice<>
大部分工作完成:
- 它是短暂的
- 它将公开子类化
std::bitset<length>
,充当临时缓冲区 - 在施工时,它将从
packed_bits<>& m_parent;
- 在销毁时,它将写入
m_parent
- 除了参考 via 之外
m_parent
,还必须知道 offset、length get<>
将成为一个自由函数,它接受 a并按值而不是按引用packet_bits<>
返回 abit_slice<>
bitset<>
这种方法有各种缺点:
bit_slice<>
必须相对较短以避免混叠问题,因为我们只更新构造和破坏- 我们必须在编码时避免多个重叠的引用(无论是否线程化)
- 当我们有一个子类的实例时,如果我们试图持有一个指向基类的指针,我们将倾向于切片
但我认为这足以满足我的需求。完成后我会发布完成的代码。
更新 3
在与编译器斗争之后,我想我有一个基本版本可以工作。不幸的是,我无法::get()
正确编译自由浮动: BROKEN
显示现场。否则,我认为它正在工作。
非常感谢 Yakk 的建议:根据他的评论,下面的代码大约 90%+。
更新 4
自由浮动::get()
固定。
更新 5
正如 Yakk 所建议的,我已经删除了副本。 bit_slice<>
将继续阅读get_value()
和写作set_value()
。无论如何,我的调用中可能有 90% 以上是通过这些接口进行的,因此无需子类化/复制。
不再肮脏。
代码
#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;
}