Как и другие, я рекомендую вам не изобретать велосипед, если вы можете избежать его. То есть используйте существующую платформу, если можете. Но я собираюсь немного расширить возможное решение.
C ++ не имеет отражения, что означает, что вы не можете во время выполнения принять указатель функции, проверить его и интерпретировать аргументы. Но вы можете сделать это во время компиляции. Решение не будет простым, но оно может быть сделано с помощью стирания типа. Чтобы упростить задачу, я предполагаю, что все функции возвращают void
, это достаточно сложно.
В одном из возможных решений структура диспетчера может выглядеть (ошибки игнорируются):
while (true) {
std::string func = read_function_name();
std::vector<parameter_t> params = read_parameters();
functions[ func ]( params );
}
Теперь, чтобы заполнить пробелы: read_function_name
- это простая функция, которая возвращает имя вызываемой функции. Не функция, только имя. read_parameters
- это функция, которая обрабатывает входной файл и создает последовательность параметров для передачи в функцию. functions
- это ассоциативный контейнер, который отображает имя функции на одну из наших функций. Параметры являются своего рода типом стирания фактических типов параметров, так что мы можем управлять ими в общем виде в одном контейнере. Наши функции являются экземплярами класса, который реализует operator()
, который принимает одну последовательность параметров и выполняет стирание типа для конкретной функции, которую нужно вызвать. Это было просто! По крайней мере, если вы игнорируете детали.
Реализация параметров не слишком сложна, вы можете просто использовать boost::any
и надеяться на лучшее, или вы можете реализовать свое собственное стирание типов с немного большей информацией, чтобы вы могли выполнять лучшее обнаружение ошибок или неявные преобразования или ... вы называете это.
Реализация типа function_t
немного сложнее. Нам нужно выполнить стирание типа для фактического элемента, который можно вызвать, и std::function<>
соответствует этой проблеме. Но мы не можем использовать его, потому что это оставляет нам наименьший возможный интерфейс для функции: operator()
вы знаете параметры , и у нас есть operator()
, который знает параметры * * 1030
Это где большая часть работы должна быть сделана, и вам понадобится стирание вручную. Это может быть реализовано базовым абстрактным классом function_base
, который предлагает virtual operator()( std::vector<parameter_t> [const] & ) [const]
(поиграйте с константностью или забудьте о ней на время, чтобы немного упростить проблему). function_t
будет просто удерживать указатель на этот базовый тип и выполнять вызов. До здесь все еще просто.
Реализация стирания типа для функций ... Теперь следующая проблема заключается в том, как реализовать каждый конкретный производный тип из function_base
, и в этом месте все становится немного сложнее (мы согласились, что остальное было просто, не так ли мы?) Вам необходимо предоставить шаблон конструктора в function_t
, который будет принимать любую функцию, создавать экземпляр шаблонного типа, производного от function_base
, и хранить указатель внутри function_t
.
Опять же, чтобы избежать сложности, предположим, что вы можете сделать это с одним аргументом. Остальное - просто больше кода ... Реализация function_impl<>
просто должна хранить исходный указатель функции и запоминать тип и количество (в данном случае 1) аргументов. Реализация operator()
выполняет итерацию вектора параметров и для каждого параметра стирает тип (преобразует обратно в исходный тип) и вызывает функцию. Этот код должен быть в значительной степени ручным в шаблоне, но он будет многократно использоваться для всех функций одной арности.
Чтобы упростить ситуацию, вы можете использовать библиотеки признаков функций, которые смогут извлекать тип и количество аргументов из получаемой функции.
Так что же осталось для кода пользователя? Это просто, и в данном случае я это имею в виду. Ваш интерфейс должен предлагать function_t
, который может быть построен из любого указателя функции (если он соответствует требованиям того, что вам удалось реализовать там), и функцию для регистрации любого такого function_t
в вашей системы, указав имя:
// user code: registration of a new function
void print_single_int( int );
lib::function_t f( &print_single_int );
lib::register( "print_single_int", f );
Эти две строки нужно будет вводить для каждой функции, которую вы хотите добавить в свою систему.Конечно, вы можете решить, что сложность решения не компенсирует проблему и пойти на ручную реализацию, если пользовательский код просто создает закодированные вручную производные function_base
, которые вручную обрабатывают вектор параметров ивызывает функцию ... если на вашем языке сценариев вы хотите реализовать несколько операций, возможно, не стоит дополнительных усилий по его созданию.