在c++中模拟CLOS:before、:after和:around

Emulating CLOS :before, :after, and :around in C++

本文关键字:after around before 模拟 CLOS c++      更新时间:2023-10-16

CLOS有一个简洁的概念:before,:after和:around方法。

  • :before方法在主方法之前调用。
  • :after方法在主方法之后调用。
  • :around方法围绕:before+primary+:after序列调用。

:before、:after和:around方法被链接而不是覆盖。假设父类和子类都定义了foo方法和:before foo方法。子节点的foo方法会覆盖父节点的foo方法,但是在父节点和子节点的:before foo方法被调用之前,这个被覆盖的方法才会被调用。

Python装饰器提供了类似于CLOS:around方法的东西。c++中没有这样的东西。必须手卷:

class Child : public Parent {
    virtual void do_something (Elided arguments) {
        do_some_preliminary_stuff();
        Parent::do_something (arguments);
        do_some_followup_stuff();
    }
};

缺点:

    这是一个反模式。
  • 它要求我明确指定父类。
  • 它要求我的类的扩展器遵循相同的范式。
  • 如果我需要调用祖父母,因为父没有覆盖do_something,多重继承怎么办?
  • 它没有完全捕捉CLOS的概念。

当我使用Flavors (CLOS的前身)时,我发现这些概念非常方便。我已经在一些地方使用了上面的解决方法,并且有一些地方将其作为反模式进行了挑战。(其他人已经在其他地方模仿它,所以这种嘲笑并不普遍。)

问题是:这在c++中被认为是一种反模式吗?有没有更好的解决方法?

您可以使用(std/boost)::shared_ptr很好地获得这些基础知识。详见:http://www.boost.org/doc/libs/1_46_1/libs/smart_ptr/sp_techniques.html#wrapper

获得你提到的继承行为只需要前缀/后缀函数调用父类中的前缀/后缀函数。

我可以这么做,但是还是有点难看。

基本上我把实际的工作放在一个单独的钩子中,所以你不需要在处理方法中调用pre/post钩子。在继承链中,你可以完全控制是否要添加pre/post钩子,以及钩子调用的顺序(在子钩子之前或之后调用父钩子)。

#include <iostream>
#define C(s) std::cout << s << std::endl;
class Parent {
    public:
    virtual void do_something(int arg) {
        do_some_pre_hook();
        do_some_hook(arg);
        do_some_post_hook();
    }
    virtual void do_some_pre_hook() {
        C("parent pre_hook");
    }
    virtual void do_some_post_hook() {
        C("parent post_hook");
    }
    virtual void do_some_hook(int arg) {
        //this is where you actually do the work
    }
};
class Child : public Parent {
    public:
    typedef Parent super;
    virtual void do_some_pre_hook() {
        super::do_some_pre_hook();
        C("Child pre_hook");
    }
    virtual void do_some_post_hook() {
        super::do_some_post_hook();
        C("Child post_hook");
    }
};
class GrandChild : public Child {
    public:
    typedef Child super;
    virtual void do_some_pre_hook() {
        super::do_some_pre_hook();
        C("GrandChild pre_hook");
    }
    virtual void do_some_post_hook() {
        super::do_some_post_hook();
        C("GrandChild post_hook");
    }
    virtual void do_some_hook(int arg) {
        //this is where you actually do the work
        C("GrandChild hook");
    }
};
int main() {
    GrandChild gc;
    gc.do_something(12);
}

注意:理想情况下,你会使用AOP c++编译器或编译器扩展来完成这样的任务,但上次我尝试它时,它不是很稳定…

我并不是说这与其他语言所做的相同或可比较,但我认为非虚拟接口习语适用于您的问题:

class parent {
public:
    void foo()
    {
        before_foo();
        do_foo();
        after_foo();
    }
protected:
    // you can make those pure virtual with an implentation, too
    virtual void before_foo() { ... }
    virtual void do_foo() { ... }
    virtual void after_foo() { ... }
};
class child: public parent {
protected: // or private
    void before_foo() { ... }
    void do_foo() { ... }
    // must provide a dummy after_foo that delegates to parent::after_foo
    // if it is pure virtual in the parent class
};

调用p.foo()时,总是调用衍生最多的before_fooafter_foodo_foo