C++ 使用boost::join合并多个阵列

C++ 使用boost::join合并多个阵列,c++,arrays,boost,C++,Arrays,Boost,使用boost::join访问和更改不同数组的值是否更好 我在class元素中定义了一个成员数组 class element { public: element(); int* get_arr(); private: int m_arr[4]; } 在不同的地方,我访问这些数组,并使用boost::join将它们连接在一起,并更改数组值 //std::vector<element> elem; auto temp1 = boost::joi

使用boost::join访问和更改不同数组的值是否更好

我在class
元素
中定义了一个成员数组

class element
{
public:
     element();
     int* get_arr();
private:
     int m_arr[4];   

}
在不同的地方,我访问这些数组,并使用boost::join将它们连接在一起,并更改数组值

 //std::vector<element> elem;
 auto temp1 = boost::join(elem[0].get_arr(),elem[1].get_arr());
 auto joined_arr = boost::join(temp1,elem[2].get_arr()); 

//now going to change the values of the sub array
for(auto& it:joined_arr)
{
     it+=  sample[i];
      i++;
}
//标准::向量元素;
auto temp1=boost::join(元素[0]。get_arr(),元素[1]。get_arr());
auto-join_-arr=boost::join(temp1,elem[2]。get_-arr());
//现在要更改子数组的值
用于(自动和it:joined_arr)
{
it+=样本[i];
i++;
}

如上所述修改类中数组的值是一个好主意吗?

在代码中,您可能希望加入4元素数组。为此,请将
get_arr
的签名更改为:

typedef int array[4];
array& get_arr() { return m_arr; }
这样就不会丢失阵列大小

就性能而言,通过连接视图访问元素的成本不是零。双for循环将是最有效的,也很容易阅读,例如:

for(auto& e : elem)
    for(auto& a : e.get_arr())
        a += sample[i++];

boost::join
在每个合成步骤中返回一个更复杂的类型。在某些情况下,您可能会超出编译器对内联的限制,因此您将有一个运行时成本

跳出框框思考,看起来您真的在创建一个缓冲区抽象,它允许您用很少的分配完成像IO一样的分散/聚集

碰巧,Boost Asio对此有很好的抽象,您可以使用它:

正如我在一篇文章中发现的,遗憾的是,抽象只适用于通过本机char类型元素访问的缓冲区。那不好

因此,在这次重写中,我提出了一个类似的抽象,它只包含一个“层次迭代器”,它知道如何迭代一系列“缓冲区”(在这个实现中,任何范围都可以)

您可以选择直接对一系列范围进行操作,例如:

std::vector<element> seq(3); // tie 3 elements together as buffer sequence
element& b = seq[1];
实施您的测试程序

// DEMO
struct element {
    int peek_first() const { return m_arr[0]; }

    auto begin() const { return std::begin(m_arr); } 
    auto end() const   { return std::end(m_arr);   } 
    auto begin()       { return std::begin(m_arr); } 
    auto end()         { return std::end(m_arr);   } 

  private:
    int m_arr[4] { };
};

namespace boost { // range adapt
    template <> struct range_iterator<element> { using type = int*; };
    // not used, but for completeness:
    template <> struct range_iterator<element const> { using type = int const*; };
    template <> struct range_const_iterator<element> : range_iterator<element const> {};
}

#include <algorithm>
#include <iostream>
#include <vector>

template <typename Output, typename Input, typename Operation>
size_t process(Output& output, Input const& input, Operation op) {
    auto ib = boost::begin(input), ie = boost::end(input);
    auto ob = boost::begin(output), oe = boost::end(output);

    size_t n = 0;
    for (;ib!=ie && ob!=oe; ++n) {
        op(*ob++, *ib++);
    }
    return n;
}

