Search code examples
c++oopdesign-patternsarchitecture

C++ Problem with handling a state between two classes in a hierarchy


Problem Description

Consider the following class hierarchy. Concrete templated class A, templated abstract class B and an interface C, with A <-- B <-- C. Consider that B has a function declaration

virtual std::optional<T> foo() const  = 0;

and c has a function declaration

virtual bar() = 0;

both of which are defined in A appropriately.

There exists somewhere in the code a list of objects B, over which we call foo(), and there exists a list of C* pointers over which we call bar().

For a minimal example:

Example and My Issue

class A <typename T> : public B <T>
{
public: 
    std::optional<T> foo() const override { /*...*/ };
    bar() override { /*...*/ };

    // ...
};

class B <typename T> : public C
{
public: 
    virtual std::optional<T> foo() const = 0;

    // ...
}

class C
{
public: 
    virtual bar() = 0;
}

The issue I am having is that I want to add a boolean state over this small hierarchy. Particularly, I have the following requirements

  1. I need a boolean state m_isInteresting over the object represented by this hierarchy.
  2. I don't want m_isInteresting state it to be in A (for decoupling reasons).
  3. I want foo() to set m_isInteresting = true;.
  4. I want bar() to set m_isInteresting = false;.

How can I achieve this?

Things I have tried

  • I tried to create a decorator wrapper around C with the m_isInteresting member and the appropriate accessors, with a method forward of bar(), in which I call C's bar() function and set the class member m_isInteresting to false, but then I can't set the member to true because foo() is down the class hierarchy.
  • I tried to move foo()'s declaration to C (while keeping it's definition in A), but this is very difficult because the function returns a std::optional<T> and well, C is not templated; a derived class's function signature must match that of the base class.
  • I have thought of making decorator classes for both B and C with m_isInteresting shared between them, but held externally, but this is getting ugly and complicated.

There must be something that I am missing here.


Solution

  • The below code fulfils your requirements. You can put m_isInteresting inside the C class and mark it protected for the subclasses to be able to access it. You can then refer to it as this->m_isInteresting in the subclasses.

    #include "optional"
    
    class C {
    protected:
        bool m_isInteresting = false;
    public:
        virtual void bar() = 0;
    };
    
    template <typename T>
    class B: public C {
    public:
        virtual std::optional<T> foo() = 0;
    };
    
    template <typename T>
    class A: public B<T> {
    public:
        std::optional<T> foo() override {
            this->m_isInteresting = true;
            return {};
        }
        void bar() override {
            this->m_isInteresting = false;
        }
    };