Consider:
// member data omitted for brevity
// assume that \"setAngle\" needs to be implemented separately
// in Label and Image, and that Button does need
I think (I haven't tested it) this will do it using templates:
template<class T> struct TWidget {
T& move(Point newPos) { pos = newPos; return (T&)*this; }
};
template<class T> struct TLabel : TWidget<T> { ... }
struct Label : TLabel<Label> { ... }
struct Button : TLabel<Button> { ... }
Notes:
LabelT and Label classes).dynamic_cast if you like.return *this", the base class could contain a T& as a data member (the derived class would pass this to the base class' constructor), which would be an extra data member, but which avoids a cast and I think may permit composition instead of or as well as inheritance.You can extend CRTP to handle this. monjardin's solution goes in the right direction. All you need now is a default Label implementation to use it as a leaf class.
#include <iostream>
template <typename Q, typename T>
struct Default {
typedef Q type;
};
template <typename T>
struct Default<void, T> {
typedef T type;
};
template <typename T>
void show(char const* action) {
std::cout << typeid(T).name() << ": " << action << std::endl;
}
template <typename T>
struct Widget {
typedef typename Default<T, Widget<void> >::type type;
type& move() {
show<type>("move");
return static_cast<type&>(*this);
}
};
template <typename T = void>
struct Label : Widget<Label<T> > {
typedef typename Default<T, Widget<Label<T> > >::type type;
type& set_text() {
show<type>("set_text");
return static_cast<type&>(*this);
}
};
template <typename T = void>
struct Button : Label<Button<T> > {
typedef typename Default<T, Label<Button<T> > >::type type;
type& push() {
show<type>("push");
return static_cast<type&>(*this);
}
};
int main() {
Label<> lbl;
Button<> btt;
lbl.move().set_text();
btt.move().set_text().push();
}
That said, consider whether such an effort is worth the small added syntax bonus. Consider alternative solutions.
This compiles on gcc 4.3.2 and is sort of a mixin pattern.
#include <string>
using namespace std;
struct Point {
Point() : x(0), y(0) {}
Point(int x, int y) : x(x), y(y) {}
int x, y;
};
template <typename T>
struct Widget {
T& move(Point newPos) {
pos = newPos;
return *reinterpret_cast<T *> (this);
}
Point pos;
};
template <typename T>
struct Label : Widget<Label<T> > {
T& setText(string const& newText) {
text = newText;
return *reinterpret_cast<T *> (this);
}
T& setAngle(double newAngle) {
angle = newAngle;
return *reinterpret_cast<T *> (this);
}
string text;
double angle;
};
struct Button : Label<Button> {
Button& setAngle(double newAngle) {
backgroundImage.setAngle(newAngle);
Label<Button>::setAngle(newAngle);
return *this;
}
Label<Button> backgroundImage;
};
int main() {
Button btn;
// oops: Widget::setText doesn't exist
btn.move(Point(0,0)).setText("Hey");
// oops: calling Label::setAngle rather than Button::setAngle
btn.setText("Boo").setAngle(0.0);
}