Pregunta

The setting

When you want to have iterators that twiddle with what they are iterating over before returning it, boost::transform_iterator are pretty good. You pass them a unary function that transforms the result of the underlying iterator's operator*() and the transforming iterator then returns that:

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;

So far, so good. But.

What mess this leads to

Your co-workers like this shiny new tool and also start to employ it, and pretty soon someone collects in a header what you all have come up with so far. Here's ours:

  • iterator_transform_traits_map_first
  • iterator_transform_traits_map_second
  • iterator_transform_traits_map_deref (dereferences any container's entry)
  • iterator_transform_traits_map_deref_second (dereferences the map entry's second)
  • iterator_transform_traits_map_dynamic_cast (performs a dynamic_cast<>() any container's entry)
  • iterator_transform_traits_map_any_second (performs an any_cast<>() on map entry's second)

Of course, this leaves out a lot of useful ones (because nobody needed them yet), and it does not scale at all. I was just tasked to write an iterator that dereferences the map entry's second and does a dynamic_cast<>(), and I being who I am refused to just add a iterator_transform_traits_map_dynamic_cast_deref_second and move on.

What I want

Instead I am trying to write a few basic traits and a compile-time composing traits that allows to name a couple of these as template parameters and simply pipelines the invocations. Ideally, I want something like this:

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

My current idea is to recursively derive a wrapper template and and have that recursively call all the traits, passing the output from one to the next. I have done something like this a decade ago and have a basic idea about how to do that. However, the last time I have done this was on foot. That is, I implemented all the template meta magic myself.

That is silly, of course, given that we now have boost.mpl, boost.fusion, etc., so I'd rather employ what's already there. However, after tinkering with this for an afternoon it's clear that I would have lots to learn before I get this done. And while I am not opposed to learn all that, I have someone breathing down my neck who likes what I do, but says he'd need to pull the plug anyway, because there's this deadline... I now have the choice to just write the damn iterator_transform_traits_map_dynamic_cast_deref_second, copy lots of code that's been rotting for a decade and built on that, or come up with a clean solution.

That's where you come in.

The question

How would you go about implementing this composite traits making use of what's already there?

The platform

There is, however, one problem: We are on an embedded platform and stuck with GCC 4.1.2, which means C++03, TR1, and boost 1.52. No variable template arguments, no decltype and all that fancy stuff.

¿Fue útil?

Solución

Here you go:

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_;
    };
};

You use it like this :

#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;
}

Now some infos:

  • Each iterator_transform_trait has to be templated on the value_type he is going to receive as parameter not on a container or else you cannot automatically chain them.
  • There is a lot of little metaprogramming operations using boost::mpl and boost::fusion, i hope the metafunctions names are explicit enough, feel free to ask any question
  • You need to use an mpl sequence as a template parameter since you do not have access to C++11 and variadic templates.
  • Here is an online compiling version : http://rextester.com/ZIYG56999
  • This was a fun exercise which caused me some headache :)
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top