int main() {
    element a, b, c;
    std::vector<std::reference_wrapper<element> > seq {a,b,c}; // tie 3 elements together as buffer sequence

    //// Also supported, container of range objects directly:
    // std::list<element> seq(3); // tie 3 elements together as buffer sequence
    // element& b = seq[1];

    std::vector<int> const samples { 
         1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,
        13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
        25, 26, 27, 28, 29, 30, 31, 32
    };

    using boost::make_iterator_range;
    auto input  = make_iterator_range(samples);
    auto output = make_iterator_range(buffers_begin(seq), buffers_end(seq));

    while (auto n = process(output, input, [](int& el, int sample) { el += sample; })) {
        std::cout << "Copied " << n << " samples, b starts with " << b.peek_first() << "\n";
        input.advance_begin(n);
    }
}
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator_range.hpp>
#include <functional> // std::reference_wrapper

namespace detail {
    template<typename T> constexpr T&       get(T &t)                         { return t;  } 
    template<typename T> constexpr T const& get(T const &t)                   { return t;  } 
    template<typename T> constexpr T&       get(std::reference_wrapper<T> rt) { return rt; } 

    template <typename T> struct unwrap { using type = T; };
    template <typename T> struct unwrap<std::reference_wrapper<T> > { using type = T; };
}

template <typename Seq,
         typename WR = typename Seq::value_type,
         typename R = typename detail::unwrap<WR>::type,
         typename V = typename boost::range_value<R>::type
     >
struct sequence_iterator : boost::iterator_facade<sequence_iterator<Seq,WR,R,V>, V, boost::forward_traversal_tag> {
    using OuterIt = typename boost::range_iterator<Seq>::type;
    using InnerIt = typename boost::range_iterator<R>::type;

    // state
    Seq& _seq;
    OuterIt _ocur, _oend;
    InnerIt _icur, _iend;

    static sequence_iterator begin(Seq& seq) { return {seq, boost::begin(seq), boost::end(seq)}; }
    static sequence_iterator end(Seq&   seq) { return {seq, boost::end(seq),   boost::end(seq)}; }

    // the 3 facade operations
    bool equal(sequence_iterator const& rhs) const {
        return ((_ocur==_oend) && (rhs._ocur==rhs._oend))
            || (std::addressof(_seq) == std::addressof(rhs._seq) &&
                _ocur == rhs._ocur && _oend == rhs._oend &&
                _icur == rhs._icur && _iend == rhs._iend);
    }

    void increment() {
        if (++_icur == _iend) {
            ++_ocur;
            setup();
        }
    }

    V& dereference() const {
        assert(_ocur != _oend);
        assert(_icur != _iend);
        return *_icur;
    }

  private:
    void setup() { // to be called after entering a new sub-range in the sequence
        while (_ocur != _oend) {
            _icur = boost::begin(detail::get(*_ocur));
            _iend = boost::end(detail::get(*_ocur));

            if (_icur != _iend)
                break;
            ++_ocur; // skid over, this enables simple increment() logic
        }
    }

    sequence_iterator(Seq& seq, OuterIt cur, OuterIt end)
        : _seq(seq), _ocur(cur), _oend(end) { setup(); }
};

template <typename Seq> auto buffers_begin(Seq& seq) { return sequence_iterator<Seq>::begin(seq); }
template <typename Seq> auto buffers_end(Seq& seq)   { return sequence_iterator<Seq>::end(seq); }

// DEMO
struct element {
    int peek_first() const { return m_arr[0]; }

    auto begin() const { return std::begin(m_arr); } 
    auto end() const   { return std::end(m_arr);   } 
    auto begin()       { return std::begin(m_arr); } 
    auto end()         { return std::end(m_arr);   } 

  private:
    int m_arr[4] { };
};

namespace boost { // range adapt
    template <> struct range_iterator<element> { using type = int*; };
    // not used, but for completeness:
    template <> struct range_iterator<element const> { using type = int const*; };
    template <> struct range_const_iterator<element> : range_iterator<element const> {};
}

#include <algorithm>
#include <iostream>
#include <vector>

