3

I have the following shortened program that embeds Python (it's stripped of all error handling, etc for shortness). It creates Python function as a string (which is required for my problem at hand) and then tries to call it and get whatever it returned using __builtin__.eval.

#include <Python.h>
#include <iostream>

int main() {
  // Initialize Python
  Py_Initialize();

  // Import builtin to get an access to eval
  PyObject *builtins = PyImport_ImportModule("__builtin__");
  PyObject *eval = PyObject_GetAttrString(builtins, "eval");

  // Create an argument tuple for the eval
  PyObject * args = PyTuple_New(1);
  PyObject * name = PyString_FromString("f(10)");
  PyTuple_SetItem(args, 0, name);

  // Create a function
  PyRun_SimpleString("f = lambda x: x * 2 - 3");

  // Try to get the result of the function execution by calling eval on it
  PyObject * result = PyObject_CallObject(eval, args);

  // Fail if the result is null pointer
  assert(result != NULL);

  // Destroy Python frame
  Py_Finalize();
}

In other words, this program is analogous to the following Python program:

f = lambda x: x * 2 - 3
import __builtin__
print __builtin__.eval("f(10)")

The problem is: it works flawlessly in Python (returning 17). But in C++, PyObject_CallObject returns NULL pointer and the program fails at assert, even though eval should clearly have returned the result of f(10). My question is why?

Thank you for your answer!

P.S. If you want to try to compile that yourself, use the following command on Unix systems (I am using it on Ubuntu):

g++ -I/usr/include/python2.7 progname.cpp -lpython2.7 -g
Dmitry Torba
  • 3,004
  • 1
  • 14
  • 24
  • Just to help your debugging, recall that a NULL return typically means that a Python exception was raised. You can call PyErr_Print() or similar to show which exception occurred. – Ove Jan 27 '18 at 08:05

1 Answers1

1

I have actually figured out how to do that myself. There is no need to use eval() in order to extract the result of the execution of the function. Instead, you can do the following:

#include <Python.h>
#include <iostream>

int main() {
  // Initialize Python
  Py_Initialize();

  // Create a function
  PyRun_SimpleString("f = lambda x: x * 2 - 3");

  // Import __main__ where the f is stored
  PyObject *main = PyImport_ImportModule("__main__");
  PyObject *function = PyObject_GetAttrString(main, "__function");

  // Create an argument tuple for the function
  PyObject * args = PyTuple_New(1);
  PyObject * first_argument = PyInt_FromLong(10);
  PyTuple_SetItem(args, 0, first_argument);

  // Call the function and get its result
  PyObject * result = PyObject_CallObject(function, args);

  // Output the result
  cout << PyInt_AsLong(result) << endl;

  // Destroy Python frame
  Py_Finalize();
}

However, I am still curious why eval did not work as shown in my original question

Dmitry Torba
  • 3,004
  • 1
  • 14
  • 24