fug*_*ede 7 c++ python inheritance cython shared-ptr
假设我有以下 C++ 继承的简单示例file.h:
class Base {};
class Derived : public Base {};
Run Code Online (Sandbox Code Playgroud)
然后,编译以下代码;也就是说,我可以分配std::shared_ptr<Derived>给std::shared_ptr<Base>:
Derived* foo = new Derived();
std::shared_ptr<Derived> shared_foo = std::make_shared<Derived>(*foo);
std::shared_ptr<Base> bar = shared_foo;
Run Code Online (Sandbox Code Playgroud)
假设我已将类型添加到 a 中decl.pxd:
cdef extern from "file.h":
cdef cppclass Base:
pass
cdef cppclass Derived(Base):
pass
Run Code Online (Sandbox Code Playgroud)
然后,我想做的是在 Cython 中模仿上面的 C++ 赋值file.pyx:
cimport decl
from libcpp.memory cimport make_shared, shared_ptr
def do_stuff():
cdef decl.Derived* foo = new decl.Derived()
cdef shared_ptr[decl.Derived] shared_foo = make_shared[decl.Derived](foo)
cdef shared_ptr[decl.Base] bar = shared_foo
Run Code Online (Sandbox Code Playgroud)
与 C++ 情况不同,现在失败并出现以下错误(使用 Cython 3.0a6):
cdef shared_ptr[decl.Base] bar = shared_foo
^
---------------------------------------------------------------
Cannot assign type 'shared_ptr[Derived]' to 'shared_ptr[Base]'
Run Code Online (Sandbox Code Playgroud)
我应该期待这种行为吗?有什么方法可以模仿 C++ 示例使用 Cython 所做的事情吗?
编辑:参见。下面对已接受答案的评论,相关功能已添加到 Cython 中,并且从版本 3.0a7 开始可用。
它应该适用于 Cython>=3.0,因为 @fuglede 使这个PR修复了下面描述的问题(对于 Cython<3.0 仍然存在)。
问题是,未命中的包装std::shared_ptr
template <class U> shared_ptr& operator= (const shared_ptr<U>& x) noexcept;
Run Code Online (Sandbox Code Playgroud)
的std::shared_ptr-类。
如果你像这样修补包装器:
cdef extern from "<memory>" namespace "std" nogil:
cdef cppclass shared_ptr[T]:
...
shared_ptr[T]& operator=[Y](const shared_ptr[Y]& ptr)
#shared_ptr[Y](shared_ptr[Y]&) isn't accepted
Run Code Online (Sandbox Code Playgroud)
你的代码将会编译。
您可能会问,为什么需要构造函数operator=而不是构造函数,因为:shared_ptr[Y]
...
cdef shared_ptr[decl.Base] bar = shared_foo
Run Code Online (Sandbox Code Playgroud)
看起来构造函数 ( template <class U> shared_ptr (const shared_ptr<U>& x) noexcept;) 不显式。但这是 Cython 对 C++ 的怪癖之一。上面的代码将被翻译为
std::shared_ptr<Base> __pyx_v_bar;
...
__pyx_v_bar = __pyx_v_shared_foo;
Run Code Online (Sandbox Code Playgroud)
并不是
std::shared_ptr<Base> __pyx_v_bar = __pyx_v_shared_foo;
Run Code Online (Sandbox Code Playgroud)
因此 Cython 将检查是否存在operator=(对我们来说幸运的是,因为 Cython 似乎不支持带模板的构造函数,但支持运算符)。
如果您想在没有打补丁的系统上分发您的模块,memory.pxd您有两个选择:
std::shared_ptr自己正确包裹%%cython
...
cdef extern from *:
"""
template<typename T1, typename T2>
void assign_shared_ptr(std::shared_ptr<T1>& lhs, const std::shared_ptr<T2>& rhs){
lhs = rhs;
}
"""
void assign_shared_ptr[T1, T2](shared_ptr[T1]& lhs, shared_ptr[T2]& rhs)
...
cdef shared_ptr[Derived] shared_foo
# cdef shared_ptr[decl.Base] bar = shared_foo
# must be replaced through:
cdef shared_ptr[Base] bar
assign_shared_ptr(bar, shared_foo)
...
Run Code Online (Sandbox Code Playgroud)
这两种选项都有缺点,因此根据您的情况,您可能更喜欢其中一种。