Search code examples
c++algorithmboostboost-multi-indexboost-range

boost multi_index_container, range mutating algorithms and constness


I'm using boost multi_index_container, which is queried by equal_range and the result returned from the function using range::join and boost::any_range
The any_range Reference argument is defined as const reference to the type - must be const because of the multi_index_container nature, not quite sure about the reference. Example:

typedef boost::any_range<TestData, boost::random_access_traversal_tag,
                         const TestData &, std::ptrdiff_t> TestRange;

Now what I need is to use mutating range algorithms like boost::sort, unique, etc., which obviously cannot run on range because of the constness of elements in range.
Is it any workaround except copying elements into new container?

EDIT 1:
struct and MIC example:

struct TestData {
  TestData()
      : m_strMem01("test"), m_intMem02(rand()),
        m_boolMem03(rand() < RAND_MAX / 2) {}
  std::string m_strMem01;
  int m_intMem02;
  bool m_boolMem03;
};

typedef boost::multi_index_container<
    TestData,
    bmi::indexed_by<
        bmi::random_access<bmi::tag<struct RndKey1>>,
        bmi::ordered_non_unique<
            bmi::tag<struct Key1>,
            bmi::composite_key<
                TestData,
                bmi::member<TestData, std::string, &TestData::m_strMem01>,
                bmi::member<TestData, bool, &TestData::m_boolMem03>>>,
        bmi::ordered_non_unique<
            bmi::tag<struct Key4>,
            bmi::composite_key<
                TestData,
                bmi::member<TestData, std::string, &TestData::m_strMem01>,
                bmi::member<TestData, bool, &TestData::m_intMem02>>>,
        bmi::ordered_non_unique<
            bmi::tag<struct Key2>,
            bmi::member<TestData, int, &TestData::m_intMem02>>,
        bmi::ordered_non_unique<
            bmi::tag<struct Key3>,
            bmi::member<TestData, bool, &TestData::m_boolMem03>>>>
    TestDataContainer;

Solution

  • OK, once you have your range you really can't sort it or somehow rearrange it because the order of elements is fixed by the index --this is an absolutely fundamental invariant of indices enforced by the constness of elements, much as you'd find with say a std::set. Rather than doing a full copy to an external container you can create a more lightweight view out of pointers or references to the original elements that can later be manipulated as you wish. This is an example with views constructed as std::vectors of std::reference_wrappers to the elements:

    Live On Coliru

    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/ordered_index.hpp>
    #include <boost/multi_index/member.hpp>
    #include <boost/range/algorithm/sort.hpp>
    #include <boost/range/join.hpp>
    #include <functional>
    #include <iostream>
    #include <vector>
    
    using namespace boost::multi_index;
    
    struct X
    {
      int x,y;
    };
    
    std::ostream& operator<<(std::ostream& os,const X& a)
    {
      return os<<"{"<<a.x<<","<<a.y<<"}";
    }
    
    typedef multi_index_container<
      X,
      indexed_by<
        ordered_non_unique<member<X,int,&X::x>>
      >
    > multi_t;
    
    struct view:std::vector<std::reference_wrapper<const X>>
    {
      using base=std::vector<std::reference_wrapper<const X>>;
    
      template<typename InputIterator>
      view(InputIterator first,InputIterator last):base(first,last){}
    
      template<typename InputIterator>
      view(const std::pair<InputIterator,InputIterator> p):base(p.first,p.second){}  
    };
    
    int main()
    {
      multi_t m={{0,1},{0,0},{0,2},{1,3},{1,1},{2,0},{2,1}};
    
      view v1=m.equal_range(0); // elements with x==0
      view v2=m.equal_range(2); // elements with x==2
      auto v3=boost::range::join(v1,v2); // join them
      boost::range::sort(v3,[](const X& a,const X& b){return a.y<b.y;}); // sort them by y
      for(const auto& x:v3)std::cout<<x<<" "; // output
    }
    

    Output:

    {0,0} {2,0} {0,1} {2,1} {0,2}