Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Possibility to mix composite pattern and curiously recurring template pattern

I have a composite pattern implementation, used for GUI components:

class CObject {
private:

  CObject * m_pParent;  
  CObjectContainer * m_pChildren;

  void private_foo() {
    this->foo();
    //Calls private_foo for each child in container.
    m_pChildren->foo();
  }

public:
  virtual void foo() {
    //empty for base class
  }

  virtual CObject * duplicate() {
    //Do duplication code
    return new CObject(*this);
  }

  virtual CObject * detach() {
    //Remove this object (along with it's children)
    //from current tree.
    m_pParent->RemoveChild(this);
    m_pParent = nullptr;
    return this;
  }
}

class CSpecificObject : public CObject {
public:
  virtual void foo() {
    //Specific code for this class
  }

  virtual CSpecificObject * duplicate() {
    //Overload, but the code only calls diferent constructor
    return new CSpecificObject(*this);
  }

  virtual CSpecificObject * detach() {
    //Note the code is identical.
    m_pParent->RemoveChild(this);
    m_pParent = nullptr;
    return this;
  }
}

Unfortunately the number of inherited classes increases rapidly and the duplicate code (in given example only the detach() method) is giving me a headache.

Is there a way to cleanly implement detach() methods, keeping the return type the same as the object, on which it is called?

I was thinking about CRTP, but I can not think of a way to keep the dynamic polymorphism along with compile time polymorphism:

template <Child>
class CObject {
private:
  ...
  Child * detach() {
    m_pParent->RemoveChild(this);
    m_pParent = nullptr;
    return static_cast<Child*>(this);
  }
  ...
}

//Array of CObject* pointers is no longer possible.
like image 500
Vytis Valentinavičius Avatar asked Dec 13 '12 20:12

Vytis Valentinavičius


People also ask

When to use CRTP?

CRTP may be used to implement "compile-time polymorphism", when a base class exposes an interface, and derived classes implement such interface.

What is composite pattern in C++?

Composite pattern composes objects in term of a tree structure to represent part as well as whole hierarchy. This type of design pattern comes under structural pattern as this pattern creates a tree structure of group of objects. This pattern creates a class that contains group of its own objects.


1 Answers

You can add one level of abstraction:

class CObjectBase
{
    public:
        // Other methods...
        virtual CObjectBase* detach() = 0;
        virtual CObjectBase* duplicate() const = 0;
};

template <typename Child>
class CObject : public CObjectBase
{
    public:
        // ...
        Child* duplicate() const
        {
            return new Child(*static_cast<Child*>(this));
        }

        Child* detach()
        {
            m_pParent->RemoveChild(this);
            m_pParent = nullptr;
            return static_cast<Child*>(this); // Cast needed here (inherent to CRTP)
        }
        std::vector<CObjectBase*> children; // Array possible now
        // ...
};

class MyObject : public CObject<MyObject>
{
    // ...
};

In natural language: an interface for all objects (CObjectBase) have a partial implementation for its descendants (CObject<Child>), which just have to inherit this partial implementation, decreasing the amount of replicated code.

like image 72
Synxis Avatar answered Sep 20 '22 11:09

Synxis