Inheritance Cython包装类中的复制固有结构 我们假设下面的C++代码定义为i ab.h:
我想在Cython中封装指向这些类的对象的共享指针,因此我创建了以下pxd文件: 正如您所看到的,这不会编译。我的目标是让BPy从APy继承foo-python函数,这样我就不必编写两次。我可以跳过BPyAPy,只写BPy,但我必须写Inheritance Cython包装类中的复制固有结构 我们假设下面的C++代码定义为i ab.h:,inheritance,cython,Inheritance,Cython,我想在Cython中封装指向这些类的对象的共享指针,因此我创建了以下pxd文件: 正如您所看到的,这不会编译。我的目标是让BPy从APy继承foo-python函数,这样我就不必编写两次。我可以跳过BPyAPy,只写BPy,但我必须写 def foo(self): return self.c_self.get().foo() 在BPy的定义中也是如此 我可以将BPy中的c_self重命名为其他名称,例如c_b_self,然后在创建BPy对象时将我的指针同时指向c_self和c_b_se
def foo(self):
return self.c_self.get().foo()
在BPy的定义中也是如此
我可以将BPy中的c_self重命名为其他名称,例如c_b_self,然后在创建BPy对象时将我的指针同时指向c_self和c_b_self,但有没有更优雅的方法来实现我的目标?这不是对你问题的直接回答,如果有,我会很好奇但是一个选择是使用包装,它可以处理这个问题而不需要太多麻烦 包装器
令人惊讶的是,尽管感觉很自然,但并没有直接的方法使PyB成为PyA的子类,-毕竟B是a的子类 但是,所需的层次结构在某些微妙的方面违反了。这一原则大致说明了以下几点: 如果B是a的子类,那么a类型的对象可以是 替换为类型B的对象,但不破坏 节目 这并不明显,因为从Liskov的角度来看,PyA和PyB的公共接口还可以,但有一个隐含的特性使我们的生活更加艰难: PyA可以包装任何类型的对象 PyB可以包装任何类型B的对象,也可以做得比PyB少! 这一观察结果意味着这个问题不会有完美的解决方案,而你使用不同指针的建议也没那么糟糕 bellow给出的解决方案有一个非常相似的想法,只是我使用了一个cast,而不是缓存指针,它可能通过支付一些类型安全性来稍微提高性能 为了使示例独立,我使用inline-C-verbatim代码,为了使其更通用,我使用了不带可空构造函数的类:
%%cython --cplus
cdef extern from *:
"""
#include <iostream>
class A {
protected:
int number;
public:
A(int n):number(n){}
void foo() {std::cout<<"foo "<<number<<std::endl;}
};
class B : public A {
public:
B(int n):A(n){}
void bar() {std::cout<<"bar "<<number<<std::endl;}
};
"""
cdef cppclass A:
A(int n)
void foo()
cdef cppclass B(A): # make clear to Cython, that B inherits from A!
B(int n)
void bar()
...
值得注意的细节包括:
thisptr的类型是A*而不是A,因为A没有可为null的构造函数
我使用原始指针,因此需要保存引用,可能可以考虑使用std::unique\u ptr或std::shared\u ptr,具体取决于类的使用方式。
创建类A的对象时,thisptr会自动初始化为nullptr,因此不需要在uu cinit_uu中显式地将thisptr设置为nullptr,这就是省略u cinit_uu的原因。
为什么使用“初始”和“不使用”将在一段时间内变得显而易见。
现在是B类的包装器:
最后一个想法:我有过这样的经历,使用继承来保存代码常常会导致问题,因为最终会因为错误的原因导致错误的层次结构。可能还有其他工具可以减少模板代码,比如@chrisb提到的pybind11框架,它们更适合这项工作。您可能会从这个问题中获得一些见解。正如@克里斯德指出的那样,为了包装复杂的C++结构,PybDun11可能是一种更简单的方法。但它隐藏了太多的细节。为了更好地使用它,对C++语义和一些棘手的模板技术有很好的理解。
cdef class APy:
def foo(self):
return self.c_self.get().foo()
cdef class BPy(APy):
def bar(self):
return self.c_self.get().bar()
def foo(self):
return self.c_self.get().foo()
#include <pybind11/pybind11.h>
#include "AB.h"
namespace py = pybind11;
PYBIND11_MODULE(example, m) {
py::class_<A>(m, "A")
.def(py::init<>())
.def("foo", &A::foo);
py::class_<B, A>(m, "B") // second template param is parent
.def(py::init<>())
.def("bar", &B::bar);
}
from setuptools import setup, Extension
import pybind11
setup(ext_modules=[Extension('example', ['wrapper.cpp'],
include_dirs=[pybind11.get_include()])])
%%cython --cplus
cdef extern from *:
"""
#include <iostream>
class A {
protected:
int number;
public:
A(int n):number(n){}
void foo() {std::cout<<"foo "<<number<<std::endl;}
};
class B : public A {
public:
B(int n):A(n){}
void bar() {std::cout<<"bar "<<number<<std::endl;}
};
"""
cdef cppclass A:
A(int n)
void foo()
cdef cppclass B(A): # make clear to Cython, that B inherits from A!
B(int n)
void bar()
...
...
cdef class PyA:
cdef A* thisptr # ptr in order to allow for classes without nullable constructors
cdef void init_ptr(self, A* ptr):
self.thisptr=ptr
def __init__(self, n):
self.init_ptr(new A(n))
def __dealloc__(self):
if NULL != self.thisptr:
del self.thisptr
def foo(self):
self.thisptr.foo()
...
...
cdef class PyB(PyA):
def __init__(self, n):
self.init_ptr(new B(n))
cdef B* as_B(self):
return <B*>(self.thisptr) # I know for sure it is of type B*!
def bar(self):
self.as_B().bar()
>>> PyB(42).foo()
foo 42
>>> PyB(42).bar()
bar 42