90

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?

Sam Mason
16.5k1 gold badge49 silver badges71 bronze badges
asked Jul 20, 2010 at 1:54
3
  • 2
    Have 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? Commented Jul 20, 2010 at 2:04
  • 1
    The 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. Commented Jul 20, 2010 at 2:16
  • 10
    Can we just see a answer with a code example and be done with it? Commented Jul 21, 2010 at 0:16

10 Answers 10

105

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.

answered Jul 22, 2010 at 15:33
Sign up to request clarification or add additional context in comments.

6 Comments

I 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.
If you're calling an object's class method, do you need to pass that object in the args?
Although 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 a float object each time it is executed. The function can be called without manual conversion and tuple creation, with result = PyObject_CallFunction(myFunction, "d", 2.0). The import can be written as module = PyImport_ImportModule("mytest") (without creating a Python string).
Does this work for calling a function that is defined within a class?
If you're using Python >3.5, PyString_FromString() is PyUnicode_FromString().
|
34

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.

answered Jul 10, 2014 at 22:21

6 Comments

Calling a.out would be better as ./a.out because your call to a.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.
See 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.
this gives me a segfault on python 2.7 :(
Compilation line (for c++) : g++ call_function.cpp python2.7-config --cflags python2.7-config --ldflags -o call_function
In python3 replace PyString_FromString to PyUnicode_FromString and Py_Finalize to Py_FinalizeEx.
|
10

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;
}
sashoalm
80.6k139 gold badges486 silver badges830 bronze badges
answered Jul 20, 2010 at 3:18

1 Comment

Please see my followup question here stackoverflow.com/questions/50441365/…
6

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
answered Aug 3, 2015 at 21:30

Comments

3

As others have already mentioned, this is answered at the Python docs. However, since I come from Python and don't have that much experience using C/C++, I had some issues running it with Python 3. So here is my full working example to get the Python docs running after spending some time on other posts of stackoverflow:

File c_function.c

#define PY_SSIZE_T_CLEAN
#include <Python.h>
int main(int argc, char *argv[])
{
 PyObject *pName, *pModule, *pFunc;
 PyObject *pArgs, *pValue;
 int i;
 if (argc < 3) {
 fprintf(stderr,"Usage: call pythonfile funcname [args]\n");
 return 1;
 }
 Py_Initialize();
 // I had to add the following two lines to make it work
 PyRun_SimpleString("import sys");
 PyRun_SimpleString("sys.path.append(\".\")");
 pName = PyUnicode_DecodeFSDefault(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 = PyLong_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", PyLong_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;
 }
 if (Py_FinalizeEx() < 0) {
 return 120;
 }
 return 0;
}

File multiply.py

def multiply(a,b):
 print("Will compute", a, "times", b)
 c = 0
 for i in range(0, a):
 c = c + b
 return c

We need to compile and link it. This can be done with the command:

gcc c_function.c -c $(python3.6-config --cflags) -fPIC

followed by

gcc c_function.o $(python3.6-config --ldflags) -o call

for the example of Python 3.6. Afterwards, the example from the Python docs can just be executed by

./call multiply multiply 3 2
answered Jan 20, 2022 at 16:50

2 Comments

This is huge!!! Thank you! // I had to add the following two lines to make it work PyRun_SimpleString("import sys"); PyRun_SimpleString("sys.path.append(\".\")");
Thank you! did you get why you need to add the import sys part?
2

Here's a minimal executable version that also works with Python 3 (tested with Python 2.7 and 3.9).

Links into the docs are included in the comments, but all are accessible under https://docs.python.org/3/c-api/

#include <Python.h>
#include <stdio.h>
int main()
{
 // Initialize the Python Interpreter
 Py_Initialize();
 // see https://docs.python.org/3/c-api/structures.html
 // NULL objects are special and Py_CLEAR knows this
 PyObject *module = NULL, *result = NULL;
 // https://docs.python.org/3/c-api/import.html
 module = PyImport_ImportModule("mytest");
 if (!module) {
 // Python generally uses exceptions to indicate an error state which
 // gets flagged in the C-API (a NULL pointer in this case) indicating
 // "something" failed. the PyErr_* API should be used to get more
 // details
 goto done;
 }
 // see https://docs.python.org/3/c-api/call.html#c.PyObject_CallMethod
 // and https://docs.python.org/3/c-api/arg.html#building-values
 result = PyObject_CallMethod(module, "myabs", "f", 3.14);
 if (!result) {
 goto done;
 }
 // make sure we got our number back
 if (PyFloat_Check(result)) {
 printf("Successfully got a float: %f\n", PyFloat_AsDouble(result));
 } else {
 printf("Successfully got something unexpected!\n");
 }
 done:
 // see https://docs.python.org/3/c-api/exceptions.html
 PyErr_Print();
 // see https://docs.python.org/3/c-api/refcounting.html
 Py_CLEAR(result);
 Py_CLEAR(module);
 // Optionally release Python Interpreter
 Py_Finalize();
 return 0;
}

This uses the OP's Python code mytest.py, or this one-line equivalent:

from math import fabs as myabs

Building is going to be OS/Python version specific, but the following works for me:

cc -o test -I/usr/include/python3.9 /usr/lib/libpython3.9.so test.c
answered Nov 3, 2021 at 0:12

1 Comment

As the community is moving to python3, this should be the best answer to-date.
1

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.

answered Jul 20, 2010 at 2:24

Comments

1

If you assign the return value to a variable, then you can use something like PyEval_GetGlobals() and PyDict_GetItemString() to get the PyObject. From there, PyNumber_Float can get you the value you want.

I suggest browsing through the whole API - certain things become obvious when you see the different methods that are available to you, and there may well be a better method than the one I've described.

answered Jul 20, 2010 at 10:07

Comments

1

I have done it using BOOST to embedded Python to C++ [This working C module should help]

#include <boost/python.hpp>
void main()
{
using namespace boost::python;
 Py_Initialize();
 PyObject* filename = PyString_FromString((char*)"memory_leak_test");
 PyObject* imp = PyImport_Import(filename);
 PyObject* func = PyObject_GetAttrString(imp,(char*)"begin");
 PyObject* args = PyTuple_Pack(1,PyString_FromString("CacheSetup"));
 PyObject* retured_value = PyObject_CallObject(func, args); // if you have arg
 double retured_value = PyFloat_AsDouble(myResult);
 std::cout << result << std::endl;
 Py_Finalize();
}
answered Jun 26, 2014 at 20:54

1 Comment

Where is the boost part in this script ?
0

Here is a simple and direct answer to your question:

 #include <iostream>
 #include <Python.h>
 using namespace std;
 int main()
 {
 const char *scriptDirectoryName = "/yourDir";
 Py_Initialize();
 PyObject *sysPath = PySys_GetObject("path");
 PyObject *path = PyString_FromString(scriptDirectoryName);
 int result = PyList_Insert(sysPath, 0, path);
 PyObject *pModule = PyImport_ImportModule("mytest");
 PyObject* myFunction = PyObject_GetAttrString(pModule,(char*)"myabs");
 PyObject* args = PyTuple_Pack(1,PyFloat_FromDouble(-2.0));
 PyObject* myResult = PyObject_CallObject(myFunction, args);
 double getResult = PyFloat_AsDouble(myResult);
 return 0;
 }
answered Feb 28, 2020 at 7:44

1 Comment

I receive the following error: ‘PyString_FromString’ was not declared in this scope PyObject *path = PyString_FromString(scriptDirectoryName);

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.