2013-02-16 86 views
3

我正在尝试使用可变参数模板和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; 
} 

回答

2

我不会有get回报bitset,因为每个bitset需要管理自己的内存。

相反,我会使用一个bitset内部管理所有的位,创造bitset::reference般的个体位参考,并bitset状“片”,其get可以返回。

A bitslice将有一个指针回到原来的packed_bits,并知道它开始的偏移量以及它的宽度。它的references到个别位将从原始packed_bitsreferences,其中references从内部bitset,可能。

您的Size是多余的 - sizeof...(pack)告诉您包装中有多少元素。

我会将切片的大小打包成一个序列,以便您可以更容易地传递它。即:

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

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

作为第一步,写bit_slice<offset, length>,这需要一个std::bitset<size>并产生bitset::reference s到个别位,其中相同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通过添加第一个n-1 packed_bits大小确定,其然后从packed_bits的内部bitset构建。

有意义吗?

显然不是。这是一个快速的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与运行时间偏移和源尺寸。这会效率较低,但更容易针对性。

+0

好的 - 我已根据您的建议实施了一切。我只是停留在最后一段:我不知道如何将单个的bitset <> :: reference绑定成某种类型的bitset <>&' - 现在想到这个问题。 – kfmfe04 2013-02-16 09:25:55

+0

@ kfmfe04所以你有一个'bit_slice',可以从'bitset'中提取一系列的位,并显示它的'operator []'视图?从'referenceset'中构造'bitset'比从'bitset'提取参考片段少。 – Yakk 2013-02-16 14:18:37

+0

是的,因为'bit_slice'派生自'std :: bitset <>',所以我可以访问'bit :: bitset <>'中的所有功能。通过分解数据(制作副本),我会使用更多的空间+还有其他锯齿危险,但如果我小心的话,它会好的。刚刚抓住'bitset :: reference'的问题是,一次一位一次并不像一堆位一样方便。出于我的目的,我正在填充几个表示多重索引的std :: bitset <>',我使用它作为一个紧凑键。例如,在操作切片时,将它们视为无符号的短片非常方便。 – kfmfe04 2013-02-16 14:29:35

相关问题