Спецификации функций времени выполнения на основе пользовательского ввода

Итак, прошло много времени с тех пор, как я написал что-то большое для C++, и я привык к некоторым тонкостям более современных языков. Это тот, который мучает меня, и я уверен, что есть ответ. Есть ли способ вызвать функцию, указанную пользователем в виде строки во время выполнения? Без необходимости прибегать к какому-то массивному блоку switch / if?

Ситуация, в которой я нахожусь, сводится к следующему: у меня есть множество проблем, связанных с математикой, которые я решил в C++ и указал как "Problem1.cpp/Problem1.h", "Problem2.cpp/Problem2.h"и т. д. Каждая проблема имеет функцию под названием problemX() (где X это номер проблемы), который начинает решение. В начале программы я хотел бы спросить пользователя "Какую проблему вы бы хотели решить?" и они указали бы номер. Затем я хотел бы позвонить соответствующему problemX() функция без необходимости прибегать к массивному жестко закодированному оператору переключения (или к оператору if, или к индексируемому массиву указателей на функции и т. д.).

Я уверен, что это возможно, но я просто не могу вспомнить, как это сделать. Есть идеи?

3 ответа

Решение

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 можно найти по адресу:

Другие вопросы по тегам