Re: Dynamic menu
On 9 juuli, 14:54, "Francesco S. Carta" <entul...@gmail.com> wrote:
Hi there,
in it.comp.lang.c++ somebody asked for directions (for a tutorial,
actually) about creating a menu in C++, and I've answered mentioning
pointers to functions and creating a simple example that accepts only
pointers to void functions taking no parameters.
Then I've started fiddling with the idea and I've implemented a more
complex version that accepts pointers to functions that take parameters
too, allowing the client code to pass any kind of function - with a
single parameter but without type restrictions, as the "add_item()"
method is a template).
Here below I paste the complete code with an usage example, I wonder if
there is any simpler way of achieving the same purposes, maybe without
using inheritance.
Any other suggestion or correction about my coding style will be
welcome, I completely avoided to add comments because the code should be
self-explaining even if a bit convoluted.
1) Why so lot of copy-paste? For example caption(), command() and
parent() are worthlessly virtual, all derived classes implement them
same copy-paste.
2) Where is const correctness? For example the same functions should
be const. They do not change visible state of class.
Thanks for your attention.
//-------
#include <iostream>
#include <typeinfo>
#include <vector>
#include <list>
class MenuItem {
public:
MenuItem() {}
virtual ~MenuItem() {}
virtual void execute() {}
virtual void display() {}
virtual std::string caption() = 0;
virtual std::string command() = 0;
virtual MenuItem* parent() = 0;
};
class MenuItemContainer : public MenuItem {
std::string _caption;
std::string _command;
MenuItem* _parent;
protected:
friend class Menu;
std::vector<MenuItem*> _items;
public:
MenuItemContainer(std::string caption,
std::string command,
MenuItem* parent) :
_caption(caption),
_command(command),
_parent(parent) {}
std::string caption() {
return _caption;
}
std::string command() {
return _command;
}
MenuItem* parent() {
return _parent;
}
};
class MenuItemWithoutParam : public MenuItem {
std::string _caption;
std::string _command;
MenuItem* _parent;
void (*_callback)();
public:
MenuItemWithoutParam(std::string caption,
std::string command,
MenuItem* parent,
void (*callback)()) :
_caption(caption),
_command(command),
_parent(parent),
_callback(callback) {}
void execute() {
if (_callback) {
_callback();
}
}
std::string caption() {
return _caption;
}
std::string command() {
return _command;
}
MenuItem* parent() {
return _parent;
}
};
template<class T> class MenuItemWithParam : public MenuItem {
std::string _caption;
std::string _command;
MenuItem* _parent;
void (*_callback)(T*);
T* _param;
public:
MenuItemWithParam(std::string caption,
std::string command,
MenuItem* parent,
void (*callback)(T*),
T* param) :
_caption(caption),
_command(command),
_parent(parent),
_callback(callback),
_param(param) {}
void execute() {
if (_callback && _param) {
_callback(_param);
}
}
std::string caption() {
return _caption;
}
std::string command() {
return _command;
}
MenuItem* parent() {
return _parent;
}
};
class Menu {
MenuItemContainer _basecontainer;
std::istream& _input;
std::ostream& _output;
public:
Menu(std::istream& input,
std::ostream& output) :
_basecontainer("","",0),
_input(input),
_output(output) {}
template<class T>
MenuItem* add_item(std::string caption,
std::string command,
MenuItem* parent,
void (*callback)(T*),
T* param) {
MenuItem* item;
if (parent && typeid(*parent) == typeid(MenuItemCo=
ntainer)) {
MenuItemContainer* container
= static_cast<MenuItemContainer*>(parent);
item = new MenuItemWithParam<T>(caption,
=
command,
=
parent,
=
callback,
=
param);
container->_items.push_back(item);
} else {
item = new MenuItemWithParam<T>(caption,
=
command,
=
&_basecontainer,
=
callback,
=
param);
_basecontainer._items.push_back(item);
}
return item;
}
MenuItem* add_item(std::string caption,
std::string command,
MenuItem* parent,
void (*callback)()) {
MenuItem* item;
if (parent && typeid(*parent) == typeid(MenuItemCo=
ntainer)) {
MenuItemContainer* container
= static_cast<MenuItemContainer*>(parent);
item = new MenuItemWithoutParam(caption,
=
command,
=
parent,
=
callback);
container->_items.push_back(item);
} else {
_basecontainer._items.push_back(item);
}
return item;
}
MenuItem* add_item(std::string caption,
std::string command,
MenuItem* parent) {
MenuItem* item;
if (parent && typeid(*parent) == typeid(MenuItemCo=
ntainer)) {
MenuItemContainer* container
= static_cast<MenuItemContainer*>(parent);
item = new MenuItemContainer(caption,
=
command,
=
parent);
container->_items.push_back(item);
} else {
item = new MenuItemContainer(caption,
=
command,
=
&_basecontainer);
_basecontainer._items.push_back(item);
}
return item;
}
void interact() {
std::string line;
MenuItemContainer* current = &_basecontainer;
MenuItem* selected;
for (;;) {
selected = 0;
_output << "\n-------" << std::endl;
for (size_t i = 0; i < current->_items.size(=
); ++i) {
_output << "[" << current->_items[i]->=
command()
<< "] " << current->_items[i]->caption=
()
<< std:: endl;
}
if (current->parent()) {
_output << "[up] Upper menu" << std::e=
ndl;
}
_output << "[exit] Exit program" << std::endl;
_output << "Insert command:" << std::endl << "=
";
getline(_input, line);
if (line == "up" && current->parent()) {
current = static_cast<MenuItemContai=
ner*>
(current->parent()=
);
} else if (line == "exit") {
return;
} else {
for (size_t i = 0; i < current->_ite=
ms.size(); ++i) {
if (current->_items[i]->comman=
d() == line) {
selected = current->=
_items[i];
break;
}
}
}
_output << std::endl;
if (selected) {
if (typeid(*selected) == typeid(Me=
nuItemContainer)) {
current = static_cast<MenuIt=
emContainer*>
(selected)=
;
} else {
selected->execute();
}
} else {
_output << "Unrecognized command" << s=
td::endl;
}
}
}
};
// client section
using namespace std;
Menu menu(cin, cout);
void increment(int* i) {
++*i;
}
void print(int* i) {
cout << *i << endl;
}
void cite(string* str) {
cout << *str << endl;
}
void addquote(MenuItem* parent) {
static list<string> quotes;
string caption;
string command;
string quote;
cout << "Insert caption: " << endl;
cout << ">";
getline(cin, caption);
cout << "Insert command: " << endl;
cout << ">";
getline(cin, command);
cout << "Insert quote: " << endl;
cout << ">";
getline(cin, quote);
if (caption != "" && command != "" && quote != "") {
quotes.push_back(quote);
menu.add_item(caption, command, parent, cite, "es.=
back());
} else {
cout << "Unable to create quote"
<< " please retry and fill in all fields" << endl;
}
}
int main() {
int i = 42;
MenuItem* program = menu.add_item("Program", "p", 0);
MenuItem* variable = menu.add_item("Variable", "v", program)=
;
MenuItem* quotes = menu.add_item("Quotes", "q", program);
menu.add_item("Increment", "i", variable, increment, &i);
menu.add_item("Print", "p", variable, print, &i);
string hello = "Hello world!";
string panic = "Don't panic!";
menu.add_item("Add quote", "a", quotes, addquote, quotes);
menu.add_item("Hello", "h", quotes, cite, &hello);
menu.add_item("H2G2", "42", quotes, cite, &panic);
menu.interact();
return 0;}
//-------
--
FSC -http://userscripts.org/scripts/show/59948
http://fscode.altervista.org-http://sardinias.com