template <typename Output, typename Input, typename Operation>
size_t process(Output& output, Input const& input, Operation op) {
    auto ib = boost::begin(input), ie = boost::end(input);
    auto ob = boost::begin(output), oe = boost::end(output);

    size_t n = 0;
    for (;ib!=ie && ob!=oe; ++n) {
        op(*ob++, *ib++);
    }
    return n;
}

int main() {
    element a, b, c;
    std::vector<std::reference_wrapper<element> > seq {a,b,c}; // tie 3 elements together as buffer sequence

    //// Also supported, container of range objects directly:
    // std::list<element> seq(3); // tie 3 elements together as buffer sequence
    // element& b = seq[1];

    std::vector<int> const samples { 
         1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,
        13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
        25, 26, 27, 28, 29, 30, 31, 32
    };

    using boost::make_iterator_range;
    auto input  = make_iterator_range(samples);
    auto output = make_iterator_range(buffers_begin(seq), buffers_end(seq));

    while (auto n = process(output, input, [](int& el, int sample) { el += sample; })) {
        std::cout << "Copied " << n << " samples, b starts with " << b.peek_first() << "\n";
        input.advance_begin(n);
    }
}
完整清单,与C++11兼容

// DEMO
struct element {
    int peek_first() const { return m_arr[0]; }

    auto begin() const { return std::begin(m_arr); } 
    auto end() const   { return std::end(m_arr);   } 
    auto begin()       { return std::begin(m_arr); } 
    auto end()         { return std::end(m_arr);   } 

  private:
    int m_arr[4] { };
};

namespace boost { // range adapt
    template <> struct range_iterator<element> { using type = int*; };
    // not used, but for completeness:
    template <> struct range_iterator<element const> { using type = int const*; };
    template <> struct range_const_iterator<element> : range_iterator<element const> {};
}

#include <algorithm>
#include <iostream>
#include <vector>

template <typename Output, typename Input, typename Operation>
size_t process(Output& output, Input const& input, Operation op) {
    auto ib = boost::begin(input), ie = boost::end(input);
    auto ob = boost::begin(output), oe = boost::end(output);

    size_t n = 0;
    for (;ib!=ie && ob!=oe; ++n) {
        op(*ob++, *ib++);
    }
    return n;
}

int main() {
    element a, b, c;
    std::vector<std::reference_wrapper<element> > seq {a,b,c}; // tie 3 elements together as buffer sequence

    //// Also supported, container of range objects directly:
    // std::list<element> seq(3); // tie 3 elements together as buffer sequence
    // element& b = seq[1];

    std::vector<int> const samples { 
         1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,
        13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
        25, 26, 27, 28, 29, 30, 31, 32
    };

    using boost::make_iterator_range;
    auto input  = make_iterator_range(samples);
    auto output = make_iterator_range(buffers_begin(seq), buffers_end(seq));

    while (auto n = process(output, input, [](int& el, int sample) { el += sample; })) {
        std::cout << "Copied " << n << " samples, b starts with " << b.peek_first() << "\n";
        input.advance_begin(n);
    }
}
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator_range.hpp>
#include <functional> // std::reference_wrapper

namespace detail {
    template<typename T> constexpr T&       get(T &t)                         { return t;  } 
    template<typename T> constexpr T const& get(T const &t)                   { return t;  } 
    template<typename T> constexpr T&       get(std::reference_wrapper<T> rt) { return rt; } 

    template <typename T> struct unwrap { using type = T; };
    template <typename T> struct unwrap<std::reference_wrapper<T> > { using type = T; };
}

template <typename Seq,
         typename WR = typename Seq::value_type,
         typename R = typename detail::unwrap<WR>::type,
         typename V = typename boost::range_value<R>::type
     >
struct sequence_iterator : boost::iterator_facade<sequence_iterator<Seq,WR,R,V>, V, boost::forward_traversal_tag> {
    using OuterIt = typename boost::range_iterator<Seq>::type;
    using InnerIt = typename boost::range_iterator<R>::type;

