Calling C/C++ from Python

Calling C/C++ from Python?

You should have a look at Boost.Python. Here is the short introduction taken from their website:

The Boost Python Library is a framework for interfacing Python and
C++. It allows you to quickly and seamlessly expose C++ classes
functions and objects to Python, and vice-versa, using no special
tools -- just your C++ compiler. It is designed to wrap C++ interfaces
non-intrusively, so that you should not have to change the C++ code at
all in order to wrap it, making Boost.Python ideal for exposing
3rd-party libraries to Python. The library's use of advanced
metaprogramming techniques simplifies its syntax for users, so that
wrapping code takes on the look of a kind of declarative interface
definition language (IDL).

call/cc in Python — Possible?

The problem is that the standard interpreter — CPython — is a stackful interpreter, i.e. every invocation of Python function results in recursive call inside the interpreter. So the Python FrameType objects are just views (.f_back is a read-only attribute for the good reason) of the C stack frames, there is no point to change the f_back pointer.

If you really want to manipulate the stack, you will have to write a C module, like the greenlet module does.

Goog luck!

How does calling C or C++ from python work?

You can call between C, C++, Python, and a bunch of other languages without spawning a separate process or copying much of anything.

In Python basically everything is reference-counted, so if you want to use a Python object in C++ you can simply use the same reference count to manage its lifetime (e.g. to avoid copying it even if Python decides it doesn't need the object anymore). If you want the reverse, you may need to use a C++ std::shared_ptr or similar to hold your objects in C++, so that Python can also reference them.

In some cases things are even simpler than this, such as if you have a pure function in C or C++ which takes some values from Python and returns a result with no side effects and no storing of the inputs. In such a case, you certainly do not need to copy anything, because you can read the Python values directly and the Python interpreter will not be running while your C or C++ code is running (because they are all in a single thread).

There is an extensive Python (and NumPy, by the way) C API for this, plus the excellent Boost.Python for C++ integration including smart pointers.

Calling c++ function from python

The problem is that strings are passed as pointers to wchar_t wide characters in Python 3. And in little-endian system your string can be coded in binary as

"h\0\0\0e\0\0\0l\0\0\0l\0\0\0o\0\0\0\0\0\0\0"

Which, when printed with %s will stop at the first null terminator.


For UTF-8-encoded byte strings (char *) you need a bytes object. For example:

lib.Foo_bar("hello".encode())

or use bytes literals:

lib.Foo_bar(b"hello")

Even better if you had specified the correct argument types:

from ctypes import cdll, c_char_p
foo_bar = cdll.LoadLibrary("./libfoo.so").Foo_bar
foo_bar.argtypes = [c_char_p]
foo_bar(b"hello\n")
foo_bar("hello\n")

when run will output the following:

hello
Traceback (most recent call last):
File "foo.py", line 5, in <module>
foo_bar("hello\n")
ctypes.ArgumentError: argument 1: <class 'TypeError'>: wrong type

i.e. the latter call that uses a string instead of bytes would throw.

Calling a python method from C/C++, and extracting its return value

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.

Calling a C/C++ code from Python with an argument

Let's say we want to write a Python script that acts as a wrapper on top of a C binary and passes arguments from the terminal to the C binary. first, create a test.c C program as follows:

#include <stdio.h>

int main(int argc, char *argv[])
{
if(argc > 1)
{
int i;
printf("C binary: ");
for(i = 0; i < argc; i++)
printf("%s ", argv[i]);
printf("\n");
}
else
printf("%s: No argument is provided!\n", argv[0]);
return(0);
}

then compile it using:

gcc -o test test.c

and run it for two dummy arguments using:

./test arg1 arg2

Now, going back to your question. How I could pass arguments from Python to a C binary. First you need a Python script to read the arguments from the terminal. The test.py would do that for you:

import os
import sys

argc = len(sys.argv)
argv = sys.argv

if argc > 2:
cmd = './'
for i in range(1,argc):
cmd = cmd + argv[i] + ' '
if os.path.isfile(argv[1]):
print('Python script: ', cmd)
os.system(cmd)
else:
print('Binary file does not exist')
bin = 'gcc -o ' + argv[1] + ' '+ argv[1] + '.c'
print(bin)
os.system(bin)
if os.path.isfile(argv[1]):
os.system(cmd)
else:
print('Binary source does not exist')
exit(0)
else:
print('USAGE: python3.4', argv[0], " BINARY_FILE INPUT_ARGS");
exit(0)

Having test.c and test.py in the same directory. Now, you can pass arguments from the terminal to the test C binary using:

python3.4 test.py test arg1 arg2

Finally, the output would be:

Python script:  ./test arg1 arg2
C binary: ./test arg1 arg2

Two last remarks:

  • Even if you don't compile the source code, the test.py will look for the test.c source file and try to compile it.
  • If you don't want to pass arguments from the terminal, you can always define the arguments in the Python script and pass them to the C binary.


Related Topics



Leave a reply



Submit