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.txtto 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

8 thoughts on “Calling C++ Classes from Python, with ctypes…

    1. 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.

  1. 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?

    1. 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)

  2. 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

    1. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.