У меня появилась идея, как сделать распаковку tuple-ов, тех что в библиотеке boost, более удобной.
Обычный синтаксис с использованием get методов
int value = boost::tuples::get<0>(mytuple);
выглядит страшновато, когда нужно часть одного кортежа скопировать в другой, короче разбить один большой кортеж, на несколько маленьких... в общем хочу я
странного такого:
using namespace boost::tuples;
tuple<int, std::string> tuple1(44, "aaaaa");
tuple<double, int> tuple2(55.33f, 111);
tuple<int> tuple3(88);
tuple<int, std::string, double, int, int> tuple4(1, "'2'", 3.14159f, 4, 5);
(tuple1, tuple2, tuple3)= tuple4;
int local = 0;
(tuple1, tuple2, tie(local)) = tuple4;
результат должен быть такой,
до:
tuple4 = (1 '2' 3.14159 4 5)
tuple1 = (44 aaaaa)
tuple2 = (55.33 111)
tuple3 = (88)
и после:
tuple4 = (1 '2' 3.14159 4 5)
tuple1 = (1 '2')
tuple2 = (3.14159 4)
tuple3 = (5)
local = 5
причем это должно работать в обе стороны, так:
(tuple1, tuple2, tie(local)) = tuple4;
и так:
tuple4 = (tuple1, tuple2, tie(local));
это уже похоже на pattern matching в функциональных языках
в общем сразу приведу результат =)
#include <boost/tuple/tuple.hpp>
#include <boost/type_traits/add_reference.hpp>
namespace boost { namespace tuples {
template < class T , class H>
struct split_;
template <
class head, class tail,
class ex_head, class ex_tail
>
struct split_< cons<head, tail>, cons<ex_head, ex_tail> >
{
typedef cons<
head,
typename split_<
tail,
cons<ex_head, ex_tail>
>::type
> type;
};
template <
class head,
class ex_head, class ex_tail
>
struct split_< cons<head, null_type>, cons<ex_head, ex_tail> >
{
typedef boost::tuples::cons<
head,
cons<ex_head, ex_tail>
> type;
};
template <class T>
struct make_cons_ref_;
template <class head, class tail>
struct make_cons_ref_ < cons<head,tail> >
{
typedef cons<
typename add_reference<head>::type,
typename make_cons_ref_ <tail>::type
> type;
};
template <class head>
struct make_cons_ref_ < cons<head, null_type > >
{
typedef cons<
typename add_reference<head>::type,
null_type
> type;
};
template <
class lhead, class ltail,
class rhead, class rtail
>
typename make_cons_ref_<
typename split_<
cons<lhead, ltail>, cons<rhead, rtail>
>::type
>::type initialize_tie_ ( cons<lhead, ltail> &tuple_1st, cons<rhead, rtail> &tuple_2nd )
{
typedef cons<lhead, ltail> _1st;
typedef cons<rhead, rtail> _2nd;
typedef typename make_cons_ref_<
typename split_<_1st, _2nd>::type
>::type _3rd;
typedef typename make_cons_ref_<
typename split_<ltail, _2nd>::type
>::type next_t;
next_t next = initialize_tie_ (tuple_1st.tail, tuple_2nd);
_3rd result(tuple_1st.head, next );
return result;
}
template <
class lhead,
class rhead, class rtail
>
typename make_cons_ref_ <
cons<lhead, cons<rhead, rtail> >
>::type initialize_tie_ ( cons<lhead, null_type> &tuple_1st, cons<rhead, rtail> &tuple_2nd )
{
typedef cons<lhead, null_type>
_1st;
typedef cons<rhead, rtail> _2nd;
typedef typename make_cons_ref_ <
cons<lhead,
cons<rhead, rtail>
>
>::type _3rd;
typedef typename make_cons_ref_<
cons<rhead, rtail>
>::type next_t;
next_t next = initialize_tie_ (tuple_2nd);
_3rd result(tuple_1st.head, next);
return result;
}
template <
class head, class tail
>
typename make_cons_ref_ <
cons<head, tail>
>::type initialize_tie_ ( cons<head, tail> &tuple_2nd)
{
typedef cons<head, tail> _2nd;
typedef typename make_cons_ref_<_2nd>::type _3rd;
typedef typename make_cons_ref_<tail>::type next_t;
next_t next = initialize_tie_ ( tuple_2nd.tail );
_3rd result( tuple_2nd.head, next );
return result;
}
template <
class head
>
typename make_cons_ref_ <
cons<head, null_type>
>::type
initialize_tie_ (
cons<head, null_type> &tuple_2nd)
{
typedef cons<head, null_type> _2nd;
typedef typename make_cons_ref_<_2nd>::type _3rd;
return _3rd( tuple_2nd.head, null_type() );
}
//template arg
template <
class lhead, class ltail,
class rhead, class rtail
>
//return value
typename make_cons_ref_ <
typename split_<
cons<lhead, ltail>, cons<rhead, rtail>
>::type
>::type
operator , ( cons<lhead, ltail> &tuple_1st, cons<rhead, rtail> &tuple_2nd )
{
return initialize_tie_(tuple_1st, tuple_2nd);
}
}; };
#include <iostream>
#include <conio.h>
#include <boost/tuple/tuple_io.hpp>
using namespace boost::tuples;
int main()
{
tuple<int, std::string> tuple1(44, "aaaaa");
tuple<double, int> tuple2(55.33f, 111);
tuple<int> tuple3(88);
tuple<int, std::string, double, int, int> tuple4(1, "'2'", 3.14159f, 4, 5);
std::cout << "tuple4 = " << tuple4 << std::endl ;
std::cout << "tuple1 = " << tuple1 << std::endl;
std::cout << "tuple2 = " << tuple2 << std::endl;
std::cout << "tuple3 = " << tuple3 << std::endl;
std::cout << "(tuple1, tuple2, tuple3) = tuple4" << std::endl;
(tuple1, tuple2, tuple3)= tuple4;
std::cout << "tuple4 = " << tuple4 << std::endl ;
std::cout << "tuple1 = " << tuple1 << std::endl;
std::cout << "tuple2 = " << tuple2 << std::endl;
std::cout << "tuple3 = " << tuple3 << std::endl;
std::cout << "(tuple1, tuple2, tie(local)) = tuple4" << std::endl;
int local = 0;
(tuple1, tuple2, tie(local)) = tuple4;
std::cout << "local = " << local << std::endl;
_getch();
return 0;
}
этому коду явно не хватает комментариев))
в двух словах принцип здесь такой
кортеж представляет из себя по сути экземпляр списка типов, вот такого вот
template<class H, class T>
struct cons
{
H head;
T tail;
};
(реально конечно не так, пример сильно утрирован)
head - хранимое значение, tail - может быть либо null_type, либо другой cons. В общем так можно получить последовательность любой длины, вот такая вот последовательность:
cons<int, cons<long, cons<std::string, null_type> > >
соответствует кортежу tuple: int, long, string.
boost::tuple так и реализован, с той лишь разницей, что tuple наследует от списка типов, а линеаризация достигается с помощью параметров шаблона, если заглянуть в исходники, то можно увидеть там:template <class T0, class T1, class T2, class T3, class T4,
class T5, class T6, class T7, class T8, class T9>
class tuple :
public detail::map_tuple_to_cons<T0, T1, T2, T3, T4, T5, T6, T7, T8, T9>::type
................
typedef cons<T0,
typename map_tuple_to_cons<T1, T2, T3, T4, T5,
T6, T7, T8, T9, null_type>::type
> type;
в общем моя идея состоит в том, что-бы перегрузить оператор ,(comma) для кортежей так, что-бы он возвращал кортеж ссылок на элементы своих аргуметов
Для этого сначала нужно вычислить тип результата. Для этого сначала кортежи объединяются метафункцией split_
typedef typename split_<
cons<int, null_type>,
cons<long, cons<double, null_type> >
>::type result;
где result будет равно cons<int, cons<long, cons<double, null_type> > >
Метафункция make_cons_ref_ - делает параметры ссылками, то-есть после ее применения у нас будет последовательность вида:
cons<int&, cons<long&, cons<double&, null_type> > >
Теперь остается ее проинициализировать. Здесь самая большая сложность со ссылками, так как они не имеют конструктора по умолчанию и должны инициализироваться во время создания.
Для этого предназначена функция initialize_tie_. Структура cons имеет конструктор cons(H head, T tail), соответственно функция initialize_tie_ вызывается рекурсивно и всегда возвращает tail.
С этим кодом есть одна проблема. Он соответствует стандарту только для случая tuple3 = tuple2, tuple1; то есть для 2х кортежей, так как временный объект не может использоваться как rvalue при инициализации не константной ссылки. Но у компилятора Visual Studio на этот счет свое мнение =), там есть расширение, которое позволяет это делать, в общем в студии это отлично работает.
Производительность - такая-же как и при использовании get функций, компилятор отлично справляется с оптимизацией, я проверял =)
Может быть мне удастся сделать это более переносимым, посмотрим...
Комментариев нет:
Отправить комментарий