Image

Listens: Iron Maiden — [Virtual XI #06] The Educated Fool

Category:

Рекурсивный циклический вызов

Пусть есть несколько функторов:

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(); }; };

Каждый из них инстанцируется функтором, который надо вызвать следующим (counter просто ограничивает глубину рекурсии).

Задача: написать такой код, который бы строил цепочки вызовов A → B → C → A → B → C → A → ... с таким синтаксисом построения:

Caller< L<A, L<B, L<C, L<> > > > > caller; caller();

Каррент мьюзик какбе намекает: в боевом коде такого писать не стоит :)

Решено: Imageshuffle_c предложил отличный вариант:

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;
    };
};

Как это работает? Легко догадаться, что если функтор 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>, и цепочка замыкается.