简体   繁体   中英

Pass member function pointers by argument and store them

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.

Update

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.

Update 2:

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM