4

我正在尝试packed_bits使用可变参数模板和std::bitset.

特别是,我在编写一个函数时遇到问题,该函数返回对包含所有打包位get的成员子集的引用。m_bits该功能应类似于std::getfor 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;
}
4

2 回答 2

2

我不会getreturn a bitset,因为每个人都bitset需要管理自己的内存。

相反,我会在bitset内部使用 a 来管理所有位,并创建bitset::reference-like 单个位引用和bitset-like “切片”,它get可以返回。

Abitslice会有一个指向原始 的指针packed_bits,并且会知道它开始的偏移量以及它的宽度。它的references个别位可能references来自原始packed_bits,可能references来自内部bitset

Size是多余的——sizeof...(pack)告诉你包中有多少元素。

我会将切片的大小打包成一个序列,这样你就可以更容易地传递它。IE:

template<unsigned... Vs>
struct seq {};

是一种类型,您可以从中提取任意长度的unsigned ints 列表,但可以作为单个参数传递给模板。

作为第一步,写入bit_slice<offset, length>,它接受 astd::bitset<size>并将bitset::references 生成到各个位,其中bit_slice<offset, length>[n]与 相同bitset[n+offset]

可选地,bit_slice可以存储offset为运行时参数(因为offset作为编译时参数只是一种优化,而不是我怀疑的那么强)。

一旦有了bit_slice,处理 的元组语法packed_bits是可行的。 get<n, offset=0>( packed_bits<a,b,c,...>& )返回bit_slice<x>通过索引packed_bits大小offset确定的 a,通过添加前 n-1 个大小确定的a packed_bits,然后从.bitsetpacked_bits

有道理?

显然不是。这是一个快速bit_slice表示std::bitset.

#include <bitset>

template<unsigned Width, unsigned Offset, std::size_t SrcBitWidth>
struct bit_slice {
private:
  std::bitset<SrcBitWidth>* bits;
public:
  // cast to `bitset`:
  operator std::bitset<Width>() const {
    std::bitset<Width> retval;
    for(unsigned i = 0; i < Offset; ++i) {
      retval[i] = (*this)[i];
    }
    return retval;
  }
  typedef typename std::bitset<SrcBitWidth>::reference reference;
  reference operator[]( size_t pos ) {
    // TODO: check that pos < Width?
    return (*bits)[pos+Offset];
  }
  constexpr bool operator[]( size_t pos ) const {
    // TODO: check that pos < Width?
    return (*bits)[pos+Offset];
  }
  typedef bit_slice<Width, Offset, SrcBitWidth> self_type;
  // can be assigned to from any bit_slice with the same width:
  template<unsigned O_Offset, unsigned O_SrcBitWidth>
  self_type& operator=( bit_slice<Width, O_Offset, O_SrcBitWidth>&& o ) {
    for (unsigned i = 0; i < Width; ++i ) {
      (*this)[i] = o[i];
    }
    return *this;
  }
  // can be assigned from a `std::bitset<Width>` of the same size:
  self_type& operator=( std::bitset<Width> const& o ) {
    for (unsigned i = 0; i < Width; ++i ) {
      (*this)[i] = o[i];
    }
    return *this;
  }
  explicit bit_slice( std::bitset<SrcBitWidth>& src ):bits(&src) {}
  bit_slice( self_type const& ) = default;
  bit_slice( self_type&& ) = default;
  bit_slice( self_type&o ):bit_slice( const_cast<self_type const&>(o)) {}
  // I suspect, but am not certain, the the default move/copy ctor would do...
  // dtor not needed, as there is nothing to destroy

  // TODO: reimplement rest of std::bitset's interface that you care about
};

template<unsigned offset, unsigned width, std::size_t src_width>
bit_slice< width, offset, src_width > make_slice( std::bitset<src_width>& src ) {
  return bit_slice< width, offset, src_width >(src);
}

#include <iostream>
int main() {
  std::bitset<16> bits;
  bits[8] = true;
  auto slice = make_slice< 8, 8 >( bits );
  bool b0 = slice[0];
  bool b1 = slice[1];
  std::cout << b0 << b1 << "\n"; // should output 10
}

另一个有用的类是bit_slice具有运行时偏移量和源大小的类。这会降低效率,但更容易编程。

于 2013-02-16T03:44:09.873 回答
1

我猜是这样的:

#include <iostream>
#include <bitset>

using namespace std;

template<int N, int L, int R>
bitset<L-R>
slice(bitset<N> value)
{
    size_t W = L - R + 1;
    if (W > sizeof(uint64_t) * 8) {
        W = 31;
        throw("Exceeding integer word size");
    }

    uint64_t svalue = (value.to_ulong() >> R) & ((1 << W) - 1);

        return bitset<L-R>{svalue};
    }
}

int main()
{
    bitset<16>  beef { 0xBEEF };
    bitset<4-3> sliced_beef      = slice<16, 4, 3>(beef);    
    auto        fast_sliced_beef = slice<16, 4, 3>(beef);
    return 0;
}
于 2018-07-31T18:34:23.820 回答