Calling C++ Classes from Python, with ctypes…
I recently found myself needing to call a C++ class from within Python. I didn’t want to call a separate process (as I had previously when using Thrift – see Using Apache Thrift for Python & C++); but rather to call a C++ library directly.
Before I go on I should say that there are a lot of different ways to do this is Python – and I’ve picked one that worked for me. Other techniques are available – and opinion seems quite divided as to which (if any) technique is ‘best’.
To start with we have our C++ class, written in the usual way.
#include <iostream> // A simple class with a constuctor and some methods... class Foo { public: Foo(int); void bar(); int foobar(int); private: int val; }; Foo::Foo(int n) { val = n; } void Foo::bar() { std::cout << "Value is " << val << std::endl; } int Foo::foobar(int n) { return val + n; }
Next we need to place a C wrapper around the C++ code – since the ctypes
system cannot use C++ code… To do this we add the following to the bottom of the file.
// Define C functions for the C++ class - as ctypes can only talk to C... extern "C" { Foo* Foo_new(int n) {return new Foo(n);} void Foo_bar(Foo* foo) {foo->bar();} int Foo_foobar(Foo* foo, int n) {return foo->foobar(n);} }
Note that we need to provide a non-class-based name for each Method we want to call.
We now need to build a lib.so
file from our code.
We could do this by hand:
$ g++ -c -fPIC foo.cpp -o foo.o $ g++ -shared -W1,-soname,libfoo.so -o libfoo.so foo.o
Or we could use CMake.
The following is the CMakeLists.txt
to build foo.cpp
.
cmake_minimum_required(VERSION 2.8.9) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -std=c++11 -Wall") set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}") set(CMAKE_MACOSX_RPATH 1) project (foo) set (SOURCE foo.cpp) add_library(foo MODULE ${SOURCE})
Note that I was building on my Mac – hence the MacOS specific line 4. This will work okay on Linux; but it’s not needed.
Now that we’ve written and compiled our C++ – we now need to build a Python wrapper for the Class…
import ctypes lib = ctypes.cdll.LoadLibrary('./libfoo.so') class Foo(object): def __init__(self, val): lib.Foo_new.argtypes = [ctypes.c_int] lib.Foo_new.restype = ctypes.c_void_p lib.Foo_bar.argtypes = [ctypes.c_void_p] lib.Foo_bar.restype = ctypes.c_void_p lib.Foo_foobar.argtypes = [ctypes.c_void_p, ctypes.c_int] lib.Foo_foobar.restype = ctypes.c_int self.obj = lib.Foo_new(val) def bar(self): lib.Foo_bar(self.obj) def foobar(self, val): return lib.Foo_foobar(self.obj, val)
Note the requirement to define the argument types, and the type of the return value (even if there isn’t one – e.g. you’re returning void
). Without this you’ll get a segmentation fault (etc.).
Now that we’ve done everything we need to build the module, we can simply import
it from within Python.
For example:
from foo import Foo # We'll create a Foo object with a value of 5... f=Foo(5) # Calling f.bar() will print a message including the value... f.bar() # Now we'll use foobar to add a value to that stored in our Foo object, f print (f.foobar(7)) # Now we'll do the same thing - but this time demonstrate that it's a normal # Python integer... x = f.foobar(2) print (type(x))
The full source code for this simple demo can be found here:
https://github.com/Auctoris/ctypes_demo
20 thoughts on “Calling C++ Classes from Python, with ctypes…”
Leave a Reply Cancel reply
This site uses Akismet to reduce spam. Learn how your comment data is processed.
What did you mean by “or we could use CMake”, where does CMake store the .so file?
CMake is a simple way to manage building complex code on Linux / Mac systems. Have a look here (http://derekmolloy.ie/hello-world-introductions-to-cmake/) for a good tutorial.
Essentially you create the CMakeLists.txt file as described above, then run cmake to create a standard UNIX Makefile. Then you just run make to do the building. In this case the libfoo.so file will be build in the current working directory.
I hope that helps – and sorry for the delay in replying.
Hi
Why int foobar(int); need to set
lib.Foo_foobar.argtypes = [ctypes.c_void_p, ctypes.c_int]
what is void type mean?
thx.
The c_void_p argument is the Foo* pointer to the object needed by the C wrapper function. In C, void* means ‘pointer to something’ where something is not specified. In python you the Foo.foobar() function calls lib.foobar(foo_obj,n) where foo_obj is the object and n is where the int argument goes.
Thanks for this description, it worked well until I changed the argument und return value from int to double. Now Python gives me a ‘None’ back. I defined the argument types as ctypes.c_double. Any idea whats wrong? Am I missing something?
Not sure I’m afraid… That should work.
Here’s one that runs for me.
foo.cpp
#include <iostream>
#include <cstring>
#include <cstdlib>
// A simple class with a constuctor and some methods...
class Foo
{
public:
Foo(double);
const char* bar();
double foobar(double);
private:
double val;
};
Foo::Foo(double n)
{
val = n;
}
const char* Foo::bar()
{
int i;
std::string s;
std::string msg;
s = std::to_string(val);
msg = "The value is " + s;
i = msg.length();
const char* rv = msg.c_str();
return rv;
}
double Foo::foobar(double n)
{
return val + n;
}
// Define C functions for the C++ class - as ctypes can only talk to C...
extern "C"
{
Foo* Foo_new(double n) {return new Foo(n);}
const char* Foo_bar(Foo* foo) {return foo->bar();}
double Foo_foobar(Foo* foo, double n) {return foo->foobar(n);}
}
foo.py
"""foo.py – a simple demo of importing a calss from C++"""
import ctypes
lib = ctypes.cdll.LoadLibrary('./libfoo.so')
class Foo(object):
"""The Foo class supports two methods, bar, and foobar..."""
def __init__(self, val):
lib.Foo_new.argtypes = [ctypes.c_double]
lib.Foo_new.restype = ctypes.c_void_p
lib.Foo_bar.argtypes = [ctypes.c_void_p]
lib.Foo_bar.restype = ctypes.c_char_p
lib.Foo_foobar.argtypes = [ctypes.c_void_p, ctypes.c_double]
lib.Foo_foobar.restype = ctypes.c_double
self.obj = lib.Foo_new(val)
def bar(self):
"""bar returns a string continaing the value"""
return (lib.Foo_bar(self.obj)).decode()
def foobar(self, val):
"""foobar takes an integer, and adds it to the value in the Foo class
- returning the result"""
return lib.Foo_foobar(self.obj, val)
Hi AJP, thank you very much for posting this, its very interesting and helpful!!
Could you please help me build the python wrapper for a specific case? I am using Windows and I already have the compiled “LA_CTYPE.dll” and only the “LA_CTYPE.h” file that gives me clues of what happens in the original “.cpp”.
The “LA_CTYPE.h” has the structure as follows:
typedef struct LA LA;
extern “C” __declspec(dllexport) LA* CreateLala( );
extern “C” __declspec(dllexport) uint16 DestroyLala( LA* LVREF );
extern “C” __declspec(dllexport) int Init( LA* LVREF, bool playBackEna = false, int cameraNum = -1);
extern “C” __declspec(dllexport) void setDepthRange( LA* LVREF, uint16 value, uint16 level=0 );
extern “C” __declspec(dllexport) void setPulseCount( LA* LVREF, uint16 value );
extern “C” __declspec(dllexport) uint16 getPulseCount( LA* LVREF );
.
.
.
How can I write the python program to wrap this? The structure seems very different from your example, so I am confused.
Thanks once again
Hello, thanks for sharing!
One question: Does it work in Windows?
It *should* – but the build process will likely be a bit different. I’m afraid that I don’t use Windows though, so I can’t help much more than this.
Hello, thanks very much for this example, I found it easy to follow, and it got me up and running pretty quickly wrapping a DLL on Windows (the wall-o’-text ctypes docs, while thorough, are a bit dense for a noob trying to understand the steps of the process at a high level)
Great; I’m glad it was helpful!
How the newly created Foo objects gets deleted?
I guess you would need to create Foo_delete() function.
void Foo_delete(Foo* p) { delete(p); }
Then perhaps using the the __del__ method or just use the “with” statement by providing
def __enter__(self):
def __exit__(self):
methods to the python version of Foo.
I’m on Windows. Any idea on how to make this work if I just have the DLL file and not the source code? As I’m using an API? Thanks.
I don’t use Windows so I’m not certain how to do it; but it should be the same (after the steps about building the library code) – as long as you know the correct names for the function entry points.
Ok, thank you!
Hi , How can I implement the array parameters
if c++ export function as
extern “C”
{
void Foo_getIntArr(Foo* foo,int a, int* intArray) {for(int i=0;i<a;i++){intArray[i]= i*10;} }
}
then in def __init__(self, val): section
lib.Foo_getIntArr.argtypes = [ctypes.c_void_p, POINTER(ctypes.c_int) ]
lib.Foo_getIntArr.restype = none
and how do I do for following def code ?
def getIntArr(self, a, ???? ):
return lib.Foo_getIntArr(self.obj, a, ????)
and how do I call this in python test demo ?
Thank you.
My g++ made error like this:
g++: error: unrecognized command line option ‘-W1,-soname,libfoo.so’
What exactly is that option -W1 and -soname, and how can I solve this error?
Try -Wl,-soname=libfoo.so
-Wl (that is a lowercase L) passes an option to the linker
-soname=libfoo.so sets the shared object name to be libfoo.so
https://stackoverflow.com/questions/13547750/g-error-unrecognized-option-soname
https://en.wikipedia.org/wiki/Soname