Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning a reference to the derived class from a base class method

I have a task to implement a simple SVG generator. I need to support Circle, Polyline and Text. All three have at least 4 common methods: - SetStrokeColor - SetFillColor - SetStrokeWidth - ToString One of the main requirements is to support chaining, e.g.: Polyline{}.SetStrokeColor("white").SetFillColor("black")...

I decided to implement a base class Element, which all the other classes inherit from. The idea is to have a class Document that holds a vector of all the elements added to the document. A sample signature for a base method:

// source of trouble
Element &SetStrokeColor(const Color &color) {
    ...
    return *this;
}

My derived classes do call these methods, but the trouble is that the methods return a reference to the base class Element, not the derived class.

My question is whether it is all together possible to implement in c++???

Further discussion here

like image 552
magom001 Avatar asked Jun 03 '19 08:06

magom001


2 Answers

If you want to share implementations and preserve type information, the CRTP is what you need:

struct ElementBase { };

template <class Concrete>
struct Element : ElementBase {

    Concrete &setStrokeWidth(int width) {
        // Actual implementation...
        (void) width;

        return cthis();
    }

private:
    friend Concrete;
    Element() = default;

    Concrete &cthis() { return static_cast<Concrete &>(*this); }
    Concrete &cthis() const { return static_cast<Concrete const &>(*this); }
};

struct Circle : Element<Circle> {
    Circle &SetCircleCenter(int x, int y) {
        // Actual implementation...
        (void) x;
        (void) y;

        return *this;
    }
};

int main() {
    Circle c;
    c.setStrokeWidth(4).SetCircleCenter(0, 0);
}

See it live on Wandbox

like image 184
Quentin Avatar answered Oct 19 '22 22:10

Quentin


With covariant return types, you can

class Element {
  public:
    // ...

    virtual Element& refToThis() { return *this; };
};

and in derived classes

class Derived : public Element {
  public:
    // ...

    Derived& refToThis() override { return *this; };
};

which lets you handle Derived instances as Derived instances when the static type is Derived (e.g. inside Derived itself). When tht static type is Element, the return type of refToThis() is, too.

like image 43
lubgr Avatar answered Oct 19 '22 23:10

lubgr