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 with
s 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 withParam
s overloaded, some of which don't make sense to include in a GET parameter list.
Thanks in advance!
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;
}
};