    // state
    Seq& _seq;
    OuterIt _ocur, _oend;
    InnerIt _icur, _iend;

    static sequence_iterator begin(Seq& seq) { return {seq, boost::begin(seq), boost::end(seq)}; }
    static sequence_iterator end(Seq&   seq) { return {seq, boost::end(seq),   boost::end(seq)}; }

    // the 3 facade operations
    bool equal(sequence_iterator const& rhs) const {
        return ((_ocur==_oend) && (rhs._ocur==rhs._oend))
            || (std::addressof(_seq) == std::addressof(rhs._seq) &&
                _ocur == rhs._ocur && _oend == rhs._oend &&
                _icur == rhs._icur && _iend == rhs._iend);
    }

    void increment() {
        if (++_icur == _iend) {
            ++_ocur;
            setup();
        }
    }

    V& dereference() const {
        assert(_ocur != _oend);
        assert(_icur != _iend);
        return *_icur;
    }

  private:
    void setup() { // to be called after entering a new sub-range in the sequence
        while (_ocur != _oend) {
            _icur = boost::begin(detail::get(*_ocur));
            _iend = boost::end(detail::get(*_ocur));

            if (_icur != _iend)
                break;
            ++_ocur; // skid over, this enables simple increment() logic
        }
    }

    sequence_iterator(Seq& seq, OuterIt cur, OuterIt end)
        : _seq(seq), _ocur(cur), _oend(end) { setup(); }
};

template <typename Seq> auto buffers_begin(Seq& seq) { return sequence_iterator<Seq>::begin(seq); }
template <typename Seq> auto buffers_end(Seq& seq)   { return sequence_iterator<Seq>::end(seq); }

// DEMO
struct element {
    int peek_first() const { return m_arr[0]; }

    auto begin() const { return std::begin(m_arr); } 
    auto end() const   { return std::end(m_arr);   } 
    auto begin()       { return std::begin(m_arr); } 
    auto end()         { return std::end(m_arr);   } 

  private:
    int m_arr[4] { };
};

namespace boost { // range adapt
    template <> struct range_iterator<element> { using type = int*; };
    // not used, but for completeness:
    template <> struct range_iterator<element const> { using type = int const*; };
    template <> struct range_const_iterator<element> : range_iterator<element const> {};
}

#include <algorithm>
#include <iostream>
#include <vector>

template <typename Output, typename Input, typename Operation>
size_t process(Output& output, Input const& input, Operation op) {
    auto ib = boost::begin(input), ie = boost::end(input);
    auto ob = boost::begin(output), oe = boost::end(output);

    size_t n = 0;
    for (;ib!=ie && ob!=oe; ++n) {
        op(*ob++, *ib++);
    }
    return n;
}

