Есть несколько вариантов.Я предполагаю, что вы хотите что-то, что пользователь может «вызвать» несколько раз, например:
void *s = make_func("2 * x + 7");
...
printf("%lf\n", call_func(s, 3.0)); // prints 13
...
printf("%lf\n", call_func(s, 5.0)); // prints 17
...
free_func(s);
Один из вариантов - реализовать это как рекурсивную структуру, содержащую указатели и константы функций.Что-то вроде:
enum item_type { VAR, CONST, FUNC };
struct var {
enum item_type;
int id;
};
struct constant {
enum item_type;
double value;
};
struct func {
enum item_type;
double (*func)(double, double);
enum item_type *a, *b;
};
Тогда make_func
будет анализировать приведенную выше строку во что-то вроде:
(struct func *){ FUNC, &plus,
(struct func *){ FUNC, ×,
(struct constant *){ CONST, 2 },
(struct var *){ VAR, 'x' } }
(struct constant *){ CONST, 7 } }
Если вы понимаете, что - enum type_item
в struct func
используется для указания на следующий узел в дереве (точнее, на первый элемент этого узла, который является enum
), а enum
- это то, что наш код использует, чтобы выяснить, какой тип элемента.Затем, когда мы используем функцию call(void *, ...)
, она подсчитывает, сколько существует переменных - это количество дополнительных аргументов, которые должна быть передана функции call
, - затем заменяет переменные значениями, с которыми мы ее вызвали, затемвыполняет вычисления.
Другой вариант (который, вероятно, будет значительно быстрее и проще в расширении) - это использовать что-то вроде libjit , чтобы выполнить большую часть этой работы за вас.Я никогда не использовал его, но JIT-компилятор дает вам некоторые базовые строительные блоки (такие как «сложение», «умножение» и т. Д. «Инструкции»), которые вы можете связывать вместе по мере необходимости, и он компилирует их в фактический код сборки (поэтомуне нужно проходить через построенное синтаксическое дерево, вызывающее указатели на функции, как это было раньше), поэтому, когда вы вызываете его, он настолько быстр и динамичен, насколько это возможно.
Я не знаю API libjit, но он выглядит легко способнымделать то, что вам нужно.make_func
и free_func
могут быть почти такими же, как они указаны выше (вам, возможно, придется изменить свои вызовы на call_func
), и в основном будут создавать, использовать и уничтожать объект JIT на основе того, как он анализируетстрока пользователя.На самом деле то же самое, что и выше, но вам не нужно определять синтаксическое дерево, типы данных и т. Д. Самостоятельно.
Надеюсь, это несколько полезно.