Reputation: 12108
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).
Upvotes: 2
Views: 1156
Reputation: 5658
If you can't use C++14, the backporting to C++03 with Boost could look like this:
#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:
#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
}
Upvotes: 2
Reputation: 5658
You need some metaprogramming to do this, namely replacing the run-time for
with a compile-time construct that can iterate over types representing the constants 0,...,5. A very simple static_for
relying on C++14 capabilities is shown below. Note that the generic lambda function substituting for the for
body is passed a std::integral_constant
i
whose numerical value is obtained through operator()
, hence the "i()
" in "auto& index=result.get<i()>();
".
#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";
});
}
Output:
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
Upvotes: 1