C ++: метод вызова функций, список параметров которых был проанализирован из текстового файла - PullRequest
0 голосов
/ 22 июля 2011

Я хочу разрешить вызов функций в моей программе из текстового файла, похожего на скрипт.

Я бы хотел иметь возможность зарегистрировать любую функцию в этом "диспетчере скриптов", не заставляя его соответствовать какой-то определенной сигнатуре. Следовательно, возможность вызова из скрипта: MyFunc (bool, string) или MyFunc2 (int, float, char). Со стороны разбора я могу поместить эти параметры в список параметров, но проблема в том, как я могу передать эти параметры в функцию?

Я не могу назвать это как MyFunc (paramlist [0], paramlist [1]), так как это вызывает особую подпись. Я также не хочу, чтобы вызываемые функции должны были знать о «диспетчере скриптов», и поэтому им не нужно иметь возможность обрабатывать списки параметров.

Как я могу отделить эти два компонента (вызываемые функции и "диспетчер сценариев"), не написав какой-нибудь обертки вокруг первого (вызываемые функции)?

Ответы [ 2 ]

0 голосов
/ 22 июля 2011

Как и другие, я рекомендую вам не изобретать велосипед, если вы можете избежать его. То есть используйте существующую платформу, если можете. Но я собираюсь немного расширить возможное решение.

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, которые вручную обрабатывают вектор параметров ивызывает функцию ... если на вашем языке сценариев вы хотите реализовать несколько операций, возможно, не стоит дополнительных усилий по его созданию.

0 голосов
/ 22 июля 2011

Конечно, вы можете делать такие вещи самостоятельно, но я думаю, что лучше просто использовать Lua (http://lua.org).Lua хорошо интегрируется с программами на языке c / c ++, и для этого доступно множество исчерпывающей документации и руководств;например, посмотрите на это: http://csl.sublevel3.org/lua/ (обратите внимание на раздел «вызов функций C из Lua», это то, что вам нужно).

...