Scorb
Scorb

Reputation: 1850

Creating subclass using templates

I have a design problem I am trying to overcome. Lets assume that the class structure and hierarchy is correct, and I can not change that.

I have a class called BaseWidget. It has internal nested classes Grid, and Square. Grid maintains a vector of squares. They are there to better organise drawing behaviour. BaseWidget constructs the vector of Grid and Squares in its Layout() method.

Now I have Sub-classed BaseWidget with class DerivedWidget. Derived widget has additional drawing requirements for the class BaseWidget::Square. So it would be nice and easy to sub-class BaseWidget::Square with Derived::WidgetSquare.

The problem comes from the fact that the Grids and Squares are constructed in BaseWidget::OnLayout. So when the Grid vector of squares is created I would like to say "use DerivedSquare instead of Square in the vector you are populating".

I believe my solution is to use templates but I am not sure in what capacity.

class BaseWidget : public Widget
{
    protected:
        void Layout(void)
        void Draw(canvas c);

        class Square {
            Square(Rect r) : region(r) {}
            Rect region;
            virtual void Draw(canvas c);
        };

        class Grid {
            std::std::vector<shared_ptr<<Square>> squares;
            Rect region;
            void Draw(canvas c);
        };

        std::vector<Grid> m_grids;
};

void Parent::Layout {
    m_grids.resize(num_grids);

    for (int i=0; i<num_grids; ++i) {

        m_grids[i].region = some_rect;

        for (int j=0; j<num_squares; ++j) {
            m_grids[i].m_squares.push_back(std::make_shared<Square>(some_other_rect));
        }
    }
}

void BaseWidget::Draw(Canvas c) 
{
    for (int i = 0; i < m_grids.size(); i++) {
        m_grids.Draw(c);
    }
}

void Grid::Draw(Canvas c)
{
    // draw some stuff here
    for (int i = 0; i < m_squares.size(); i++) {
        m_squares[i].Draw(c);
    }
}

void Square::Draw(Canvas c)
{
    // draw some stuff here
}



// new class that extends parent
class DerivedWidget : public BaseWidget 
{
    protected:

        /*
         * This class wants to do some special drawing in addition to its parents drawing
         */
        class DerivedSquare : public Square {
            virtual void Draw(canvas c);
        };
}

Upvotes: 0

Views: 84

Answers (3)

Jarod42
Jarod42

Reputation: 217135

It seems you just need a factory, you can add a virtual method for that:

class BaseWidget : public Widget
{
protected:
    void Layout()
    void Draw(canvas c);

    class Square {
    public:
        Square(Rect r) : region(r) {}
        Rect region;
        virtual void Draw(canvas c);
    };

    class Grid {
    public:
        std::std::vector<shared_ptr<<Square>> squares;
        Rect region;
        void Draw(canvas c);
    };

    virtual std::unique_ptr<Square> MakeSquare() const { return std::make_unique<Square>(/*...*/); }


    std::vector<Grid> m_grids;
};

// new class that extends parent
class DerivedWidget : public BaseWidget 
{
protected:

    /*
     * This class wants to do some special drawing in addition to its parents drawing
     */
    class DerivedSquare : public Square {
        virtual void Draw(canvas c);
    };


    std::unique_ptr<Square> MakeSquare() const override { return std::make_unique<DerivedSquare>(/*...*/); }
}

And use MakeSquare() in your code instead of hard coding Square.

Upvotes: 1

gmoshkin
gmoshkin

Reputation: 1135

Try something like this:

template <class Derived>
class BaseWidget : public Widget
{
   // ...
   class Grid {
        std::std::vector<shared_ptr<<Derived::SquareType>> squares;
        // ...
   }
}

class DerivedWidget : public BaseWidget 
{
    using SquareType = DerivedSquare;
    // ...
}

Upvotes: 0

Maarten Bamelis
Maarten Bamelis

Reputation: 2423

If I understood your question correctly, you are looking for something like the following. Note that I simplified the classes and methods, it should serve as just a proof of concept:

struct BaseWidget
{
    struct BaseSquare
    {
        virtual void draw()
        {
            // ... Drawing Logic for Base Square
        }
    };

    virtual void draw()
    {
        drawInternal<BaseSquare>();
    }

protected:

    template <typename SquareType>
    void drawInternal()
    {
        auto squareToDraw = std::make_shared<SquareType>();
        squareToDraw->draw();
    }
};

struct DerivedWidget : BaseWidget
{
    struct DerivedSquare : BaseSquare
    {
        virtual void draw() override
        {
            // Drawing Logic for Dervied Square
        }
    };

    virtual void draw() override
    {
        drawInternal<DerivedSquare>();
    }
};

Of course this has some important limitations. For one, the BaseWidget class must know how to construct the squares, so the DerivedSquare cannot have a fancy constructor. You could potentially overcome this by also passing constructor arguments to the drawInternal function but I am unsure if that fits in your design.

Upvotes: 1

Related Questions