Chaîne de responsabilité dans les modèles variadiques C ++

Il s'agira d'un modèle aussi simple mais souvent utilisé que la chaîne de responsabilité. L'essence du modèle est que nous utilisons plusieurs gestionnaires pour traiter un événement, dont chacun décide quoi et quand passer au suivant. Il y a des tonnes d'exemples d'implémentation C ++ sur le net, mais je veux montrer l'implémentation uniquement sur les expressions lambda. Dans cette implémentation, il sera possible de regarder un peu de magie de modèle de rue .

Disons que nous avons un objet:

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

Et nous aurons 3 gestionnaires, dont chacun passera l'objet plus loin:

 //        auto blind_sage3 = ChainOfRepsonsibility::start_new([](Elephant& e) { std::cout << "Third blind sage: " << e.touch_tail() << "\n"; }); // ""    ,     auto blind_sage2 = blind_sage3.attach([](Elephant& e, auto& next) { std::cout << "Second blind sage: " << e.touch_trunk() << "\n"; next(e); }); //    ,     ,    auto blind_sage1 = blind_sage2.attach([](Elephant& e, auto& next) { if (!e.is_elephant_here()) { std::cout << "First blind sage: So empty... so true\n"; } else { std::cout << "First blind sage: " << e.touch_leg() << "\n"; next(e); } }); //      Elephant e; blind_sage1(e); 

Dans cet exemple, il existe 3 gestionnaires, dont chacun est une expression lambda, qui sont combinés en une chaîne de responsabilité à l'aide de la classe ChainOfRepsonsibility.

Voici l'implémentation de classe elle-même:

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

Cela fonctionne comme ceci:

  • Tout d'abord, nous créons une chaîne de responsabilité en utilisant la fonction start_new. Le problème principal à ce stade est d'obtenir une liste d'arguments du lambda passé et de créer un «prototype» du gestionnaire basé sur eux.
  • Pour obtenir les arguments lambda, la classe StartChain et un thème délicat avec spécialisation des modèles sont utilisés. Tout d'abord, nous déclarons une version générique de la classe, puis la structure de spécialisation StartChain <void (C :: *) (Args ...) const> . Cette construction nous permet d'accéder à la liste d'arguments par le membre de classe passé.
  • Ayant une liste d'arguments, nous pouvons déjà créer les classes ChainTail et Chain, qui seront un wrapper pour les gestionnaires. Leur tâche consiste à enregistrer le lambda dans la fonction std :: et à l'appeler au bon moment, ainsi qu'à implémenter la méthode attach (en définissant le gestionnaire par le haut).

Le code a été testé pour Visual Studio 2015, pour gcc, il peut être nécessaire de corriger la spécialisation StartChain en supprimant la const à partir de là, si en plus des lambdas vous voulez simplement passer des fonctions, vous pouvez ajouter une autre spécialisation StartChain. Vous pouvez toujours essayer de vous débarrasser de la copie en pièce jointe, ne bougez pas, mais afin de ne pas compliquer l'exemple, je n'ai laissé que le cas le plus simple.

Source: https://habr.com/ru/post/fr413765/


All Articles