Поиск составного паттерна черт для итераторов преобразования буста
Настройки
Когда вы хотите иметь итераторы, которые перебирают то, что они перебирают, перед возвратом, 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 и boost 1.52. Нет переменных шаблона аргументов, нет decltype
и все такое модное.
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
sequence как параметр шаблона, так как у вас нет доступа к C++11 и шаблонам с переменным числом аргументов. - Вот онлайн версия компиляции: http://rextester.com/ZIYG56999
- Это было забавное упражнение, которое вызвало у меня головную боль:)