I'm writing a template function which should swap two elements of a boost::mpl::vector
(similarly to std::swap
). The difficult part is there is no concept of a variable during compile time. I have written a draft but I wonder if there are better ways to approach this.
My current code sketch extracts an integral index from iterators and performs a copy of the sequence type with the elements swapped. The question is - can this be done better:
#include <boost/mpl/distance.hpp>
#include <boost/mpl/begin_end.hpp>
#include <boost/mpl/int.hpp>
#include <boost/mpl/eval_if.hpp>
#include <boost/mpl/comparison.hpp>
#include <boost/mpl/equal.hpp>
#include <boost/mpl/clear.hpp>
#include <boost/mpl/next_prior.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/at.hpp>
#include <boost/mpl/or.hpp>
using boost::mpl::distance;
using boost::mpl::begin;
using boost::mpl::end;
using boost::mpl::next;
using boost::mpl::at;
using boost::mpl::or_;
using boost::mpl::int_;
using boost::mpl::eval_if;
using boost::mpl::greater;
using boost::mpl::equal;
using boost::mpl::clear;
using boost::mpl::push_back;
namespace boost { namespace mpl {
template<template<typename, typename> class T, class A, class B>
struct eval2 {
typedef typename T<typename A::type, typename B::type>::type type;
};
namespace details {
template <typename Dest_seq, typename It_end, typename It_first, typename It_second, typename It_idx>
struct copy_and_swap {
private:
typedef typename eval_if< is_same<It_idx, It_first>,
eval2<push_back, Dest_seq, deref<It_second> >,
eval_if<is_same<It_idx, It_second>,
eval2<push_back, Dest_seq, deref<It_first> >,
eval2<push_back, Dest_seq, deref<It_idx> >
>
>::type Limit_idx;
typedef typename next<It_idx>::type it_idx_next;
public:
// next step
typedef typename eval_if <is_same<it_idx_next, It_end>,
New_seq,
copy_and_swap<New_seq,
It_end,
It_first,
It_second,
it_idx_next>
>::type type;
};
} // namespace details
template<typename Seq, typename Begin, typename End>
struct swap {
private:
typedef typename begin<Seq>::type it_begin;
typedef typename end<Seq>::type it_end;
// get an empty container type "compatible" with Seq
typedef typename clear<Seq>::type Container_t;
// border case - swap self
typedef typename is_same<Begin, End>::type swap_self;
// border case - less than 2 elements in sequence
typedef typename less<size<Seq>, int_<2> >::type no_swap;
public:
// perform the element swapping
typedef typename eval_if <or_<swap_self, no_swap>,
Seq,
details::copy_and_swap<Container_t,
it_end,
Begin,
End,
it_begin >
>::type type;
};
} // namespace mpl
} // namespace boost
This metafunction can be used like:
struct value_printer {
template< typename U > void operator()(U x) {
std::cout << x << ',';
}
};
typedef vector_c<int, 1, 2, 3, 6, 5, 4>::type test_vect;
typedef begin<test_vect>::type it_beg;
typedef advance<it_beg, int_<2> >::type it;
typedef advance<it_beg, int_<5> >::type it_stop;
typedef m_swap<test_vect, it_stop, it>::type result;
boost::mpl::for_each< result >( value_printer() );
and the result is 1,2,4,6,5,3,
Here is a solution using only MPL metafunctions, without explicit recursion. The idea is to start by copying all the values between the beginning of the sequence and the first value to swap, insert the second value, copy the middle, insert the first value, and finally copy the end.
A disadvantage of this method is that the iterators must form a valid range: Second
must not be before First
. I don't think there is any way to overcome this restriction with this solution, but it does not seem like an unbearable requirement.
Here is the code:
// Precondition: [First, Second] is a valid range in Seq
template< typename Seq, typename First, typename Second >
struct swap {
private:
typedef typename begin< Seq >::type begin;
typedef typename end< Seq >::type end;
typedef typename clear< Seq >::type empty_container;
// Insert values from begin to first
typedef typename
copy<
iterator_range< begin, First >,
back_inserter< empty_container >
>::type prefix;
// Insert second value
typedef typename
push_back<
prefix, typename
deref< Second >::type
>:: type prefixSecond;
// Insert values from first+1 to second
typedef typename
copy<
iterator_range< typename next< First >::type, Second >,
back_inserter< prefixSecond >
>::type prefixSecondMiddle;
// Insert first value
typedef typename
push_back<
prefixSecondMiddle, typename
deref< First >::type
>::type prefixSecondMiddleFirst;
// Insert values from second+1 to end
typedef typename
copy<
iterator_range< typename next< Second >::type, end >,
back_inserter< prefixSecondMiddleFirst >
>::type prefixSecondMiddleFirstSuffix;
public:
typedef prefixSecondMiddleFirstSuffix type;
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With