I'm trying to tackle OOP with a little project of mine. I'm trying to create a simple Menu class (terminal-based) to build, show and execute a menu. And this menu should execute functions from a second class Calendar which is the "main" program.
Which interface I try to achieve is:
int main()
{
Menu menu;
menu.add("Exit program", &Calendar::exit);
menu.show();
menu.execute(menu.EXIT); // or just 0
}
Which should look something like:
CALENDAR MENU
0 Exit program
What I want to achieve is the add()-method should add a function-pointer to a map<int, function-pointer>. This is where my problem starts. Currently I have a hard-coded array with function-pointers in my execute()-method. Now I want to dynamically add a menu-item with the appropriate function.
class Menu {
// declaration of function-pointer thingy
typedef void (Calendar::*CalendarPtr)();
// will hold the string in the add()-method
std::vector<std::string> options_strings;
// will hold the integer associated to the appropriate function
std::map<int, CalendarPtr> options_map;
public:
// possible options
enum OPTIONS {EXIT};
// shows the menu on-screen
void show();
// should add string to vector, function-pointer to map
void add(std::string, CalendarPtr);
// should execute appropriate function (based on option passed)
void execute(OPTIONS option);
};
// ...
void Menu::add(std::string option, CalendarPtr) // what doesn't work obviously
{
// adding string to vector
options_strings.push_back(option);
// Just binding the latest entry of the string to the function-pointer
int index = options_strings.size();
// TRYING to add function-pointer to map
options_map.insert(std::pair<int, CalendarPtr>(index, CalendarPtr));
}
Menu::execute(OPTIONS option) // enum declared as public in class
{
int option = EXIT; // or 0
CalendarPtr cptr[] = {&Calendar::exit};
Calendar* cal;
if (option >= 0 && option < static_cast<int>(options_strings.size()))
(cal->*cptr[option])();
}
The insert-line currently gives:
../src/Menu.cpp:24: error: expected primary-expression before ';' token
which means that my use of function-pointers isn't right, but what is the right way? How would I declare the wanted add()-method (so which parameters), how would I insert the function-pointers in the map and how would I call the desired item in the map with my execute()-method?
If something is not clear, please say it and I'll try to explain it better/different :)
Thanks in advance!
EDIT 1:
I changed
void Menu::add(std::string option, CalendarPtr)
// ...
options_map.insert(std::pair<int, CalendarPtr>(index, CalendarPtr));
to
void Menu::add(std::string option, CalendarPtr cptr)
// ...
options_map.insert(std::pair<int, CalendarPtr>(index, cptr));
But how do you "call" the function at the inserted (say index 0) position? options_map0; doesn't work...
EDIT 2:
It inserted the function-pointer with index 1 ;) Problem solved!
I get an error when I have this method:
void Menu::execute(OPTIONS option) { Calendar c; Calendar* calendar = &c; CalendarPtr cptr = options_map.at(0); (*calendar.*cptr)(); }
terminate called after throwing an instance of 'std::out_of_range' what(): map::at
So I conclude that the insert failed, why so?
Apart the fact that your add function should read:
void Menu::add(std::string option, CalendarPtr ptr) // what doesn't work obviously
{
// adding string to vector
options_strings.push_back(option);
// Just binding the latest entry of the string to the function-pointer
int index = options_strings.size();
// TRYING to add function-pointer to map
options_map.insert(std::pair<int, CalendarPtr>(index, ptr));
}
I would recommend to implement a delegate (such as The Impossibly Fast C++ Delegates ) for this. Function calls through member function pointers are very slow, and the delegate version would be much more elegant, and very fast.
First: you don't actually have to use options_strings
, you know that? You could just use
std::map<std::string, CalendarPtr> options_map;
to map strings to member function pointers.
So once you recover your CalendarPtr
, you call it like this:
void CallIndex(Calendar* calendar, int index) {
CalendarPtr pfunc = options_map[index].second;
(*calendar).*pfunc();
}
But you may want to avoid syntax errors when calling a member function using a pointer-to-member-function .
In Menu::add
, you haven't given your CalendarPtr
argument a name. This is legal, so long as you don't actually want to use that argument!
The main mistake is that you are trying to add a type to a map
instead of object. Others include - using non-constant reference to std::string
in parameters, which results in unnecessary copying. Using int
to represent a size, its capacity is not enough, you have to use size_t
instead. Here is the code that should help:
#include <map>
#include <vector>
#include <string>
struct Calendar {
void exit () {}
};
class Menu {
// declaration of function-pointer thingy
typedef void (Calendar::*CalendarPtr)();
// will hold the string in the add()-method
std::vector<std::string> options_strings;
// will hold the integer associated to the appropriate function
std::map<size_t, CalendarPtr> options_map;
public:
// possible options
enum OPTIONS {EXIT};
// shows the menu on-screen
void show();
// should add string to vector, function-pointer to map
void add(const std::string &, CalendarPtr);
// should execute appropriate function (based on option passed)
void execute(OPTIONS option);
};
// ...
void Menu::add (const std::string & option, CalendarPtr v) // what doesn't work obviously
{
// adding string to vector
options_strings.push_back (option);
// Just binding the latest entry of the string to the function-pointer
size_t index = options_strings.size ();
// TRYING to add function-pointer to map
options_map.insert (std::make_pair (index, v));
}
void Menu::execute(OPTIONS option) // enum declared as public in class
{
Calendar c;
option = EXIT; // or 0
CalendarPtr cptr[] = {&Calendar::exit};
Calendar* cal = &c;
if (option >= 0 && option < static_cast<int>(options_strings.size()))
(cal->*cptr[option])();
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.