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.
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.
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.
How would you go about implementing this composite traits making use of what's already there?
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.
Here you go:
#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:
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.boost::mpl
and boost::fusion
, i hope the metafunctions names are explicit enough, feel free to ask any questionmpl
sequence as a template parameter since you do not have access to C++11 and variadic templates.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