Calling a python method from C/C++, and extracting its return value
Solution 1
As explained before, using PyRun_SimpleString seems to be a bad idea.
You should definitely use the methods provided by the C-API (http://docs.python.org/c-api/).
Reading the introduction is the first thing to do to understand the way it works.
First, you have to learn about PyObject that is the basic object for the C API. It can represent any kind of python basic types (string, float, int,...).
Many functions exist to convert for example python string to char* or PyFloat to double.
First, import your module :
PyObject* myModuleString = PyString_FromString((char*)"mytest");
PyObject* myModule = PyImport_Import(myModuleString);
Then getting a reference to your function :
PyObject* myFunction = PyObject_GetAttrString(myModule,(char*)"myabs");
PyObject* args = PyTuple_Pack(1,PyFloat_FromDouble(2.0));
Then getting your result :
PyObject* myResult = PyObject_CallObject(myFunction, args)
And getting back to a double :
double result = PyFloat_AsDouble(myResult);
You should obviously check the errors (cf. link given by Mark Tolonen).
If you have any question, don't hesitate. Good luck.
Solution 2
Here is a sample code I wrote (with the help of various online sources) to send a string to a Python code, then return a value.
Here is the C code call_function.c
:
#include <Python.h>
#include <stdlib.h>
int main()
{
// Set PYTHONPATH TO working directory
setenv("PYTHONPATH",".",1);
PyObject *pName, *pModule, *pDict, *pFunc, *pValue, *presult;
// Initialize the Python Interpreter
Py_Initialize();
// Build the name object
pName = PyString_FromString((char*)"arbName");
// Load the module object
pModule = PyImport_Import(pName);
// pDict is a borrowed reference
pDict = PyModule_GetDict(pModule);
// pFunc is also a borrowed reference
pFunc = PyDict_GetItemString(pDict, (char*)"someFunction");
if (PyCallable_Check(pFunc))
{
pValue=Py_BuildValue("(z)",(char*)"something");
PyErr_Print();
printf("Let's give this a shot!\n");
presult=PyObject_CallObject(pFunc,pValue);
PyErr_Print();
} else
{
PyErr_Print();
}
printf("Result is %d\n",PyInt_AsLong(presult));
Py_DECREF(pValue);
// Clean up
Py_DECREF(pModule);
Py_DECREF(pName);
// Finish the Python Interpreter
Py_Finalize();
return 0;
}
Here is the Python code, in file arbName.py
:
def someFunction(text):
print 'You passed this Python program '+text+' from C! Congratulations!'
return 12345
I use the command gcc call_function.c -I/usr/include/python2.6 -lpython2.6 ; ./a.out
to run this process. I'm on redhat. I recommend using PyErr_Print(); for error checking.
Solution 3
A complete example of calling a Python function and retrieving the result is located at http://docs.python.org/release/2.6.5/extending/embedding.html#pure-embedding:
#include <Python.h>
int
main(int argc, char *argv[])
{
PyObject *pName, *pModule, *pDict, *pFunc;
PyObject *pArgs, *pValue;
int i;
if (argc < 3) {
fprintf(stderr,"Usage: call pythonfile funcname [args]\n");
return 1;
}
Py_Initialize();
pName = PyString_FromString(argv[1]);
/* Error checking of pName left out */
pModule = PyImport_Import(pName);
Py_DECREF(pName);
if (pModule != NULL) {
pFunc = PyObject_GetAttrString(pModule, argv[2]);
/* pFunc is a new reference */
if (pFunc && PyCallable_Check(pFunc)) {
pArgs = PyTuple_New(argc - 3);
for (i = 0; i < argc - 3; ++i) {
pValue = PyInt_FromLong(atoi(argv[i + 3]));
if (!pValue) {
Py_DECREF(pArgs);
Py_DECREF(pModule);
fprintf(stderr, "Cannot convert argument\n");
return 1;
}
/* pValue reference stolen here: */
PyTuple_SetItem(pArgs, i, pValue);
}
pValue = PyObject_CallObject(pFunc, pArgs);
Py_DECREF(pArgs);
if (pValue != NULL) {
printf("Result of call: %ld\n", PyInt_AsLong(pValue));
Py_DECREF(pValue);
}
else {
Py_DECREF(pFunc);
Py_DECREF(pModule);
PyErr_Print();
fprintf(stderr,"Call failed\n");
return 1;
}
}
else {
if (PyErr_Occurred())
PyErr_Print();
fprintf(stderr, "Cannot find function \"%s\"\n", argv[2]);
}
Py_XDECREF(pFunc);
Py_DECREF(pModule);
}
else {
PyErr_Print();
fprintf(stderr, "Failed to load \"%s\"\n", argv[1]);
return 1;
}
Py_Finalize();
return 0;
}
Solution 4
To prevent the extra .py file as in the other answers, you can just retrieve the __main__
module, which is created by the first call to PyRun_SimpleString
:
PyObject *moduleMainString = PyString_FromString("__main__");
PyObject *moduleMain = PyImport_Import(moduleMainString);
PyRun_SimpleString(
"def mul(a, b): \n"\
" return a * b \n"\
);
PyObject *func = PyObject_GetAttrString(moduleMain, "mul");
PyObject *args = PyTuple_Pack(2, PyFloat_FromDouble(3.0), PyFloat_FromDouble(4.0));
PyObject *result = PyObject_CallObject(func, args);
printf("mul(3,4): %.2f\n", PyFloat_AsDouble(result)); // 12
Solution 5
You have to extract the python method somehow and run it with PyObject_CallObject()
. To do that, you can provide Python a way to set the function, as the Extending and Embedding Python Tutorial example does.
Related videos on Youtube
D R
Updated on January 21, 2022Comments
-
D R over 2 years
I'd like to call a custom function that is defined in a Python module from C. I have some preliminary code to do that, but it just prints the output to stdout.
mytest.py
import math def myabs(x): return math.fabs(x)
test.cpp
#include <Python.h> int main() { Py_Initialize(); PyRun_SimpleString("import sys; sys.path.append('.')"); PyRun_SimpleString("import mytest;"); PyRun_SimpleString("print mytest.myabs(2.0)"); Py_Finalize(); return 0; }
How can I extract the return value into a C
double
and use it in C?-
user1066101 almost 14 yearsHave you read this: docs.python.org/c-api? It seems to answer your question. docs.python.org/c-api/number.html#PyNumber_Float seems to be what you're looking for. What's wrong with it? What more do you need?
-
D R almost 14 yearsThe question really is how to access the returned object from
mytest.myabs(2.0)
. Once I have a pointer to it, I can easily convert it to a float using the PyNumber_Float function. -
Matt Joiner almost 14 yearsCan we just see a answer with a code example and be done with it?
-
-
D R almost 14 yearsI will accept your answer because of the useful tips, but it would have been more helpful to me if you could provide a complete working C module calling the above python function.
-
Palec almost 10 yearsCalling
a.out
would be better as./a.out
because your call toa.out
relies on working directory being in PATH, which is not a very common configuration. You should also consider providing-o
option to GCC to give the executable a better name. -
Palec almost 10 yearsSee How does editing work? on Meta SE. If you can get to the revision history but could not see the changes in markup, just switch the diff view to
side-by-side markdown
. There are buttons at the top of each revision in revision history that switch diff view. If you do not understand what the markup does, refer to editing help. If you’re editing a post, the editor has built-in help – see the orange question mark to the top right of the editor. -
jhegedus over 8 yearsthis gives me a segfault on python 2.7 :(
-
andrew over 8 yearsCompilation line (for c++) : g++ call_function.cpp
python2.7-config --cflags
python2.7-config --ldflags
-o call_function -
Translunar over 8 yearsIf you're calling an object's class method, do you need to pass that object in the args?
-
Antonello about 8 yearsWhere is the boost part in this script ?
-
user4815162342 over 7 yearsAlthough it is accepted, this answer is unfortunately not a good template for calling Python code (and I've seen it referenced elsewhere). The
PyTuple_Pack
line leaks afloat
object each time it is executed. The function can be called without manual conversion and tuple creation, withresult = PyObject_CallFunction(myFunction, "d", 2.0)
. The import can be written asmodule = PyImport_ImportModule("mytest")
(without creating a Python string). -
user13107 almost 6 yearsPlease see my followup question here stackoverflow.com/questions/50441365/…
-
messy212 almost 6 yearsI just had some troubles with this code, I didn't succeed to import my code, so all there is to do is add
PyRun_SimpleString("import sys; sys.path.append('.')");
and then use thePyObject
method. thanks for this code, it work now. -
nice_remark over 5 yearsDoes this work for calling a function that is defined within a class?
-
e-info128 over 5 yearsIn python3 replace
PyString_FromString
toPyUnicode_FromString
andPy_Finalize
toPy_FinalizeEx
. -
mauriii almost 5 yearsthe reason for why a "z" was used in
pValue=Py_BuildValue("(z)",(char*)"something");
is it represents a Python str/null object and a C++ char*. For other arg types look at docs.python.org/3.6/c-api/arg.html -
applemonkey496 over 3 yearsIf you're using Python >3.5,
PyString_FromString()
isPyUnicode_FromString()
. -
fisakhan over 3 yearsI receive the following error: ‘PyString_FromString’ was not declared in this scope PyObject *path = PyString_FromString(scriptDirectoryName);