英文:
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;
};
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论