C ++: как передать имя универсальной функции? - PullRequest
13 голосов
/ 03 июня 2011

Я строю программу на c ++, где пользователь может установить функцию, вызываемую при достижении определенных пользователем условий.Я только немного знаком с C ++.

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

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

Есть идеи о том, как выполнить это в c ++ без необходимости все жестко кодировать?

Ответы [ 4 ]

22 голосов
/ 03 июня 2011

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

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)();
8 голосов
/ 03 июня 2011

Ваш код Python фактически переводит на C ++ почти напрямую:

# 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 в C ++. Оба они являются ассоциативными контейнерами, отображающими значение из одного типа в значение другого (в нашем случае, строка в функцию). В C ++ функции не являются первоклассными гражданами, поэтому вы не можете сохранить функцию на карте, но вы можете сохранить указатель на функцию. Следовательно, определение карты становится немного затруднительным, потому что мы должны указать, что типом значения является «указатель на функцию, которая не принимает аргументов и возвращает void».

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

6 голосов
/ 03 июня 2011

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

http://www.newty.de/fpt/intro.html

0 голосов
/ 03 июня 2011

Другой вариант - функциональные объекты + наследование:

#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;
}

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

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

    getch();
    return 0;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...