I realise that the solution I have here is far from ideal with C++, so I'm asking what a proper C++ programmer would do in this situation. (C++11)
I have a DialogBox
class, which stores a collection of buttons. At the moment I have a pure abstract inner class DialogBox::Button
with the pure virtual function virtual void callback() const
.
From Java I'm used to using this strategy to create and instantiate an anonymous class deriving from Button in-place which implements the callback function. Something like this:
db.add_button(new Button( "Button text", BUTTONTYPE ) {
public void callback() {
// Callback code
}});
which is what prompted this C++ solution.
My C++ solution therefore looks like
dialogbox.h
class DialogBox {
public:
// Abstract class for buttons with callback functions
class Button;
private:
/* ...
stuff
*/
public:
/* ...
stuff
*/
const std::vector< unique_ptr<DialogBox::Button> >& get_buttons() const;
void add_button( unique_ptr<DialogBox::Button>& new_button );
};
class DialogBox::Button {
private:
/* ...
stuff
*/
public:
// Constructor
Button( const string& button_text, const DialogButtonType button_type = DialogButtonType::NORMAL );
/* ...
stuff
*/
// Virtual callback function
virtual void callback() const = 0;
};
Usage:
// Instantiate
DialogBox db{ /* ... args ... */ };
// Test adding buttons
class Button1 : public DialogBox::Button {
using DialogBox::Button::Button;
public: void callback() const {
// Callback code
}
};
std::unique_ptr<DialogBox::Button> button1{ new Button1{ "Button1", DialogButtonType::ENTER } };
db.add_button( button1 );
This works, but it's clearly not as clean as the Java version and certainly feels like I'm shoehorning in something that C++ is not designed to do.
So, how would a C++ programmer do this? It seems conceptually right to have Button as a class (since it has internal data and its own behaviour). At the moment I'm thinking of using a lambda expression to pass in the callback function to Button's constructor, but I thought I'd get some expert opinion on the subject.
Consider an aggregate:
now you can
add_button({"hello", []{std::cout<<" world\n";}});
Minimal code, minimal boilerplate.
I generally start with this kind of thing, and only add more infrastructure as needed.
The C++11 solution would be to have
Button
look something like this. I'm skipping thestring
andDialogButtonType
parameters for brevity:This allows you to have a container of
Button
s that do completely arbitrary things in theircallback
s - no inheritance necessary. It also allows you to great one-off buttons on the fly by providing them with an arbitrary callback functor as part of the construction process:As a side-note,
add_button
should definitely take itsunique_ptr
argument by value, not by reference.