Итератор по кортежу И так, нам нам нужно огранизованно пройтись по всем членам кортежа. Мне это понадобилось для сохранения в xml — файл конструкций типа std::vector<boost::tuple<...>>. Кстати, пишу я это в Qt.
Ну да ладно, к делу.
Берём на вооружение библиотеку boost::fusion. В принципе итератор — указатель на первый элемент кортежа получить проще простого.
Вот наш кортеж к примеру.
Ну да ладно, к делу.
Берём на вооружение библиотеку boost::fusion. В принципе итератор — указатель на первый элемент кортежа получить проще простого.
Вот наш кортеж к примеру.
typedef boost::tuple<int , char* , float> tup; // псведоним
tup my_tup(12, "two" , 3.4); // объявление и инициализация
boost::fusion::result_of::begin::type it(my_tup); // начальный итератор
std::cout << *it << '\n'; // 12
Инкременировать итератор сложнее, но тоже решаемо.
std::cout << *boost::fusion::next( it ) << '\n'; // "two"
На помощь была призвана шаблонная рекурсия.
Хотелось бы, что то типа:
template void work(R& item)
{
// тут обрабатываем член кортежа
std::cout << *item << '\n';
//
work( boost::fusion::next(item));
}
Но проблема была в остановке, как понять что нужно остановиться? В принципе когда итератор станет равным следующей конструкции, то нужно остановить всё это дело.
boost::fusion::result_of::end::type end; // конечный итератор
На помощь зовём boost::type_traits и boost::mpl::if_.
Надо бы сдеалть так, чтобы когда итератор поймёт, что дальше уже некуда, рекурсия оборвалась и пошла по другому пути. В качестве другого пути подсунем ей метод — пустышку, то есть, когда итератор дошёл до конца, он должен вызвать этот метод, вместо рабочего. Для этого засунем оба метода в структуры и поместим их там, как статические. Тогда вызовы этих методов будут похожими и отличаться только типом структур. А тип структуры нам нужно в последний момент подменить, для остановки рекурсии. Что бы поменять тип структуры используем boost::mpl::if_, в качестве условия которого засунем конструкцию boost::is_same из boost::type_traits. Это условие будет сверять каждый раз текущий итератор с конечным. Поехали:
struct do_work
{
template<typename T,typename R >
static void work(R& item ) // рабочий метод
{
#pragma message("******** process tuple **************") // для наглядности печтаем compile-time сообщения в отладку
// procees item
std::cout << *item << '\n';
//
typedef typename boost::fusion::result_of::end::type end; // тип конечного итератора
typedef typename boost::fusion::result_of::next::type next; // тип следующего итератора
typename next nex = boost::fusion::next( item ) ;
boost::mpl::if_<
boost::is_same<next , end>::type , // условие - провекрка
end_work , // конец рекурсии
do_work // следующий элемент
>::type::work( nex );
}
};
struct end_work // метод пустышка
{
template <typename T,typename R>
static void work(R& /*tip*/) // метод пустышка
{
#pragma message("******** end of tuple *************")
}
};
Теперь можно юзать , что мы тут нагородили.
int main()
{
typedef boost::tuple<int , char* , float> tup; // псевдоним
tup my_tup(12, "two" , 3.4); // объявление и инициализация
do_work::work( boost::fusion::begin(my_tup) ); // запуск
std::cin.ignore();
return 0;
}
При компиляции в окне отладки мы должны увидеть ,как компилятор три раза сработал рекурсивно, и на третьем разе споткнулся и съел метод пустышку.
******** process tuple **************
******** process tuple **************
******** process tuple **************
******** end of tuple *************
Всё.
ЗЫ Автор не претендует на оригинальность и професинальность, и грамотность кода и вообще не на что, просто если бы я такую статью прочитал три дня назад, сэкономил бы эти дни для моей жизни.
haccel.pp.ru