I am trying to wrap a C++ library into a python API with Cython. The class I want to wrap has the following template:
template<typename Value>
class ClassToWrap
{
public:
typedef std::shared_ptr<std::vector<Value> > TypeToWrap;
ClassToWrap(TypeToWrap data)
{
}
}
I'm not confident with C++ standard library. How I can wrap the TypeToWrap
in Cython in a way that it can be inizialized in a simple way like an array or a multidimenstional array, for example with a for loop of assignments? Thanks for any suggestion.
let's assume you have a C++ header as follows:
// cpp_class.h
#include <memory>
#include <vector>
template<typename Value>
class ClassToWrap
{
public:
typedef std::shared_ptr<std::vector<Value> > TypeToWrap;
ClassToWrap(TypeToWrap data) : obj(std::move(data))
{
}
private:
TypeToWrap obj;
};
you would need to expose this class to cython, this is done by a cdef extern
from cython wrapping Cpp documentation.
# my_cy_class.pyx
# distutils: language = c++
from libcpp.memory cimport make_shared, shared_ptr
from libcpp.vector cimport vector
cdef extern from "cpp_class.h" nogil:
cdef cppclass ClassToWrap[T]:
ctypedef shared_ptr[vector[T]] TypeToWrap
ClassToWrap(TypeToWrap)
# define anything you intend to use
note that you only need to define the functions, not their implementations.
secondly, let's define a cython class to wrap it and expose it to python, since python is going to use it, it needs to know the type of T
, let's assume it is an int
:
from cython.operator cimport dereference as deref
from libcpp.utility cimport move
cdef class wrapper_class:
cdef ClassToWrap[int]* wrapped_obj # needs to be a defined type and heap allocated
def __cinit__(self, some_list):
cdef vector[int] v = some_list
cdef ClassToWrap[int].TypeToWrap ptr = make_shared[vector[int]](move(v))
self.wrapped_obj = new ClassToWrap[int](move(ptr))
# deref(self.wrapped_obj).foo()
def __dealloc__(self):
del self.wrapped_obj
you may be wondering why a pointer to an object is used ? the reason is because your object has no default zero arguments constructor, and cython requires a default zero arguments constructor to be able to stack allocate it, using a __cinit__
and a __dealloc__
guarantee no memory leaks
note that some_list
doesn't need to be a python list, it can easily be a numpy array, and knowing the type beforehand can help the compiler optimize the code for it, the following code can test it.
import pyximport
script_args = ["--cython-cplus"]
setup_args = {
"script_args": script_args,
"include_dirs": ['.'],
}
pyximport.install(setup_args=setup_args, language_level=3,)
import numpy as np
import my_cy_class
inputs = np.array([1,2,3,4,5])
a = my_cy_class.wrapper_class(inputs)