Looking for a composite traits pattern for boost's transform iterators
boost, c++, c++03, traits
Solution
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 :)
Problem
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.