int main() {
    element a, b, c;
    std::vector<std::reference_wrapper<element> > seq {a,b,c}; // tie 3 elements together as buffer sequence

    //// Also supported, container of range objects directly:
    // std::list<element> seq(3); // tie 3 elements together as buffer sequence
    // element& b = seq[1];

    std::vector<int> const samples { 
         1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,
        13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
        25, 26, 27, 28, 29, 30, 31, 32
    };

    using boost::make_iterator_range;
    auto input  = make_iterator_range(samples);
    auto output = make_iterator_range(buffers_begin(seq), buffers_end(seq));

    while (auto n = process(output, input, [](int& el, int sample) { el += sample; })) {
        std::cout << "Copied " << n << " samples, b starts with " << b.peek_first() << "\n";
        input.advance_begin(n);
    }
}
#包括
#包括
#include//std::reference\u包装器
名称空间详细信息{
模板constexpr T&get(T&T){return T;}
模板constexpr T const&get(T const&T){return T;}
模板constexpr T&get(std::reference_wrapper rt){return rt;}
模板结构展开{using type=T;};
模板结构展开{using type=T;};
}
模板
结构序列迭代器:boost::迭代器{
使用OuterIt=typename boost::range\u迭代器::type;
使用InnerIt=typename boost::range\u迭代器::type;
//陈述
顺序和顺序;
外部的,外部的;
内在的,内在的;
静态序列_迭代器begin(Seq&Seq){return{Seq,boost::begin(Seq),boost::end(Seq)};}
静态序列_迭代器end(Seq&Seq){return{Seq,boost::end(Seq),boost::end(Seq)};}
//3个门面操作
布尔相等(序列迭代器常量和rhs)常量{
返回((_ocur==u oend)和&(rhs._ocur==rhs.\u oend))
||(std::addressof(_seq)=std::addressof(rhs._seq)&&
_ocur==rhs.\U ocur&&U oend==rhs.\U oend&&
_icur==rhs.\U icur&&U iend==rhs.\U iend);
}
无效增量(){
如果(+++icur==\iend){
++_奥科;
设置();
}
}
V取消引用()常量(&D){
断言(_ocur!=_oend);
断言(_icur!=_iend);
返回*\icur;
}
私人:
void setup(){//在序列中输入新的子范围后调用
而(_ocur!=_oend){
_icur=boost::begin(细节::get(*_ocur));
_iend=boost::end(细节::get(*_ocur));
如果(如果)
打破
++_ocur;//滑动,这将启用简单的increment()逻辑
}
}
序列迭代器(Seq&Seq,OuterIt cur,OuterIt end)
:_seq(seq),_ocur(cur),_oend(end){setup();}
};
模板自动缓冲区_begin(Seq&Seq){返回序列_迭代器::begin(Seq);}
模板自动缓冲区_end(Seq&Seq){返回序列_迭代器::end(Seq);}
//演示
结构元素{
int peek_first()常量{return m_arr[0];}
auto begin()常量{return std::begin(m_arr);}
auto end()常量{return std::end(m_arr);}
自动开始(){return std::begin(m_arr);}
自动结束(){return std::end(m_arr);}
私人:
int m_arr[4]{};
};
名称空间提升{//范围调整
模板结构范围_迭代器{using type=int*;};
//未使用,但为了完整性:
模板结构范围_迭代器{using type=int const*;};
模板结构范围常量迭代器:范围迭代器{};
}
#包括bug


²已被其他各种库采用,如Boost Beast、Boost Process,并且似乎已进入C++20的网络TS中:

True。我认为OP真的是在追求更优雅。我怀疑我的答案可能会恰到好处——看起来他只是在连接输出缓冲区
boost::join(elem[0]。get_arr(),elem[1]。get_arr())
——这不起作用,因为你不连接数组,只连接指针。在函数调用的位置,编译器没有关于指针指向的数组大小的信息!最初的问题是向数组元素添加样本。这个解决方案似乎是在复制。是吗?@MaximEgorushkin我已经用一个。。。实际上是有效的。这基本上是相同的想法,而不是使用Asio的,因为它不适合非字符缓冲区。今天,我又学到了很多。你提到OP真的在追求更优雅。你认为如果你添加更多的代码,你的解决方案会更加优雅吗?@MaximeGroushkin不,我认为这是一个非常好的地方。这都是关于使用正确的抽象级别。请注意,我确实首先尝试了现有的实现。我可能不会回答这些问题
Copied 12 samples, b starts with 5
Copied 12 samples, b starts with 22
Copied 8 samples, b starts with 51
#include <boost/iterator/iterator_facade.hpp>
#include <boost/range/iterator_range.hpp>
#include <functional> // std::reference_wrapper

namespace detail {
    template<typename T> constexpr T&       get(T &t)                         { return t;  } 
    template<typename T> constexpr T const& get(T const &t)                   { return t;  } 
    template<typename T> constexpr T&       get(std::reference_wrapper<T> rt) { return rt; } 

    template <typename T> struct unwrap { using type = T; };
    template <typename T> struct unwrap<std::reference_wrapper<T> > { using type = T; };
}

