С++: Как передать общее имя функции?

Я создаю программу в С++, где пользователь может установить функцию, которая будет вызываться, когда будут определены условия пользователя. Я немного разбираюсь в С++.

Я знаю, как это сделать в python. Вы просто определяете функции и помещаете имена указанных функций в структуру (я всегда использовал словарь). Когда вы перейдете к использованию функции, вы сделаете вызов, похожий на:

methods = { "foo" : foo, "bar" : bar } 
choice = input("foo or bar? ")
methods[choice]()

Любые идеи о том, как это сделать в С++ без необходимости жесткого кода?

Ответ 1

Ваш код на Python фактически переводит непосредственно на С++:

# Python:
# Create a dictionary mapping strings to functions
methods = { "foo" : foo, "bar" : bar } 
// C++:
// create a map, mapping strings to functions (function pointers, specifically)
std::map<std::string, void(*)()> methods; 
methods["foo"] = foo;
methods["bar"] = bar;

# Python
choice = input("foo or bar? ")
// C++:
std::string choice;
std::cout << "foo or bar? ";
std::cin >> choice;

# Python:
methods[choice]()
// C++
methods[choice]();

Словарь Python похож на С++ map. Они оба являются ассоциативными контейнерами, сопоставляя значение от одного типа к значению другого (в нашем случае строка для функции). В С++ функции не совсем первоклассные граждане, поэтому вы не можете хранить функцию на карте, но вы можете сохранить указатель на функцию. Следовательно, определение карты становится немного волосатым, потому что мы должны указать, что тип значения является "указателем на функцию, которая не принимает аргументов и возвращает void".

С одной стороны, предполагается, что все ваши функции имеют одну и ту же подпись. Мы не можем хранить обе функции, которые возвращают void и функции, которые возвращают int в одной карте без какого-либо дополнительного обмана.

Ответ 2

Вы можете использовать карту указателей функций:

void foo() { }
void bar() { }

typedef void (*FunctionPtr)();
typedef std::map<std::string, FunctionPtr> FunctionMap;

FunctionMap functions;
functions.insert(std::make_pair("foo", &foo));
functions.insert(std::make_pair("bar", &bar));

std::string method = get_method_however_you_want();

FunctionMap::const_iterator it(functions.find(method));
if (it != functions.end() && it->second)
    (it->second)();

Ответ 4

Другим вариантом являются объекты-объекты + наследование:

#include <string>
#include <iostream>
#include <conio>
#include <exception>
//---------------------------------------------------------------------------

struct Method{
    virtual ~Method(){}

    virtual
    void operator() (void)=0;
};
struct foo: public Method{
    virtual ~foo(){}

    virtual
    void operator() (void){
        std::cout << "this is foo\n";
    }
};
struct bar: public Method{
    virtual ~bar(){}

    virtual
    void operator() (void){
        std::cout << "this is bar\n";
    }
};

Method* getMethodByName(std::string methName){
    if( methName == "foo" )
        return new foo();
    else if( methName == "bar" )
        return new bar();

    throw invalid_argument("Unknown method");
}
//---------------------------------------------------------------------------

int main(int argc, char* argv[])
{
    std::string choice;

    std::cout << "foo or bar?\n";
    std::cin >> choice;

    boost::shared_ptr<Method> method = getMethodByName(choice);
    (*method)();

    getch();
    return 0;
}

Хотя для этого требуется повысить интеллектуальный указатель lib. С ванилью С++:

    Method* method = getMethodByName( choice );
    try{
        (*method)();
        delete method;
    }
    catch(...){
        delete method;
    }

    getch();
    return 0;