Как перебирать индексы контейнера Boost Multi_index?

У меня есть контейнер boost::multi_index::multi_index_container с шестью разными индексами ordered_non_unique. Идея состоит в том, чтобы иметь возможность сортировать данные по этим шести индексам (как средство ранжирования решений с использованием нескольких критериев).

Проблема, с которой я сталкиваюсь, заключается в получении индексов в цикле. Boost требует, чтобы я использовал следующий синтаксис, чтобы получить (скажем) 4-й индекс:

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

Что я пытаюсь сделать, так это поместить приведенный выше оператор в цикл, который работает от 0 до 5, чтобы я мог использовать один и тот же код для всех шести индексов. Конечно, следующий фрагмент кода не скомпилируется:

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
  ...
 }

Поскольку get<i> — это шаблон, который необходимо определить во время компиляции.

Как я могу использовать вышеуказанную функциональность, чтобы мне не нужно было дублировать код 6 раз? Кажется, boost:preprocessor может помочь в этом, но я не могу точно понять, как его использовать - любые указатели будут очень признательны!

EDIT: я был бы очень признателен за решение, отличное от C++ 11, чтобы дополнить отличный ответ, используя его. (По нетехническим причинам я вынужден использовать устаревшую версию gcc).


person TCSGrad    schedule 09.07.2016    source источник


Ответы (2)


Если вы не можете использовать C++14, перенос на C++03 с Boost может выглядеть так:

Демонстрационная версия Coliru

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

что значительно уродливее. Другой альтернативой является использование препроцессора с BOOST_PP_REPEAT. Я сам не уверен, какое решение выглядит лучше всего, хотя я бы предпочел первое, поскольку оно лучше подготовлено для обновления C++ 14:

Демо-версия Coliru

#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
}
person Joaquín M López Muñoz    schedule 09.07.2016
comment
В итоге остановился на последнем варианте, так как он имеет минимальный шаблонный код! - person TCSGrad; 10.07.2016

Для этого вам потребуется некоторое метапрограммирование, а именно замена for времени выполнения конструкцией времени компиляции, которая может перебирать типы, представляющие константы 0,...,5. Ниже показан очень простой static_for, основанный на возможностях C++14. Обратите внимание, что универсальной лямбда-функции, заменяющей тело for, передается std::integral_constant i, чей числовое значение получается с помощью operator(), поэтому "i()" в "auto& index=result.get<i()>();".

Демо-версия Coliru

#include <type_traits>

template<typename T,T N0,T N1,typename F>
void static_for(F f)
{
  static_for<T,N0,N1>(f,std::integral_constant<bool,(N0<N1)>{});
}

template<typename T,T N0,T N1,typename F>
void static_for(F f,std::true_type)
{
  f(std::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,std::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;
using result_multi=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>>
  >
>;

#include <iostream>

int main()
{
  result_multi result={0,1,2};

  static_for<int,0,6>([&](auto i){
    auto& index=result.get<i()>();

    std::cout<<"index #"<<i()<<": ";
    for(int x:index)std::cout<<x<<" ";
    std::cout<<"\n";
  });
}

Вывод:

index #0: 0 1 2 
index #1: 2 1 0 
index #2: 0 1 2 
index #3: 2 1 0 
index #4: 0 1 2 
index #5: 2 1 0 
person Joaquín M López Muñoz    schedule 09.07.2016
comment
Спасибо за ответ С++ 11! К сожалению, у меня нет доступа к компилятору С++ 11 на моем рабочем месте (но, к счастью, это позволяет мне использовать boost!), поэтому, если вы можете опубликовать другой ответ, используя макросы boost и/или препроцессора, я был бы очень рад. рад принять это! (Ваш ответ в настоящее время полезен для меня в другом контексте — помогите убедить людей обновить gcc!) - person TCSGrad; 09.07.2016