template <typename Seq,
         typename WR = typename Seq::value_type,
         typename R = typename detail::unwrap<WR>::type,
         typename V = typename boost::range_value<R>::type
     >
struct sequence_iterator : boost::iterator_facade<sequence_iterator<Seq,WR,R,V>, V, boost::forward_traversal_tag> {
    using OuterIt = typename boost::range_iterator<Seq>::type;
    using InnerIt = typename boost::range_iterator<R>::type;

    // state
    Seq& _seq;
    OuterIt _ocur, _oend;
    InnerIt _icur, _iend;

    static sequence_iterator begin(Seq& seq) { return {seq, boost::begin(seq), boost::end(seq)}; }
    static sequence_iterator end(Seq&   seq) { return {seq, boost::end(seq),   boost::end(seq)}; }

    // the 3 facade operations
    bool equal(sequence_iterator const& rhs) const {
        return ((_ocur==_oend) && (rhs._ocur==rhs._oend))
            || (std::addressof(_seq) == std::addressof(rhs._seq) &&
                _ocur == rhs._ocur && _oend == rhs._oend &&
                _icur == rhs._icur && _iend == rhs._iend);
    }

    void increment() {
        if (++_icur == _iend) {
            ++_ocur;
            setup();
        }
    }

    V& dereference() const {
        assert(_ocur != _oend);
        assert(_icur != _iend);
        return *_icur;
    }

  private:
    void setup() { // to be called after entering a new sub-range in the sequence
        while (_ocur != _oend) {
            _icur = boost::begin(detail::get(*_ocur));
            _iend = boost::end(detail::get(*_ocur));

            if (_icur != _iend)
                break;
            ++_ocur; // skid over, this enables simple increment() logic
        }
    }

    sequence_iterator(Seq& seq, OuterIt cur, OuterIt end)
        : _seq(seq), _ocur(cur), _oend(end) { setup(); }
};

template <typename Seq> auto buffers_begin(Seq& seq) { return sequence_iterator<Seq>::begin(seq); }
template <typename Seq> auto buffers_end(Seq& seq)   { return sequence_iterator<Seq>::end(seq); }

// DEMO
struct element {
    int peek_first() const { return m_arr[0]; }

    auto begin() const { return std::begin(m_arr); } 
    auto end() const   { return std::end(m_arr);   } 
    auto begin()       { return std::begin(m_arr); } 
    auto end()         { return std::end(m_arr);   } 

  private:
    int m_arr[4] { };
};

namespace boost { // range adapt
    template <> struct range_iterator<element> { using type = int*; };
    // not used, but for completeness:
    template <> struct range_iterator<element const> { using type = int const*; };
    template <> struct range_const_iterator<element> : range_iterator<element const> {};
}

#include <algorithm>
#include <iostream>
#include <vector>

template <typename Output, typename Input, typename Operation>
size_t process(Output& output, Input const& input, Operation op) {
    auto ib = boost::begin(input), ie = boost::end(input);
    auto ob = boost::begin(output), oe = boost::end(output);

    size_t n = 0;
    for (;ib!=ie && ob!=oe; ++n) {
        op(*ob++, *ib++);
    }
    return n;
}

int main() {
    element a, b, c;
    std::vector<std::reference_wrapper<element> > seq {a,b,c}; // tie 3 elements together as buffer sequence

    //// Also supported, container of range objects directly:
    // std::list<element> seq(3); // tie 3 elements together as buffer sequence
    // element& b = seq[1];

    std::vector<int> const samples { 
         1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12,
        13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24,
        25, 26, 27, 28, 29, 30, 31, 32
    };

    using boost::make_iterator_range;
    auto input  = make_iterator_range(samples);
    auto output = make_iterator_range(buffers_begin(seq), buffers_end(seq));

    while (auto n = process(output, input, [](int& el, int sample) { el += sample; })) {
        std::cout << "Copied " << n << " samples, b starts with " << b.peek_first() << "\n";
        input.advance_begin(n);
    }
}