c ++装饰模式,带模板的静态多态和注册回调方法

时间:2016-09-04 12:07:12

标签: c++ callback polymorphism decorator

我试图使用静态多态来创建装饰器模式。 至于为什么我不使用动态多态,请参阅此QA。基本上,我不能dynamic_cast每个装饰器,以便访问仅在装饰器中存在的某些特定功能(而不是在基类A中)。

使用静态多态性已经克服了这个问题,但是现在我无法将装饰器中的所有et()方法注册回基类A(作为回调或其他方式),因此调用A::et()时,只有A::et()Z::et()被执行。我希望所有A,X,Y,Z ::et()都被执行(X,Y,Z的顺序无关紧要)。

如何使用以下结构执行此操作? 我可以在维基百科中看到CRTP应该允许您使用static_cast访问派生类的成员,但是如果有多个派生模板类,您如何处理该问题?

如果静态多态无法实现这一点,但是动态多态可能会回复另一个问题吗?

struct I {
    virtual void et() = 0;
};

class A : public I {
  public:
    A() {
        cout << "A::ctor " ;
        decList.clear(); 
    }
    void regDecorator(I * decorator) 
    {
        if (decorator) {
            cout << "reg= " << decorator << " ";
            decList.push_back(decorator);
        }
        else
            cout << "dec is null!" <<endl;
    }  
    virtual void et()
    {
        cout << "A::et ";
        cout << "declist size= " << decList.size() << endl;

        list<I*>::iterator it;
        for( it=decList.begin(); it != decList.end(); it++ )
            static_cast<I *>(*it)->et();
    }

    std::list<I*> decList; //FIXME
};

template<typename Base>
class X: public Base {
  public:
    X(){
        cout << "X::ctor ";
        Base::regDecorator(this);
    }

    virtual void et(){
        cout << "X::et" <<endl;
    }
};

template<typename Base>
class Y: public Base {//public D {
  public:
    Y(){
        cout << "Y::ctor ";
        Base::regDecorator(this);
        }

    void et(){
        cout << "Y::et" <<endl;
    }
};

template<typename Base>
class Z: public Base {//public D {
  public:
    Z() {
        cout << "Z::ctor ";
        Base::regDecorator(this);
        }    
    void et(){
        cout << "Z::et" <<endl;
    }
};

int main(void) {
    Z<Y<X<A> > > mlka;
    cout << endl;
    mlka.et();
    return 0;
}

该结构将用作从一组传感器获取数据的参考。 A类是基类,包含所有传感器的通用功能。这包括:

- data container (f.e. `boost::circular_buffer`) to hold an amount of timestamped sample data acquired from the sensor.
- a Timer used to measure some timed quantities related to the sensors. 
- other common data and calculation methods (fe. `calculateMean()`, `calculateStdDeviation()`)

事实上,A::timer将在完成时调用A::et(),以便对采样数据执行某些统计计算。

类似地,X,Y,Z是传感器对象的类型,每个传感器对象负责从采样数据中提取不同类型的信息。并且X,Y,Z::et()对数据执行不同类型的统计计算。目标是在A::Timer等待时间过后立即执行此计算。这就是为什么我想从A :: et()访问所有X,Y,Z :: et()的原因。是否可以在不影响示例中显示的静态多态的情况下使用?

谢谢

1 个答案:

答案 0 :(得分:2)

你开始使用mixins,所以最后使用它们 它遵循一个最小的工作示例:

#include<iostream>

struct I {
    virtual void et() = 0;
};

template<typename... T>
struct S: I, private T... {
    S(): T{}... {}

    void et() override {
        int arr[] = { (T::et(), 0)..., 0 };
        (void)arr;
        std::cout << "S" << std::endl;
    }
};

struct A {
    void et() {
        std::cout << "A" << std::endl;
    }
};

struct B {
    void et() {
        std::cout << "B" << std::endl;
    }
};

int main() {
    I *ptr = new S<A,B>{};
    ptr->et();
    delete ptr;
}

与原始代码一样,有一个接口I,它提供了要调用的虚拟方法 S实现了该接口并删除了作为参数包传递的一堆类型 无论何时在et的特化上调用S,它都会对用于特化它的每种类型调用相同的方法。

我想这个例子很清楚,可以作为最终代码的良好基础 如果我已经正确理解了真正的问题,这可能是适合您班级的设计。

修改

我正在尝试回复此答案的一些评论,要求提供更多详细信息。

S 的特化是构建它的所有(子)对象。
在上面的示例中,S<A, B> <{1}}和A。 这意味着B可以扩展一个或多个类以提供公共数据,并且可以在以下示例中用于推送这些数据和其他子对象:

S