Существует два основных способа сделать это.
Первый - это просто позвонить eval
так же, как и в Python. Единственный трюк в том, что вам нужен дескриптор модуля builtins
, потому что вы не получите его бесплатно в C API. Есть несколько способов сделать это, но один очень простой способ, это просто импортировать:
/* or PyEval_GetBuiltins() if you know you're at the interpreter's top level */
PyObject *builtins = PyImport_ImportModule("builtins");
PyObject *eval = PyObject_GetAttrString(builtins, "eval");
PyObject *args = Py_BuildValue("(s)", expression_as_c_string);
PyObject *result = PyObject_Call(eval, args);
(Это непроверенный код, и это по крайней мере утечки ссылки, и не проверяет NULL возврата, если вы хотите обрабатывать исключения на стороне C ... Но этого должно быть достаточно, чтобы передумать.)
Одна хорошая вещь в том, что вы можете использовать ast.literal_eval
точно так же, как eval
(это означает, что вы получаете некоторые бесплатная проверка); просто измените "builtins"
на "ast"
и "eval"
на "literal_eval"
. Но реальная победа в том, что вы делаете именно то, что делает eval
в Python, что вы уже знаете, именно то, что вы хотели.
Альтернативой является использование API-интерфейсов компиляции. На действительно высоком уровне вы можете просто создать оператор Python из "foo = eval(%s)"
и PyRun_SimpleString
. Ниже, используйте Py_CompileString
для синтаксического анализа и компиляции выражения (вы также можете анализировать и компилировать отдельные шаги, но это не полезно здесь), затем PyEval_EvalCode
, чтобы оценить его в соответствующих глобальных и локальных. (Если вы не отслеживаете глобальные переменные самостоятельно, используйте interpreter-reflection APIsPyEval_GetLocals
и PyEval_GetGlobals
.) Обратите внимание, что я предоставляю супер-упрощенную версию каждой функции; часто вы хотите использовать одну из функций брата и сестры. Но вы можете легко найти их в документах.
Вы не хотите использовать 'ast.literal_eval' для этого вместо' eval'? (Это не влияет на ответ, но об этом стоит подумать.) – abarnert
@abarnert Строки, которые обрабатываются, все под моим контролем, поэтому я не думаю, что мне нужно это сделать - это всегда будет словарь, содержащий различные значения строковых значений float, int, bool или (правильно экранированные). –
Если это всегда словарь, содержащий float, int, bool и строки, почему _not_ использовать 'literal_eval'? Это немного проще, и вы получаете бесплатную проверку. – abarnert