Question

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!

Was it helpful?

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;
    }
};

OTHER TIPS

Take a look at Curiously recurring template pattern.

If Base is an abstract type (only instantiated in its subclasses) then make it a template taking type name. Your Derive will extend the template - e.g. Derived : public Base<Derived>. If Base is a concrete type - then you will need to introduce a new abstract class that would be a base type for Base and Derived.

This way withFoo can be templated to return real type.

You options are either CRTP (as Mark B illustrated), or using runtime dispatch on the variable name, eg.

Derived d;
d.with("Foo", "foo").with("Bar", "bar").doOutput();

this won't be particularly performant, but it's very flexible, and a good match for protocols which can take arbitrary fields.

Since your types are not polymorphic (no virtual functions) Base has no knoweledge of Derived.

You can reach you objective esentially with static polymorphism:

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

class Derived : public Base<Derived> {
protected:
 ......
}

The drawback is that there is anymore a Base class, but as many Base instanciation as are the possible derived, hence you cannot anymore have a Base& or Base* pointing to whatever Derived.

If you need a common-base to collect, you need another CommonBase (not templetized) from which Base can derive from.

Another possibility is making Base (the old one) polimorphic by making withFoo virtual. At that point, in Derived, you can override withFoo to return the Derived& covariant type:

class Base
{
   ...
   virtual Base& withFoo(...);
   ...
   virtual ~Base() {} //just to make all the hierarchy destructible through base
};

class Derived: public Base
{
   ...
   virtual Derived& withFoo(type arg)
   { return static_cast<Derived&>(Base::withFoo(arg)); }
   ...
};

This still embrace the classic OOP paradigm, but adds run-time overhead (the vtables) and has the disadvantage it is based on a feature (covariant return type of virtual functions) not all compilers support.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top