Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to swap two elements of an mpl::vector?

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,

like image 236
Marcin Avatar asked Nov 13 '22 15:11

Marcin


1 Answers

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;
};
like image 171
Luc Touraille Avatar answered Dec 26 '22 23:12

Luc Touraille