英文:
How to wrap std::shared_ptr and std::vector from C++ in Cython?
问题
我正在尝试使用Cython将一个C++库封装为Python API。我想要封装的类具有以下模板:
template<typename Value>
class ClassToWrap
{
public:
typedef std::shared_ptr<std::vector<Value>> TypeToWrap;
ClassToWrap(TypeToWrap data)
{
}
}
我对C++标准库不太自信。我该如何在Cython中封装TypeToWrap,以便可以像数组或多维数组一样以简单的方式进行初始化,例如通过分配的for循环?感谢任何建议。
英文:
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.
答案1
得分: 2
以下是您要翻译的代码部分:
// 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;
}
# 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
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
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)
英文:
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)
答案2
得分: 1
你应该将指针传递给Python并创建一个双向观察模式(通知双方),以便在对象被删除时进行通知!并且你可以重置 shared_ptr。例如:
class CythonWrapClass {
public:
CythonWrapClass(std::shared_ptr<Foo> sharedFoo) : foo(sharedFoo) {
// 在类的任何地方进行Cython通信
// 假设类中的Cython对象变量名为: cthon
// cthon 应该获取 Foo 的指针并与其一起工作,但在完成后应该通知到这个指针(确切地是这个实例)
// 在从Python销毁时得到通知,它将减少引用计数
}
// 当cthon完成与sharedFoo的工作时,cthon将调用这个函数
void onDestroyFromcthon() { foo.reset(); }
private:
std::shared_ptr<Foo> foo;
};
英文:
You should give the pointer to Python and create a bidirectional observing pattern (notify both sides) if an object is deleted! and you can reset the shared_ptr. i.e:
class CythonWrapClass {
public:
CythonWrapClass(std::shared_ptr<Foo> sharedFoo) : foo(sharedFoo) {
// cython communication in the class anywhere
// assume cython object variable in the class has name: cthon
// cthon should take the pointer of Foo and work with it but when it has
// finished it should notify to this pointer (exactly this instance)
// when destroying notified from python it will decrease refcount
}
// cthon will call this function when it finished its job with sharedFoo
void onDestroyFromcthon() { foo.reset(); }
private:
std::shared_ptr<Foo> foo;
};
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。


评论