Поиск составного шаблона признаков для итераторов преобразования boost

Настройки

Если вы хотите, чтобы итераторы возились с тем, что они повторяют, прежде чем вернуть это, boost::transform_iterator довольно хороши. Вы передаете им унарную функцию, которая преобразует результат базового итератора operator*(), а преобразующий итератор затем возвращает это:

template<typename Map>
struct iterator_transform_traits_map_second {
  typedef typename Map::value_type    value_type;
  typedef typename Map::mapped_type   result_type;
        result_type& operator()(      value_type& v) const {return v.second;}
  const result_type& operator()(const value_type& v) const {return v.second;}
};

typedef 
    boost::transform_iterator<iterator_transform_traits_map_second> 
    transformed_iterator;

Все идет нормально. Но.

К чему это приводит

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

  • iterator_transform_traits_map_first
  • iterator_transform_traits_map_second
  • iterator_transform_traits_map_deref (разыменовывает любую запись контейнера)
  • iterator_transform_traits_map_deref_second (разыменовывает запись карты second)
  • iterator_transform_traits_map_dynamic_cast (выполняет dynamic_cast<>() запись любого контейнера)
  • iterator_transform_traits_map_any_second (выполняет any_cast<>() над записью карты second)

Конечно, при этом упускается множество полезных (потому что они еще никому не нужны) и совсем не масштабируется. . Мне только что было поручено написать итератор, который разыменовывает second записи карты и выполняет dynamic_cast<>(), и я отказался просто добавить iterator_transform_traits_map_dynamic_cast_deref_second и двигаться дальше.

Что я хочу

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

typedef 
    boost::transform_iterator< 
        iterator_transform_traits< iter_transf_tr_second
                                 , iter_transf_tr_deref
                                 , iter_transf_tr_dynamic_cast<derived>
                                 >
                             > 
    transformed_iterator;

Моя текущая идея состоит в том, чтобы рекурсивно получить шаблон оболочки и заставить его рекурсивно вызывать все черты, передавая вывод от одного к другому. Я сделал что-то подобное десять лет назад и имею общее представление о том, как это сделать. Однако в последний раз я делал это пешком. То есть всю метамагию шаблона я реализовал сам.

Глупо, конечно, учитывая, что теперь у нас есть boost.mpl, boost.fusion и т. д., поэтому я бы предпочел использовать то, что уже есть. Однако после того, как я повозился с этим днем, стало ясно, что мне нужно многому научиться, прежде чем я это сделаю. И хотя я не против узнать все это, у меня есть кое-кто, кто дышит мне в затылок, которому нравится то, что я делаю, но он говорит, что ему все равно придется махнуть рукой, потому что есть этот крайний срок... Теперь у меня есть выбор: просто напишите чертову iterator_transform_traits_map_dynamic_cast_deref_second, скопируйте много кода, который гнил десять лет и строился на нем, или придумайте чистое решение.

Вот где вы входите.

Вопрос

Как бы вы реализовали составные характеристики, используя уже существующие?

Платформа

Однако есть одна проблема: мы используем встроенную платформу и застряли на GCC 4.1.2, что означает C++03, TR1 и буст 1,52. Никаких переменных аргументов шаблона, никаких decltype и всех этих причудливых вещей.


person sbi    schedule 30.01.2014    source источник
comment
Думаю, кто-то должен настоять на том, чтобы вы сменили целевую платформу.   -  person thecoshman    schedule 30.01.2014
comment
Можно ли создать итератор, который берет два других итератора преобразования и связывает их в цепочку?   -  person user1781290    schedule 30.01.2014
comment
@user1781290 user1781290: Я не вижу преимущества в цепочке итераторов, а не в используемых ими унарных функциях. Что мне не хватает?   -  person sbi    schedule 30.01.2014
comment
@sbi Вы правы. тогда я неправильно понял ваш вопрос   -  person user1781290    schedule 30.01.2014
comment
Faux-variadics приведет к очень неприятному дублированию. Лучше ИМО пойти с чем-то вроде tr_second<tr_deref<tr_dynamic_cast<Derived, tr_identity>>>.   -  person Puppy    schedule 30.01.2014
comment
@DeadMG: Это действительно то, что я хочу, но поскольку я хочу, чтобы эти черты оставались такими же простыми, как сейчас (см. выше), я хочу обернуть их чем-то вроде этого.   -  person sbi    schedule 30.01.2014
comment
Я думаю, определенно стоило бы использовать Phoenix для определения псевдолямбд, которые вы можете использовать.   -  person Puppy    schedule 30.01.2014
comment
Обратите внимание, действительно ли это помогает, поскольку вы специально спрашивали о признаках, но если все, что вам действительно нужно, это иметь составные операции последовательности, вы можете использовать Boost.Range, в частности, адаптеры. Например. boost::adaptors::indirected (для iterator_transform_traits_map_deref) или boost::adaptors::transformed для более общих вещей.   -  person ltjax    schedule 30.01.2014


Ответы (1)


Ну вот:

iterator_transform_traits.hpp

#include <boost/mpl/vector.hpp>
#include <boost/mpl/back.hpp>
#include <boost/mpl/front.hpp>
#include <boost/mpl/fold.hpp>
#include <boost/mpl/push_back.hpp>
#include <boost/mpl/pop_front.hpp>

