Рекурсивный циклический вызов
Пусть есть несколько функторов:
Каждый из них инстанцируется функтором, который надо вызвать следующим (counter просто ограничивает глубину рекурсии).
Задача: написать такой код, который бы строил цепочки вызовов A → B → C → A → B → C → A → ... с таким синтаксисом построения:
Каррент мьюзик какбе намекает: в боевом коде такого писать не стоит :)
Решено:
shuffle_c предложил отличный вариант:
Как это работает? Легко догадаться, что если функтор A сразу вызывает B, B сразу вызывает C, а C сразу вызывает A, то попытка записать соответствующий шаблон для A никогда не завершится. Решение: вставить прокси-вызов где-то в этой цепочке. Можно вставить его между каждыми вызовами функторов, а можно только для передачи управления из последнего элемента списка в первый, как в приведенном решении. В нем Inside всегда принимает D = Caller<LA,B,C> и возвращает тип THead<TTail::Inside<D>>, выстраивая цепочку A<B<C<... Inside у пустого списка типов специализирован: он возвращает просто D, т.е. Caller<LA,B,C>. Т.о. последовательно вызываются следующие инстанцированные функторы: A<B<C<Caller<LA,B,C>>>> → B<C<Caller<LA,B,C>>> → C<Caller<LA,B,C>> → Caller<LA,B,C>, и цепочка замыкается.
int counter = 0;
template<typename D> struct A { void operator () () { ++counter; cout << "A" << endl; D func; if (counter < 20) func(); }; };
template<typename D> struct B { void operator () () { ++counter; cout << "B" << endl; D func; if (counter < 20) func(); }; };
template<typename D> struct C { void operator () () { ++counter; cout << "C" << endl; D func; if (counter < 20) func(); }; };
template<typename D> struct A { void operator () () { ++counter; cout << "A" << endl; D func; if (counter < 20) func(); }; };
template<typename D> struct B { void operator () () { ++counter; cout << "B" << endl; D func; if (counter < 20) func(); }; };
template<typename D> struct C { void operator () () { ++counter; cout << "C" << endl; D func; if (counter < 20) func(); }; };
Каждый из них инстанцируется функтором, который надо вызвать следующим (counter просто ограничивает глубину рекурсии).
Задача: написать такой код, который бы строил цепочки вызовов A → B → C → A → B → C → A → ... с таким синтаксисом построения:
Caller< L<A, L<B, L<C, L<> > > > > caller; caller();
Каррент мьюзик какбе намекает: в боевом коде такого писать не стоит :)
Решено:
template<class T>
struct Caller {
void operator() () {
typename T::Inside<Caller<T> >::Name func; func();
}
};
template<template<class> class T = Caller, class U = void>
struct L {
template<class D>
struct Inside {
typedef T<typename U::Inside<D>::Name > Name;
};
};
template<>
struct L<Caller, void> {
template<class D>
struct Inside {
typedef D Name;
};
};
struct Caller {
void operator() () {
typename T::Inside<Caller<T> >::Name func; func();
}
};
template<template<class> class T = Caller, class U = void>
struct L {
template<class D>
struct Inside {
typedef T<typename U::Inside<D>::Name > Name;
};
};
template<>
struct L<Caller, void> {
template<class D>
struct Inside {
typedef D Name;
};
};
Как это работает? Легко догадаться, что если функтор A сразу вызывает B, B сразу вызывает C, а C сразу вызывает A, то попытка записать соответствующий шаблон для A никогда не завершится. Решение: вставить прокси-вызов где-то в этой цепочке. Можно вставить его между каждыми вызовами функторов, а можно только для передачи управления из последнего элемента списка в первый, как в приведенном решении. В нем Inside всегда принимает D = Caller<LA,B,C> и возвращает тип THead<TTail::Inside<D>>, выстраивая цепочку A<B<C<... Inside у пустого списка типов специализирован: он возвращает просто D, т.е. Caller<LA,B,C>. Т.о. последовательно вызываются следующие инстанцированные функторы: A<B<C<Caller<LA,B,C>>>> → B<C<Caller<LA,B,C>>> → C<Caller<LA,B,C>> → Caller<LA,B,C>, и цепочка замыкается.