Search code examples
c++boostboost-multi-indexboost-preprocessor

How to iterate through the indices of a Boost Multi_index container?


I have a boost::multi_index::multi_index_container container having six different ordered_non_unique indices. The idea is to be able to sort the data along these six indices (as a means of ranking solutions using multiple criteria).

The issue I'm facing is while retrieving the indices in a loop. Boost requires me to use the following syntax to get (say) the 4th index:

const result_multi::nth_index<1>::type &legs_index = result.get<4>();

What I'm trying to do is to put the above statement in a loop that runs between 0 to 5, so that I can use the same code on all six indices. Of course, the following code fragment would not compile:

for (size_t i = 0; i < 5; ++i) {
  const result_multi::nth_index<1>::type &index = result.get<i>();
  ...
  ... Display result sorted along the i-th index
  ...
 }

As the get<i> is a template that needs to be defined during compilation.

How can I use achieve the above functionality so that I don't need to duplicate the code 6 times? It seems boost:preprocessor may help in doing so, but am unable to figure out exactly how to use it - any pointers would be really appreciated!

EDIT: I would really appreciate a non-C++11 solution as well, to complement the excellent answer using one. (For non-technical reasons, I'm forced to use a legacy version of gcc).


Solution

  • If you can't use C++14, the backporting to C++03 with Boost could look like this:

    Live Coliru Demo

    #include <boost/type_traits/integral_constant.hpp>
    
    template<typename T,T N0,T N1,typename F>
    void static_for(F f)
    {
      static_for<T,N0,N1>(f,boost::integral_constant<bool,(N0<N1)>());
    }
    
    template<typename T,T N0,T N1,typename F>
    void static_for(F f,boost::true_type)
    {
      f(boost::integral_constant<T,N0>());
      static_for<T,N0+1,N1>(f);
    }
    
    template<typename T,T N0,T N1,typename F>
    void static_for(F f,boost::false_type)
    {
    }
    
    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/ordered_index.hpp>
    #include <boost/multi_index/identity.hpp>
    
    using namespace boost::multi_index;
    typedef multi_index_container<
      int,
      indexed_by<
        ordered_non_unique<identity<int> >,
        ordered_non_unique<identity<int>,std::greater<int> >,
        ordered_non_unique<identity<int> >,
        ordered_non_unique<identity<int>,std::greater<int> >,
        ordered_non_unique<identity<int> >,
        ordered_non_unique<identity<int>,std::greater<int> >
      >
    > result_multi;
    
    #include <iostream>
    
    struct body
    {
      body(result_multi& result):result(result){}
    
      template<typename I>
      void operator()(I){
        typename result_multi::nth_index<I::value>::type& index=
          result.get<I::value>();
    
        std::cout<<"index #"<<I::value<<": ";
        for(typename result_multi::nth_index<I::value>::type::iterator
              b=index.begin(),
              e=index.end();
            b!=e;++b){
          std::cout<<*b<<" ";
        }
        std::cout<<"\n";
      }
    
      result_multi& result;
    };
    
    int main()
    {
      result_multi result;
      for(int i=0;i<3;++i)result.insert(i);
    
      static_for<int,0,6>(body(result));
    }
    

    which is considerably uglier. Another alternative is to use the preprocessor with BOOST_PP_REPEAT. I'm not sure myself which solution looks best, though I think I'd favor the first as it's better prepared for C++14 upgrading:

    Live Coliru Demo

    #include <boost/multi_index_container.hpp>
    #include <boost/multi_index/ordered_index.hpp>
    #include <boost/multi_index/identity.hpp>
    
    using namespace boost::multi_index;
    typedef multi_index_container<
      int,
      indexed_by<
        ordered_non_unique<identity<int> >,
        ordered_non_unique<identity<int>,std::greater<int> >,
        ordered_non_unique<identity<int> >,
        ordered_non_unique<identity<int>,std::greater<int> >,
        ordered_non_unique<identity<int> >,
        ordered_non_unique<identity<int>,std::greater<int> >
      >
    > result_multi;
    
    #include <boost/preprocessor/repetition/repeat.hpp>
    #include <iostream>
    
    int main()
    {
      result_multi result;
      for(int i=0;i<3;++i)result.insert(i);
    
    #define BODY(z,i,_)                                        \
    {                                                          \
      result_multi::nth_index<i>::type& index=result.get<i>(); \
                                                               \
      std::cout<<"index #"<<i<<": ";                           \
      for(result_multi::nth_index<i>::type::iterator           \
            b=index.begin(),                                   \
            e=index.end();                                     \
          b!=e;++b){                                           \
        std::cout<<*b<<" ";                                    \
      }                                                        \
      std::cout<<"\n";                                         \
    }
    
    BOOST_PP_REPEAT(6,BODY,~)
    
    #undef BODY
    }