Как поменять местами два элемента mpl :: vector? - PullRequest
5 голосов
/ 28 октября 2011

Я пишу шаблонную функцию, которая должна поменять местами два элемента boost::mpl::vector (аналогично std::swap). Сложность в том, что нет понятия переменной во время компиляции. Я написал черновик, но мне интересно, есть ли лучшие способы подойти к этому.

Мой текущий эскиз кода извлекает интегральный индекс из итераторов и выполняет копирование типа последовательности с заменой элементов. Вопрос - можно ли это сделать лучше:

#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

Эта метафункция может использоваться как:

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() );

и результат 1,2,4,6,5,3,

1 Ответ

1 голос
/ 01 декабря 2011

Вот решение, использующее только метафункции MPL, без явной рекурсии.Идея состоит в том, чтобы начать с копирования всех значений между началом последовательности и первым значением, которое нужно заменить, вставить второе значение, скопировать среднее, вставить первое значение и, наконец, скопировать конец.

AНедостаток этого метода в том, что итераторы должны формировать допустимый диапазон: Second не должно быть раньше First.Я не думаю, что есть какой-либо способ преодолеть это ограничение с помощью этого решения, но это не кажется невыносимым требованием.

Вот код:

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