0

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

Community
  • 1
  • 1
nass
  • 1,453
  • 1
  • 23
  • 38
  • _"Generally, is it possible from the Base class A to access same-named members in the derived classes?"_ Yes it is. What's your actual problem? – πάντα ῥεῖ Sep 04 '16 at 12:13
  • @πάνταῥεῖ as explained that I cannot access et() from all derived decorators – nass Sep 04 '16 at 12:21
  • 1
    The 200 lines of code above compile. Can you create a minimal, working example to reproduce the problem? – skypjack Sep 04 '16 at 12:26
  • 1
    Can you provide a simplified, but realistic example for what you want to use this? I fear we're facing an XY problem here. – Daniel Jour Sep 04 '16 at 12:27
  • @skypjack I have stripped down the example – nass Sep 04 '16 at 13:14
  • @DanielJour I have attempted to provide an explanation for the design (only the parts related to the example). I see this starts getting more complex than you guys think it should, so if you have time and are willing I would not mind discussing it further in a chatroom. I have attempted several patterns and read a lot and still failed to find exactly what I was looking for, so it is possible my design can be "split" and become simpler in some way that I fail to see. – nass Sep 04 '16 at 13:17

1 Answers1

2

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;
}
skypjack
  • 49,335
  • 19
  • 95
  • 187
  • could you please elaborate abit further on the `int arr[] = { (T::et(), 0)..., 0 };` I especially don't know where the 0 come from - but the notation in general I have never used. thank you – nass Sep 04 '16 at 14:33
  • @nass It's a trick to be used until fold expressions take part to the game. It creates a temporary array to be discarded immediately after. The aim is to invoke `et` for each type of the parameter pack from which `S` has been derived. If it was only one type, it would have been `T::et()`, that I guess you know, right? – skypjack Sep 04 '16 at 14:36
  • Hi there, after some thinking I have realised this cannot work for me. You see, in your case the decorators are base classes and they are "appended to "S". I realised now that this is how you get to populate `arr`. However, by doing that I get into all sorts of problems, more importantly how do I efficiently and quickly share with "A" & "B" the common member variables declared in S how will methods in "A" and "B" call on methods from the Base class S to do some common (but not chained) tasks for them. A more appropriate topology, follows in the next comment – nass Sep 06 '16 at 19:05
  • http://www.thinkbottomup.com.au/site/blog/C%20%20_Mixins_-_Reuse_through_inheritance_is_good . But in this case, I lose the functionality that you proposed in this answer which is also vital. So I'm puzzled if there is a way to "use the best of both worlds". Thank you – nass Sep 06 '16 at 19:05
  • @nass Tried to go deeper along your questions. Let me know. – skypjack Sep 06 '16 at 19:42
  • Hi there, thank you for the quick response. I am going through it. One important thing is that there exist not only common variables but also common methods. To pass these around through an argument list seems like an overkill. no? – nass Sep 06 '16 at 20:43
  • @nass You can put those methods in the template class `S` or in the `Data` class or whatever. It mostly depends on the real code and the actual design you chose and I don't know them, so it's difficult to give you a complete answer. – skypjack Sep 06 '16 at 20:46
  • if I put them in S how can I call them from A?! – nass Sep 06 '16 at 20:50
  • @nass Uhm, well, a subobject like `Data` is more suitable indeed. Touché. :-) – skypjack Sep 06 '16 at 20:51
  • ok 1 last question about naming. The solution you provided is sort of a "composition mixins" , where as the one at the link I provided is more of a "delegation mixins" ? – nass Sep 06 '16 at 21:00
  • @nass I'd rather spend my time coding and let names to the other. Those are mixins, the rest are implementation details. Who cares about _the best name_? – skypjack Sep 06 '16 at 21:05
  • well it's not about best but rather about clearing stuff in my head. Not a priority but certainly important when you discuss with other developers. – nass Sep 06 '16 at 21:08