C ++可变参数模板中的责任链

这将是一个简单但经常使用的责任链模式。 模式的本质是我们使用多个处理程序来处理一个事件,每个处理程序都决定将什么以及何时传递给下一个事件。 网上有很多C ++实现的示例,但我只想展示lambda表达式上的实现。 在此实现中,您可以看到一些街道模板魔术。

假设我们有一个对象:

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

我们将有3个处理程序,每个处理程序将进一步传递对象:

 //        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); 

在此示例中,有3个处理程序,每个处理程序都是一个lambda表达式,这些处理程序使用ChainOfRepsonsibility类组合为责任链。

这是类实现本身:

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

它是这样的:

  • 首先,我们使用start_new函数创建责任链。 此阶段的主要问题是从传递的lambda中获取参数列表,并基于它们创建处理程序的“原型”。
  • 为了获取lambda参数,使用了StartChain类和带有模板专门化的棘手主题。 首先,我们声明该类的泛型版本,然后声明专用结构struct StartChain <void(C :: *)(Args ...)const> 。 这种构造使我们可以通过传递的类成员访问参数列表。
  • 有了参数列表,我们已经可以创建ChainTail和Chain类,它们将作为处理程序的包装。 他们的任务是将lambda保存在std ::函数中,并在正确的时间调用它,并实现attach方法(从上面设置处理程序)。

该代码已在Visual Studio 2015上进行了测试,对于gcc,可能有必要通过从其中删除const来修复StartChain专业化功能。如果除了lambd之外,您还想传递函数,则可以添加另一个StartChain专业化功能。 您仍然可以尝试摆脱附加中的复制,移动,但是为了不使示例复杂化,我只留下了最简单的情况。

Source: https://habr.com/ru/post/zh-CN413765/


All Articles