Skip to content

Instantly share code, notes, and snippets.

@insertinterestingnamehere
Created October 19, 2015 22:28
Show Gist options
  • Save insertinterestingnamehere/df7894b414a94a4456c5 to your computer and use it in GitHub Desktop.
Save insertinterestingnamehere/df7894b414a94a4456c5 to your computer and use it in GitHub Desktop.
This shows how to wrap a C++ function in a capsule within Cython, how to call a function pointer wrapped in a capsule object, how to export that function as a part of the modules C API, and how to call that function from outside the module using ctypes.
cdef double cymul(double, double) nogil
# distutils: language = c++
from cpython.pycapsule cimport PyCapsule_New, PyCapsule_GetPointer
# Could just include from the cpp file, but that's less standard,
# so I'll use a header here.
cdef extern from "thing.hpp":
cdef double mul(double a, double b) nogil
# Wrap the function in a capsule
capsule_mul = PyCapsule_New(<void*>&mul, "mod.capsule_mul", NULL)
# Get the function pointer back out of the capsule
cdef double (*mul_ptr)(double, double) nogil
mul_ptr = <double (*)(double, double) nogil> PyCapsule_GetPointer(capsule_mul, "mod.capsule_mul")
# Wrap the function pointer inside another function that is exposed via a capsule to other Cython modules.
cdef double cymul(double a, double b) nogil:
return mul_ptr(a, b)
# Export a Python API built off of the Cython exported function.
def pymul(double a, double b):
return cymul(a, b)
rm -rf build mod.cpp mod.pyd mod.so
python setup.py build_ext --inplace
python test.py
from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize
# Call cythonize in advance so a single module can be compiled from a single Cython
# file along with other C++ files.
cythonize('*.pyx', language='c++')
setup(ext_modules=[Extension('mod', sources=['mod.cpp', 'thing.cpp'], language='c++')])
import ctypes as ct
ct.pythonapi.PyCapsule_GetPointer.restype = ct.c_void_p
ct.pythonapi.PyCapsule_GetPointer.argtypes = [ct.py_object, ct.c_char_p]
ptr_type = ct.CFUNCTYPE(ct.c_double, ct.c_double, ct.c_double)
import mod
# Get the function pointer from the capsule "capsule_mul"
handle = ptr_type(ct.pythonapi.PyCapsule_GetPointer(mod.capsule_mul, "mod.capsule_mul"))
# Get the function pointer from the Cython wrapper around the function pointer.
ct.pythonapi.PyCapsule_GetPointer.restype = ct.c_void_p
ct.pythonapi.PyCapsule_GetPointer.argtypes = [ct.py_object, ct.c_char_p]
ct.pythonapi.PyCapsule_GetName.restype = ct.c_char_p
ct.pythonapi.PyCapsule_GetName.argtypes = [ct.py_object]
other_handle = ptr_type(
ct.pythonapi.PyCapsule_GetPointer(
mod.__pyx_capi__['cymul'],
ct.pythonapi.PyCapsule_GetName(
mod.__pyx_capi__['cymul'])))
# Call all three exposed versions of the function.
print handle(ct.c_double(1), ct.c_double(2))
print other_handle(ct.c_double(1), ct.c_double(2))
print mod.pymul(1, 2)
double mul(double a, double b) { return a * b; }
double mul(double, double);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment