4

packed_bits可変個引数テンプレートと を使用してクラスを実装しようとしていstd::bitsetます。

特に、すべてのパックされたビットを含むgetメンバーのサブセットへの参照を返す関数を作成する際に問題が発生しています。関数はform_bitsに似ている必要があります。std::getstd::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 の推奨事項に従って書き直されたコードです。私は彼の最後の段落のポイントで立ち往生しています:個々の参照を 1 つのビットセットのような参照に結合する方法がわかりません。その最後の部分に今考えている/取り組んでいます。

更新 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% 以上です。

更新 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

それぞれが独自のメモリを管理する必要があるため、 をget返す必要はありません。bitsetbitset

代わりに、bitset内部的に を使用してすべてのビットを管理し、-のようbitset::referenceな個々のビット参照と、返される可能性のある-のbitsetような「スライス」を作成しますget

Abitsliceは元の へのポインターを持ち、packed_bits開始位置のオフセットと幅を認識します。個々のreferencesビットはreferences元のものpacked_bitsでありreferences、内部bitsetのものである可能性があります。

あなたSizeは冗長です --sizeof...(pack)パック内に要素がいくつあるかを示します。

スライスのサイズをシーケンスにまとめて、簡単に渡すことができるようにします。すなわち:

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

は任意の長さの s のリストを抽出できる型ですがunsigned int、単一のパラメーターとしてテンプレートに渡すことができます。

最初のステップとして、 writebit_slice<offset, length>を受け取り、個々のビットに s をstd::bitset<size>生成します。ここで、 は と同じです。bitset::referencebit_slice<offset, length>[n]bitset[n+offset]

オプションで、実行時パラメーターとしてbit_slice保存できます (コンパイル時パラメーターは単なる最適化であり、私が疑うほど強力ではないため)。offsetoffset

を取得したらbit_slice、 のタプル構文に取り組むことができますpacked_bits。 は、最初の n-1 個のサイズを加算して決定された を使用して、サイズにインデックスを付けて決定された をget<n, offset=0>( packed_bits<a,b,c,...>& )返します。これは、 の内部から構築されます。bit_slice<x>packed_bitsoffsetpacked_bitsbitsetpacked_bits

わかる?

どうやらそうではありません。bit_sliceこれは、 a内のビットのサブ範囲を表すクイックです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
}

もう 1 つの便利なクラスはbit_slice、ランタイム オフセットとソース サイズを持つ です。これは効率が低下しますが、プログラミングが容易になります。

于 2013-02-16T03:44:09.873 に答える