#include <boost/fusion/adapted/mpl.hpp>
#include <boost/fusion/container/vector/convert.hpp>
#include <boost/fusion/algorithm/iteration/fold.hpp>

#include <boost/ref.hpp>

template<typename IteratorTraitsSequence, typename Container>
class   iterator_transform_traits
{
  public:
    struct type
    {
    private:
        struct plcaholder_resolver
        {
          template<typename IteratorTraits, typename IteratorLambda>
          struct apply
          {
            typedef typename boost::mpl::push_back<IteratorTraits,
            typename boost::mpl::apply<typename boost::mpl::lambda<IteratorLambda>::type, typename boost::mpl::back<IteratorTraits>::type::result_type>::type>::type type;
          };
        };

        struct begin_value
        {
            typedef typename Container::value_type   result_type;
        };

        typedef typename boost::mpl::pop_front<typename boost::mpl::fold<IteratorTraitsSequence, boost::mpl::vector<begin_value>, plcaholder_resolver>::type>::type iterator_traits;

    public:
        typedef typename boost::mpl::front<iterator_traits>::type::value_type value_type;
        typedef typename boost::mpl::back<iterator_traits>::type::result_type result_type;

    public:
        struct recursive_iterator_modifier
        {
          template<class> struct result;

          template<class F, typename CurrentResult, typename IteratorTrait>
          struct result<F(CurrentResult&, const IteratorTrait&)> 
          {
              typedef typename IteratorTrait::result_type& type;
          };

          template<class F, typename CurrentResult, typename IteratorTrait>
          struct result<F(const CurrentResult&, const IteratorTrait&)> 
          {
              typedef const typename IteratorTrait::result_type& type;
          };

          template<class F, typename CurrentResult, typename IteratorTrait>
          struct result<F(const boost::reference_wrapper<CurrentResult>&, const IteratorTrait&)> 
          {
              typedef typename IteratorTrait::result_type& type;
          };


          template<typename CurrentResult, typename IteratorTrait>
          typename IteratorTrait::result_type&
          operator()(CurrentResult& modified, const IteratorTrait& it)
          {
              return (it(modified));
          }

          template<typename CurrentResult, typename IteratorTrait>
          const typename IteratorTrait::result_type&
          operator()(const CurrentResult& modified, const IteratorTrait& it)
          {
              return (it(modified));
          }

          template<typename CurrentResult, typename IteratorTrait>
          typename IteratorTrait::result_type&
          operator()(const boost::reference_wrapper<CurrentResult>& modified, const IteratorTrait& it)
          {
              return (it(modified.get()));
          }

        };

    public:
        result_type& operator()(value_type& v) const 
        {
            return boost::fusion::fold(iterator_traits_vector_, boost::ref(v), recursive_iterator_modifier());
        }

        const result_type& operator()(const value_type& v) const 
        {
            return boost::fusion::fold(iterator_traits_vector_, boost::ref(v), recursive_iterator_modifier());
        }


    private:
        typedef typename boost::fusion::result_of::as_vector<iterator_traits>::type  iterator_traits_vector;

        iterator_traits_vector  iterator_traits_vector_;
    };
};

Вы используете это так:

#include <map>
#include <string>
#include <iostream>
#include <typeinfo>

#include "iterator_transform_traits.hpp"

template<typename Pair>
struct iterator_transform_traits_map_second {
  typedef Pair    value_type;
  typedef typename Pair::second_type   result_type;
        result_type& operator()(      value_type& v) const {return v.second;}
  const result_type& operator()(const value_type& v) const {return v.second;}
};

template<typename Dereferenced>
struct iterator_transform_traits_deref {};

template<typename Dereferenced>
struct iterator_transform_traits_deref<Dereferenced*> {
  typedef Dereferenced*    value_type;
  typedef Dereferenced   result_type;
        result_type& operator()(      value_type& v) const {return *v;}
  const result_type& operator()(const value_type& v) const {return *v;}
};


typedef std::map<std::string, std::string*>  string_ptr_map;

typedef iterator_transform_traits<boost::mpl::vector<iterator_transform_traits_map_second<boost::mpl::_1>, iterator_transform_traits_deref<boost::mpl::_1> >, string_ptr_map>::type Transformer;
typedef boost::transform_iterator<Transformer, string_ptr_map::iterator> string_ptr_map_second_deref_iterator;


int main()
{
    string_ptr_map  map;
    map["key1"] = new std::string("value1");
    map["key2"] = new std::string("value2");
    map["key3"] = new std::string("value3");

    for(string_ptr_map_second_deref_iterator it(map.begin(), Transformer()), ite(map.end(), Transformer()); it != ite; ++it)
    {
        std::cout << *it << std::endl;
    }

    return 0;
}

Теперь немного информации:

  • Каждый iterator_transform_trait должен быть шаблоном для value_type, который он получит в качестве параметра, а не в контейнере, иначе вы не сможете автоматически связать их.
  • Существует множество небольших операций метапрограммирования с использованием boost::mpl и boost::fusion, я надеюсь, что имена метафункций достаточно явны, не стесняйтесь задавать любые вопросы.
  • Вам необходимо использовать последовательность mpl в качестве параметра шаблона, поскольку у вас нет доступа к C++11 и шаблонам с переменным числом переменных.
  • Вот онлайн-версия компиляции: http://rextester.com/ZIYG56999
  • Это было забавное упражнение, которое вызвало у меня некоторую головную боль :)
person Drax    schedule 30.01.2014