User Tools

Site Tools


info:cython

Compared to Other Methods of C/C++ Binding

Cython is good for more than bindings, but that is one thing it can do.

Advantages

  • Supports C++ classes
  • Everything is written in Python syntax (e.g. in SWIG you're more dependent on writing C/C++ code for massaging data)
  • Error handling (converting C/C++ error codes/exceptions to Python exceptions) seems easier and more flexible than SWIG (e.g. I'm still not sure how to throw an arbitrary Python exception, just the standard ones it provides)

Disadvantages

  • More manual work to declare classes/structs/functions/etc
    • You only have to declare what you need to use
    • Much can be copied and pasted
  • To use them from pure Python, C++ classes require creating a wrapper class that allocates and frees the memory, and wraps every method
    • The wrapper methods are where you would do error handling, translating errors into Python exceptions
  • Some odd (fixable) limitations
    • Structs are automatically converted into dictionaries when going to Python code, but some types (e.g. unsigned char arrays) cause this to break
      • Note that with SWIG or Ctypes you'd get a structure object into Python that acts like a class
      • It is possible (but more work) to memcpy the struct data (even without declaring any of its members in Cython) into a Ctypes Structure (see example on this page)
  • There's no easy way to get from Python data back into a struct; you have to manually list each member
    • You can't dynamically set the attributes (because it's generating C code, and setattr requires a Python type)
    • Probably I would use code generation if I needed to do this
  • In SWIG, structs are converted into Python classes (which manage their own memory)
  • Simple datatypes are automatically converted to Python, but there's no way to extend this functionality
    • SWIG has typemaps (written in C/C++)

Notes/Tricks

  • Ampersand works for getting a pointer (e.g. if you declared a variable on the stack); I didn't see this in the documentation

Ctypes Structures

I thought I would try using Ctypes Structures, which can be passed back to Python. I had a hard time getting the copy to work; the trick was to get the address of the Ctypes buffer into a Cython int.

I also tried to get the data into a Python string (bytes), but I wasn't able to figure out how to get it all in there (I believe Cython was trying to convert it as a string, i.e. a null-terminated C-String, rather than pure data).

When I tried this, I generated the Ctypes Structure definition automatically from the header using ctypeslib (python-ctypeslib package in Debian).

import ctypes
from libc.string cimport memcpy
 
from mystruct_ctypes import MyStruct as MyStruct_ctypes
 
# I think it should work with no members declared, like this:
cdef extern from "mystruct.h":
    ctypedef struct MyStruct:
        pass
 
def get_ctypes_struct():
    cdef MyStruct s_cython
 
    # ... get some data into s_cython
 
    s_ctypes = MyStruct_ctypes()
    # The address has to be converted to a Cython type
    cdef int ctypes_buffer = ctypes.addressof(s_ctypes)
    memcpy(<void*>ctypes_buffer, &s_cython, sizeof(MyStruct))
    return s_ctypes

CMake Example

cmake_minimum_required(VERSION 2.8)
project(someproject)
 
find_package(PythonLibs)
include_directories(${PYTHON_INCLUDE_PATH})
 
# TODO: add --gdb option to cython if in Debug mode? Separate option?
set(FOO_PYX ${CMAKE_CURRENT_SOURCE_DIR}/foo.pyx)
set(FOO_CPP ${CMAKE_CURRENT_BINARY_DIR}/foo.cpp)
add_custom_command(OUTPUT ${FOO_CPP}
    COMMAND cython --cplus ${FOO_PYX} -o ${FOO_CPP}
    DEPENDS ${FOO_PYX})
set(PYLIB foo)
add_library(${PYLIB} SHARED ${FOO_CPP})
# Remove lib prefix, which Python doesn't use
set_target_properties(${PYLIB} PROPERTIES PREFIX "")
target_link_libraries(${PYLIB} ${PYTHON_LIBRARIES})
# ... add other libraries if needed
info/cython.txt · Last modified: 2011-12-15 17:12 by sam