Итак, прошло много времени с тех пор, как я написал что-то большое для c ++, и я привык к некоторым тонкостям более современных языков. Это тот, который мучает меня, и я уверен, что есть ответ. Есть ли способ вызвать функцию, указанную пользователем в виде строки во время выполнения? Без необходимости прибегать к какому-то массивному блоку switch / if?
Ситуация, в которой я нахожусь, сводится к следующему: у меня есть множество проблем, связанных с математикой, которые я решил в C ++ и указал как «Problem1.cpp / Problem1.h», «Problem2.cpp / Problem2 .h «и т. д. В каждой задаче есть функция, которая называется problemX()
(где X
это номер проблемы), который начинает решение.
В начале программы я хотел бы спросить пользователя «Какую проблему вы бы хотели решить?» и они указали бы номер. Затем я хотел бы позвонить соответствующему problemX()
функция без необходимости прибегать к массивной жестко закодированной инструкции switch (или к оператору if, или к индексируемому массиву указателей на функции и т. д.).
Я уверен, что это возможно, но я просто не могу вспомнить, как это сделать. Есть идеи?
C ++ не имеет автоматического отражения компиляции или времени исполнения своего кода на языке. Многие библиотечные фреймворки имеют во время выполнения отражение символов в библиотеке.
Итак, решение 1:
Вставьте ваши проблемы в их собственные динамические библиотеки, и пусть основная программа динамически загрузит их и найдет имена символов, которые они экспортируют.
Решение 2:
Замените ваши сырые функции в стиле C именованными объектами. Таким образом, вы могли бы иметь:
class Problem;
void RegisterProblem( std::string name, Problem const* problem );
std::map< std::string, Problem const* >& GetProblems();
class Problem
{
protected:
Problem( std::string name ): RegisterProblem( std::move(name), this ) {}
virtual void operator() const = 0;
virtual ~Problem() {}
};
class Problem1: public Problem
{
public:
Problem1():Problem("Problem1") {}
virtual void operator() const { /* implementation */ }
};
// in .cpp file:
Problem1 problem1Instance();void RegisterProblem( std::string name, Problem const* problem )
{
GetProblems()[name] = problem;
}
std::map< std::string, Problem const* >& GetProblems()
{
static std::map< std::string, Problem const* > problemMap;
return problemMap;
}
int main()
{
// parse user input to get this string:
std::string testInput = "Problem1";
// run the problem determined by testInput:
Problem* prob = GetProblems()[testInput];
Assert(prob);
(*prob)();
}
Выше у нас есть какой-то ужасно написанный код, в котором есть проблемы с саморегистрацией (которые регистрируются в статической карте), и main (), который выполняет любую проблему, указанную в строке.
Я думаю, что это будет чище:
// In RegisterProblem.h:
// these two have obvious implementations:
std::map< std::string, std::function<void()> >& GetProblems();
bool RegisterProblem( std::string s, std::function<void()> ); // always returns true
// In problem1.cpp:
void Problem1(); // implement this!
bool bProblem1Registered = RegisterProblem( "Problem1", Problem1 );
// In problem2.cpp:
void Problem2(); // implement this!
bool bProblem2Registered = RegisterProblem( "Problem2", Problem2 );
// etc
// in main.cpp:
int main(int argc, char** argv)
{
if (argc == 0)
return -1; // and maybe print help
auto it = GetProblems().find( argv[1] );
if (it == GetProblems().end())
return -1; // and maybe print help
it->second(); // call the problem
}
где мы покончили с ненужной иерархией классов и просто поддерживаем карту между строкой и void()
функции. Ведение этой карты распространяется на каждое место, где написаны функции, поэтому нет центрального списка или оператора if.
Я не стал бы отправлять что-либо с таким грубым кодом, как указано выше, но я надеюсь, что вы поняли идею.
unordered_map
строк для указателей на функции.
Настройте пользовательский ввод, чтобы убедиться, что все в нижнем регистре (или в верхнем, если вам нравится кричать), а затем просто найдите функцию. Если он существует, вызовите его, иначе ошибка.
Вы должны использовать std::map<std::string,_function_pointer_defined_by_you>
хранить имена функций как ключи, а указатели функций как значения. Вы также можете использовать std::unordered_map<std::string,_function_pointer_defined_by_you>
что-то вроде std::hash_map
, Если вы можете использовать C ++ 11, вы найдете std::unordered_map
в заголовочном файле <unordered_map>
и если вы не можете в <tr1/unordered_map>
, Документацию о карте и unordered_map можно найти по адресу: