Search code examples
c++polymorphismfluent

method chaining with polymorphism c++


Is it possible to write fluent chanining methods that return a derived type? Consider the following two classes:

class Base {
protected:
    std::string mFoo;
public:
    Base& withFoo(std::string foo) {
        mFoo = foo;
        return *this;
    }
};

class Derived : public Base {
protected:
    std::string mBar;
public:
    Derived& withBar(std::string bar) {
        mBar = bar;
        return *this;
    }

    void doOutput() {
        std::cout << "Foo is " <<
            mFoo << ".  Bar is " <<
            mBar << "." << std::endl;
    }
};

I would then like to build my object and use it like this:

Derived d;
d.withFoo("foo").withBar("bar").doOutput();

This of course fails since withFoo returns a Base. Since all my with methods simply set member variables, I can specify the derived withs first. The problem is my builder method (doOutput in the example above) would then need to be a separate statement.

Derived d;
d.withBar("this is a bar")
    .withFoo("this is my foo");
d.doOutput();

My question is whether there is some way for withFoo to return an unknown derived type so that Base may be used seamlessly with multiple derived classes (after all, *this is a Derived, although Base (correctly) is unaware of the fact).

For a more concrete example, I'm writing a few classes to access a REST server. I have a RestConnection class with method withUrl, a PostableRest class with methods withParam and doPost, and a GettableRest class with doGet. I suspect this is not possible and will probably try cramming a bunch of virtual methods into RestConnection but I hate to do that when there are multiple withParams overloaded, some of which don't make sense to include in a GET parameter list.

Thanks in advance!


Solution

  • I think you could utilize CRTP here, something like the following, where the derived class tells the base what type it is:

    class Base
    {
        // Abstract/virtual interface here.
    };
    
    template <class Derived>
    class Base_T : public Base
    {
    private:
        std::string mFoo;
    
    public:
        Derived& withFoo(std::string foo) {
            mFoo = foo;
            return *static_cast<Derived*>(this);
        }
    };
    
    class Derived : public Base_T<Derived> {
    private:
        std::string mBar;
    public:
        Derived& withBar(std::string bar) {
            mBar = bar;
            return *this;
        }
    
        void doOutput() {
            std::cout << "Foo is " <<
                mFoo << ".  Bar is " <<
                mBar << "." << std::endl;
        }
    };