c++ 装饰器模式,带模板的静态多态性和注册回调方法 [英] c++ decorator pattern, static polymorphism with templates and registering callback methods

查看:28
本文介绍了c++ 装饰器模式,带模板的静态多态性和注册回调方法的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

I am attempting to use static polymorphism to create a decorator pattern. As to why I do not use dynamic polymorphism, please see this QA. Basically, I could not dynamic_cast to each decorator so as to access some specific functionality present only in the decorators (and not in the base class A).

With static polymorphism this problem has been overcome, but now I cannot register all the et() methods from the decorators back to the base class A (as callbacks or otherwise), thus when A::et() gets called, only A::et() and Z::et() get executed. I want all of A,X,Y,Z ::et() to be executed (the order for X,Y,Z does not matter).

How can I do that using the following structure? I can see in wikipedia that CRTP should allow you to access member of a derived class using static_cast, but how do you approach the problem when there are multiple derived template classes?

If this is not possible with static polymorphism but it is possible with dynamic polymorphism could you reply to the other question?

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

This structure is to be used as a reference for data acquisition from a set of sensors. class A is the base class and contains common functionality of all the sensors. This includes:

- 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()`)

In fact the A::timer will call A::et() on completion in order to perform some statistical calculations on the sampled data.

Similarly, X,Y,Z are types of sensor objects each with responsibility to extract different type of information from the sampled data. and X,Y,Z::et() perform a different type of statistical calculation on the data. The aim is perform this calculation as soon as the A::Timer waiting time elapses. This is why I want to have access to all of X,Y,Z::et() from A::et(). Is it possible without affecting the static polymorphism shown in the example?

Thank you

解决方案

You started using mixins, so use them to the end.
It follows a minimal, working example:

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

As in the original code, there is an interface I that offers the virtual methods to be called.
S implements that interface and erases a bunch of types passed as a parameter pack.
Whenever you invoke et on a specialization of S, it invokes the same method on each type used to specialize it.

I guess the example is quite clear and can serve as a good base for the final code.
If I've understood correctly the real problem, this could be a suitable design for your classes.

EDIT

I'm trying to reply to some comments to this answer that ask for more details.

A specialization of S is all the (sub)objects with which it is built.
In the example above, S<A, B> is both an A and a B.
This means that S can extend one or more classes to provide common data and can be used as in the following example to push around those data and the other subobjects:

#include<iostream>

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

struct Data {
    int foo;
    double bar;
};

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

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

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

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

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

这篇关于c++ 装饰器模式,带模板的静态多态性和注册回调方法的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