使用boost :: join合并多个数组

时间:2018-02-15 12:50:24

标签: c++ arrays boost

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

我在类element中定义了一个成员数组。

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

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

2 个答案:

答案 0 :(得分:3)

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

typedef int array[4];
array& get_arr() { return m_arr; }

这样数组大小就不会丢失。

在性能方面,通过联接视图访问元素的成本非零。双循环将是最有效的,并且也易于阅读,例如:

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

答案 1 :(得分:2)

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

在盒子外面思考,看起来你真的想要创建一个缓冲区抽象,它允许你像IO一样进行分散/收集,只需很少的分配。

  

碰巧,Boost Asio对此有很好的抽象²,你可以使用它:http://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/MutableBufferSequence.html

     

正如我在earlier iteration of this answer code中发现的那样,抽象可悲地仅适用于通过本地char类型元素访问的缓冲区。那不好。

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

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

std::vector<element> seq(3); // tie 3 elements together as buffer sequence
element& b = seq[1];

或者,没有任何进一步的改变,参考:

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

底部的C ++版本演示了这种方法 Live On Coliru

迭代器实现

我使用过Boost Range和Boost Iterator:

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(); }
};

这与boost::asio::buffers_iterator基本上是同一种迭代器,但它不假设元素类型。现在,为任何范围序列创建sequence_iterator都非常简单:

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

实施测试计划

<强> Live On Coliru

// 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);
    }
}

打印

Copied 12 samples, b starts with 5
Copied 12 samples, b starts with 22
Copied 8 samples, b starts with 51

完整列表,兼容C ++ 11

<强> Live On Coliru

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

¹我忽略了编译时成本和auto x = complicated_range_composition模式的潜伏危险,当复杂范围组合包含对临时工具的引用时:这是UB错误的常见来源

²已被各种其他库采用,如Boost Beast,Boost Process,似乎已经进入了C ++ 20的网络TS:Header <experimental/buffer> synopsis (PDF)