pyC++

Calling C++ Functions from Python

1. C++ Classes

Classes are on of the basic constructs of C++ and are used widely in object oriented programming. More and more libraries are written in C++ using this technique to hide complexity and to keep programs readable. All this is very efficient as long as you will not try to call class function from C. Of course this is daffy, because you can easily write the entire Program in C++ and will avoid a lot of trouble.

If you started working with python it is different - OK you can use wrappers like "swig" or "boost", which will handle almost everything for you. All this will become complicated if you like to use callbacks, which are very simple to handle with ctypes[2]. Ctypes can be used to interface any C library directly from python and is the native way, if you do not want to integrate the interface in the C library by including "python.h" etc. Especially if you use a very strict model like Design Patterns, you will like to keep it simple and use ctypes module. But in that case you can not use C++ or you have to build an interface.

I have read this to often on the Internet and so decided to solve this problem for a simple C++ Class (perhaps I will write a module for this in the future). So lets start with a very simple C++ library, which can be easily enhanced:

//Header test.h class test{ int id; //store id public: test(); //constuctor ~test(); //destructor int getId(); //return value of id void setId(int i); //set value of id }; //To avoid loading libc++ test* newtest(); //hide allocation by new test* deltest(test* t); //hide free by delete

And the source file is:

//Code test.cpp #include "test.h" test::test(){ id=1; } test::~test(){ id=0; } int test::getId(){ return id; } void test::setId(int i){ id=i; } test* newtest(){ return new test(); } test* deltest(test* t){ delete t; return 0; }

To compile this library use g++:

g++ -shared -fPIC test.cpp -o test.so

or on MacOSX (to get an .so and not a .dynlib):

g++ -fPIC -bundle -flat_namespace -undefined suppress test.cpp -o test.so

I also created the object file by using the -c switch, to be able to link the C program statically

2. C++ Name Mangling

Name mangling[1] is a technique to resolve unique names from function names, which are absolute and not dependent on the C++ object they belong to. How this is done depends on the compiler that is used but is quite simple, if you understood the basic idea. Here I will only discuss the output of GCC 3+ and how it is done on a Unix system. For further information check the Wiki entry.

To Inspect a library you can use nm to list symbols from object files:

nm test.so

And the output will be something like this (taken from MacOSX):

0000000000000dc6 s stub helpers 0000000000000d52 T __Z7deltestP4test 0000000000000d86 T __Z7newtestv 0000000000000d2c T __ZN4test5getIdEv 0000000000000d3c T __ZN4test5setIdEi 0000000000000cf0 T __ZN4testC1Ev 0000000000000cdc T __ZN4testC2Ev 0000000000000d18 T __ZN4testD1Ev 0000000000000d04 T __ZN4testD2Ev U __ZdlPv U __Znwm U ___gxx_personality_v0 0000000000000000 t __mh_bundle_header U dyld_stub_binder

You will find the function of the class in the list plus some extra stuff that is available in the library which we will ignore right now. For the function deltest() in the second line it is __Z7deltestP4test. The integers encode the number of characters of the function or object name, here 7. _Z is the beginning of each mangled name, N tells that it is a series of objects, E or P as the end of the name followed by an encoding for the arguments.

From this it is possible to resolve all the used functions of our library. It is import to know that C as its own name mangling by adding an underscore in front of the name, which has to be removed from the mangled names. We will ignore the original constructors here to keep it simple and only use our functions to create a class object:

test* deltest(test *) _Z7deltestP4test test* newtest() _Z7newtestv int test::getId() _ZN4test5getIdEv void test::setId(int i) _ZN4test5setIdEi

3. Calling from C

From C it is simple to use this functions by including them with extern. The first argument is the address or a pointer to the class object[3].

//main.c //extern function extern void *_Z7deltestP4test(void *); extern void *_Z7newtestv(); extern int _ZN4test5getIdEv(void *); extern void _ZN4test5setIdEi(void *,int); #include <stdio.h> int main(){ void *p; //create object p=_Z7newtestv(); //setId to 23 _ZN4test5setIdEi(p,23); //should print out 23 fprintf(stderr,"p = %i \n",_ZN4test5getIdEv(p)); //delete object p=_Z7deltestP4test(p); return 0; }

You can keep this a little bit more comfortable by defining a structure (expect segmentation faults and other problems, if the order of arguments or the alignment is wrong):

//main.c //structure typedef struct{ int id; }test; //extern function extern test *_Z7deltestP4test(test *); extern test *_Z7newtestv(); extern int _ZN4test5getIdEv(test *); extern void _ZN4test5setIdEi(test *,int); #include <stdio.h> int main(){ test *p; //create object p=_Z7newtestv(); //setId to 23 _ZN4test5setIdEi(p,23); //should print out 23 fprintf(stderr,"p.id = %i \n",_ZN4test5getIdEv(p)); //delete object p=_Z7deltestP4test(p); return 0; }

To compile this, you need to link the libstdc++:

gcc main.c test.o -lstdc++-static

4. Calling from Python

For python I will do almost the same, just using the ctypes module:

from ctypes import * test=cdll.LoadLibrary("test.so") #setting return and argument types of the used functions test._Z7newtestv.restype=c_void_p test._Z7deltestP4test.restype=c_void_p test._Z7deltestP4test.argtypes=[c_void_p] test._ZN4test5setIdEi.argtypes=[c_void_p,c_int] test._ZN4test5getIdEv.argtypes=[c_void_p] #same example as the c program p=test._Z7newtestv() test._ZN4test5setIdEi(p,c_int(23)) print "p.id = %i"%(test._ZN4test5getIdEv(p)) p=test._Z7deltestP4test(p)

5. Assembly

Missing: I will present a way to use the new and the delete statements by loading the C++-Library.

6. Conclusion

All in all this is a not very safe way to work with C++ libraries, but the focus was on how to interact with those libraries and perhaps to have a basis for an equivalent to ctypes for C++. It is important to be very careful if you like to try something like this because it can cause a lot of problems and is experimental work.

7. Links

[1]Name Mangling http://en.wikipedia.org/wiki/Name_mangling
[2]Ctypes http://docs.python.org/library/ctypes.html
[3]Calling Conventions http://en.wikipedia.org/wiki/X86_calling_conventions