jjzjj

c++ - 如何访问一片 packed_bits<> 作为 std::bitset<>?

coder 2024-02-22 原文

我正在尝试实现 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 );

更新

下面是根据Yakk's recommendations below重写的代码.我停留在他最后一段的要点上:不确定如何将各个引用粘合在一起成一个类似位集的引用。现在正在思考/处理最后一部分。

更新 2

好的,我的新方法是让 bit_slice<>完成大部分工作:

  • 它注定是短暂的
  • 它将公开继承std::bitset<length> , 充当临时缓冲区
  • 关于构造,它将从packed_bits<>& m_parent;复制
  • 销毁时,它将写入 m_parent
  • 除了通过 m_parent 的引用,它还必须知道偏移量,长度
  • get<>将成为一个自由函数,需要 packet_bits<>并返回 bit_slice<>按值而不是 bitset<>通过引用

这种方法有很多缺点:

  • bit_slice<>必须相对短暂以避免别名问题,因为我们只更新构造和破坏
  • 我们必须在编码时避免多次重叠引用(无论是否线程化)
  • 如果我们在拥有子类的实例时试图持有指向基类的指针,我们将很容易出现切片

但我认为这足以满足我的需求。完成后我会发布完成的代码。

更新 3

在与编译器打过交道之后,我想我已经有了一个可以工作的基本版本。不幸的是,我无法获得自由 float 的 ::get()正确编译:BROKEN显示现场。否则,我认为它有效。

非常感谢 Yakk 的建议:根据他的评论,下面的代码大约有 90%+。

更新 4

自由 float ::get()固定。

更新 5

按照 Yakk 的建议,我已经删除了拷贝。 bit_slice<>将阅读 get_value()写在set_value()上.无论如何,大概 90% 以上的调用都将通过这些接口(interface),因此无需子类化/复制。

不再肮脏。

代码

#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;
}

最佳答案

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

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

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

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

我会将切片的大小打包成一个序列,这样您就可以更轻松地传递它。即:

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

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

第一步,写bit_slice<offset, length> ,它需要一个 std::bitset<size>并产生 bitset::reference s 到单个位,其中 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通过添加第一个 n-1 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具有运行时偏移量和源大小。这会降低效率,但更易于编程。

关于c++ - 如何访问一片 packed_bits<> 作为 std::bitset<>?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/14906515/

有关c++ - 如何访问一片 packed_bits<> 作为 std::bitset<>?的更多相关文章

  1. ruby - 如何使用 Nokogiri 的 xpath 和 at_xpath 方法 - 2

    我正在学习如何使用Nokogiri,根据这段代码我遇到了一些问题:require'rubygems'require'mechanize'post_agent=WWW::Mechanize.newpost_page=post_agent.get('http://www.vbulletin.org/forum/showthread.php?t=230708')puts"\nabsolutepathwithtbodygivesnil"putspost_page.parser.xpath('/html/body/div/div/div/div/div/table/tbody/tr/td/div

  2. ruby - 如何从 ruby​​ 中的字符串运行任意对象方法? - 2

    总的来说,我对ruby​​还比较陌生,我正在为我正在创建的对象编写一些rspec测试用例。许多测试用例都非常基础,我只是想确保正确填充和返回值。我想知道是否有办法使用循环结构来执行此操作。不必为我要测试的每个方法都设置一个assertEquals。例如:describeitem,"TestingtheItem"doit"willhaveanullvaluetostart"doitem=Item.new#HereIcoulddotheitem.name.shouldbe_nil#thenIcoulddoitem.category.shouldbe_nilendend但我想要一些方法来使用

  3. python - 如何使用 Ruby 或 Python 创建一系列高音调和低音调的蜂鸣声? - 2

    关闭。这个问题是opinion-based.它目前不接受答案。想要改进这个问题?更新问题,以便editingthispost可以用事实和引用来回答它.关闭4年前。Improvethisquestion我想在固定时间创建一系列低音和高音调的哔哔声。例如:在150毫秒时发出高音调的蜂鸣声在151毫秒时发出低音调的蜂鸣声200毫秒时发出低音调的蜂鸣声250毫秒的高音调蜂鸣声有没有办法在Ruby或Python中做到这一点?我真的不在乎输出编码是什么(.wav、.mp3、.ogg等等),但我确实想创建一个输出文件。

  4. ruby-on-rails - 如何验证 update_all 是否实际在 Rails 中更新 - 2

    给定这段代码defcreate@upgrades=User.update_all(["role=?","upgraded"],:id=>params[:upgrade])redirect_toadmin_upgrades_path,:notice=>"Successfullyupgradeduser."end我如何在该操作中实际验证它们是否已保存或未重定向到适当的页面和消息? 最佳答案 在Rails3中,update_all不返回任何有意义的信息,除了已更新的记录数(这可能取决于您的DBMS是否返回该信息)。http://ar.ru

  5. ruby-on-rails - 'compass watch' 是如何工作的/它是如何与 rails 一起使用的 - 2

    我在我的项目目录中完成了compasscreate.和compassinitrails。几个问题:我已将我的.sass文件放在public/stylesheets中。这是放置它们的正确位置吗?当我运行compasswatch时,它不会自动编译这些.sass文件。我必须手动指定文件:compasswatchpublic/stylesheets/myfile.sass等。如何让它自动运行?文件ie.css、print.css和screen.css已放在stylesheets/compiled。如何在编译后不让它们重新出现的情况下删除它们?我自己编译的.sass文件编译成compiled/t

  6. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  7. ruby - 如何指定 Rack 处理程序 - 2

    Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack

  8. ruby - 如何每月在 Heroku 运行一次 Scheduler 插件? - 2

    在选择我想要运行操作的频率时,唯一的选项是“每天”、“每小时”和“每10分钟”。谢谢!我想为我的Rails3.1应用程序运行调度程序。 最佳答案 这不是一个优雅的解决方案,但您可以安排它每天运行,并在实际开始工作之前检查日期是否为当月的第一天。 关于ruby-如何每月在Heroku运行一次Scheduler插件?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/questions/8692687/

  9. ruby-on-rails - 如何从 format.xml 中删除 <hash></hash> - 2

    我有一个对象has_many应呈现为xml的子对象。这不是问题。我的问题是我创建了一个Hash包含此数据,就像解析器需要它一样。但是rails自动将整个文件包含在.........我需要摆脱type="array"和我该如何处理?我没有在文档中找到任何内容。 最佳答案 我遇到了同样的问题;这是我的XML:我在用这个:entries.to_xml将散列数据转换为XML,但这会将条目的数据包装到中所以我修改了:entries.to_xml(root:"Contacts")但这仍然将转换后的XML包装在“联系人”中,将我的XML代码修改为

  10. ruby - 如何使用文字标量样式在 YAML 中转储字符串? - 2

    我有一大串格式化数据(例如JSON),我想使用Psychinruby​​同时保留格式转储到YAML。基本上,我希望JSON使用literalstyle出现在YAML中:---json:|{"page":1,"results":["item","another"],"total_pages":0}但是,当我使用YAML.dump时,它不使用文字样式。我得到这样的东西:---json:!"{\n\"page\":1,\n\"results\":[\n\"item\",\"another\"\n],\n\"total_pages\":0\n}\n"我如何告诉Psych以想要的样式转储标量?解

随机推荐