Чтение этих команд нужно сделать в три раза:
- Узнайте какую функцию вызвать.
- Преобразовать список строк аргументов в аргументы правильного типа .
- Вызовите функцию , передав эти аргументы, чтобы сделать все, что нужно сделать.
Абстрагироваться от # 2 довольно сложно, поскольку в C ++ очень мало поддержки для работы с различными типами, которые известны только во время выполнения, но это не невозможно.
Однажды я видел статью, в которой кто-то использовал шаблонное мета-программирование, чтобы узнать о параметрах зарегистрированной функции, а затем сгенерировал код, который разбивает список строк на соответствующие аргументы. Функции сохранялись в карте строк ключей для указателей на функции (используя стирание типов для хранения функций с разными сигнатурами).
Вот эскиз о том, как использовать стирание типов для хранения различных указателей функций на карте:
struct func_base {
virtual void operator()(std::istream&) const = 0;
};
template< typename F >
class function : public func_base {
public:
function(F f) : func_(f) {}
void operator()(std::string& arguments) const;
private:
func_base func_;
};
typedef std::map< std::string, std::shared_ptr<func_base> > func_map;
template< typename F >
void addFunc(func_map& map, const std::string& keyword, F f)
{
assert(map.find(keyword) == map.end());
map[keyword] = std::shared_ptr<func_base>(new function<T>(f));
}
Это позволило бы function<F>::operator()()
разделить аргументы на отдельные строки, преобразовать их в соответствующие типы и затем вызвать функцию с ними.
Добавление строки в список аргументов не должно быть проблемой, поэтому я пропущу это. Трудной частью является вызов функции с правильными параметрами, заданными в этом списке. Обратите внимание, что тип функции известен в function<F>::operator()()
во время компиляции , поэтому в вашем распоряжении есть все методы шаблонного мета-программирования.
В этой статье ISTR сделал это, создав кортежи в соответствии со списком параметров функции, и имел средства для вызова любой функции с таким кортежем. Вот вы могли бы создать такие кортежи с рекурсивными вызовами:
template< typename Tuple >
Tuple convert_args(const std::string& line)
{
Tuple result;
// I don't know tuples well enough yet, so here's just an
// algorithm rather than code:
// 1. read first argument from line and put it into tuple's first element
// 2. call yourself for a tuple that consists of the remaining elements of Tuple
// 3. fill the remaining tuple elements from the result of #2
return result
}
Затем используйте черты для вызова этих функций:
template<typename F>
struct func_traits;
template<typename R, typename A1>// function taking one arg
struct func_traits<R(*)()> {
typedef std::tuple<A1> arg_list;
static R call(R(*f)(), const arg_list& args)
{
return f(std::get<0>(arg_list)); // how do you access an element in a tuple
}
};
template<typename R, typename A1, typename A2>// function taking two args
struct func_traits<R(*)()> {
typedef std::tuple<A1,A2> arg_list;
static R call(R(*f)(), const arg_list& args)
{
return f(std::get<0>(arg_list), std::get<1>(arg_list));
}
};
// repeat for as many args as you'll need