Es geht um ein so einfaches, aber häufig verwendetes Muster als Verantwortungskette. Das Wesentliche des Musters ist, dass wir mehrere Handler verwenden, um ein Ereignis zu verarbeiten, von denen jeder entscheidet, was und wann zum nächsten übergegangen werden soll. Es gibt viele Beispiele für C ++ - Implementierungen im Internet, aber ich möchte die Implementierung nur für Lambda-Ausdrücke zeigen. In dieser Implementierung wird es möglich sein, einen kleinen
Street Template-Zauber zu betrachten.
Nehmen wir also an, wir haben ein Objekt:
class Elephant { public: std::string touch_leg() { return "it's like a pillar"; } std::string touch_trunk() { return "it's like a snake"; } std::string touch_tail() { return "it's like a rope"; } void run_away() { m_is_gone = true; std::cout << "*** Sound of running out elephant ***\n"; } bool is_elephant_here() { return !m_is_gone; } private: bool m_is_gone = false; };
Und wir werden 3 Handler haben, von denen jeder das Objekt weitergibt:
In diesem Beispiel gibt es 3 Handler, von denen jeder ein Lambda-Ausdruck ist, die mithilfe der ChainOfRepsonsibility-Klasse zu einer Verantwortungskette zusammengefasst werden.
Hier ist die Klassenimplementierung selbst:
#include <functional> struct ChainOfRepsonsibility { template<typename... Args> struct Chain { template<typename Callee, typename Next> Chain(const Callee c, const Next& n) { m_impl = c; m_next = n; } template<typename Callee> decltype(auto) attach(Callee c) { return Chain(c, *this); } void operator()(Args... e) { m_impl(e..., m_next); } std::function<void(Args..., std::function<void(Args...)>)> m_impl; std::function<void(Args...)> m_next; }; template<typename... Args> struct ChainTail { template<typename Callee> ChainTail(Callee c) { m_impl = c; } template<typename Callee> decltype(auto) attach(Callee c) { return Chain<Args...>(c, m_impl); } void operator()(Args... e) { m_impl(e...); } std::function<void(Args... e)> m_impl; }; template<typename> struct StartChain; template<typename C, typename... Args> struct StartChain<void (C::*)(Args...) const> { using Type = ChainTail<Args...>; }; template<typename Callee> static decltype(auto) start_new(Callee c) { return StartChain<decltype(&Callee::operator())>::Type(c); } };
Es funktioniert so:
- Zunächst erstellen wir mit der Funktion start_new eine Verantwortungskette. Das Hauptproblem in dieser Phase besteht darin, eine Liste der Argumente aus dem übergebenen Lambda abzurufen und darauf basierend einen „Prototyp“ des Handlers zu erstellen.
- Um die Lambda-Argumente zu erhalten, werden die StartChain-Klasse und ein kniffliges Thema mit Spezialisierung der Vorlagen verwendet. Zuerst deklarieren wir eine generische Version der Klasse und dann die Spezialisierungsstruktur StartChain <void (C :: *) (Args ...) const> . Diese Konstruktion ermöglicht es uns, auf die Argumentliste des übergebenen Klassenmitglieds zuzugreifen.
- Mit einer Liste von Argumenten können wir bereits die Klassen ChainTail und Chain erstellen, die ein Wrapper für die Handler sein werden. Ihre Aufgabe ist es, das Lambda in der Funktion std :: zu speichern und zum richtigen Zeitpunkt aufzurufen sowie die Methode attach zu implementieren (den Handler von oben einstellen).
Der Code wurde für Visual Studio 2015 getestet. Für gcc kann es erforderlich sein, die StartChain-Spezialisierung durch Entfernen der Konstante zu korrigieren. Wenn Sie zusätzlich zu Lambdas nur Funktionen übergeben möchten, können Sie eine weitere StartChain-Spezialisierung hinzufügen. Sie können immer noch versuchen, das Kopieren in Anhängen loszuwerden, verschieben, aber um das Beispiel nicht zu komplizieren, habe ich nur den einfachsten Fall belassen.