I\'d like to build a base (abstract) class (let\'s call it type::base
) with some common funcionality and a fluent interface, the problem I\'m facing is the retu
One solution would work like this:
return *static_cast<my_type*>(&my_type().with_foo().with_bar());
Using static_cast
basically tells the compiler 'I know what I'm doing here'.
The way I'd do it in C#, and I believe it would work in C++ too is to provide a default implementation for with_foo()
and with_bar()
... Forgive my c#, but:
class base {
virtual base with_foo()
{ throw new NotImplementedException(); }
virtual base with_bar();
{ throw new NotImplementedException(); }
}
In C++ you should be returing pointers or references rather than values. Also, you might want to explain what you mean by "fluent interfaces".
You should be returning references/pointers, and you should not need to keep the type information.
class base {
public:
base();
virtual ~base();
base &with_foo();
base &with_bar();
protected:
// whatever...
};
class my_type : public base {
public:
my_type();
// more methods...
};
base *build_my_type()
{
return &new my_type()->with_foo().with_bar();
}
You already have a virtual destructor. Presumably you have other virtual functions. Access everything through the base type and the virtual functions declared there.
This problem of "losing the type" can be solved with templates - but it's rather complicated.
Eg.
class Pizza
{
string topping;
public:
virtual double price() const;
};
template <class T, class Base>
class FluentPizza : public Base
{
T* withAnchovies() { ... some implementation ... };
};
class RectPizza : public FluentPizza<RectPizza, Pizza>
{
double price() const { return length*width; :) }
};
class SquarePizza : public FluentPizza<SquarePizza, RectPizza>
{
... something else ...
};
You can then write
SquarePizza* p=(new SquarePizza)->withAnchovies();
The pattern is that instead of
class T : public B
you write
class T : public Fluent<T, B>
Another approach could be not to use fluent interface on the objects, but on pointers instead:
class Pizza { ... };
class RectPizza { ... };
class SquarePizza { ... whatever you might imagine ... };
template <class T>
class FluentPizzaPtr
{
T* pizza;
public:
FluentPizzaPtr withAnchovies() {
pizza->addAnchovies(); // a nonfluent method
return *this;
}
};
Use like this:
FluentPizzaPtr<SquarePizza> squarePizzaFactory() { ... }
FluentPizzaPtr<SquarePizza> myPizza=squarePizzaFactory().withAnchovies();