这将是一个简单但经常使用的责任链模式。 模式的本质是我们使用多个处理程序来处理一个事件,每个处理程序都决定将什么以及何时传递给下一个事件。 网上有很多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个处理程序,每个处理程序将进一步传递对象:
在此示例中,有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专业化功能。 您仍然可以尝试摆脱附加中的复制,移动,但是为了不使示例复杂化,我只留下了最简单的情况。