我可以覆盖C++;Python中使用Cython的虚拟函数? 我有一个C++类,它有一个虚拟方法: //C++ class A { public: A() {}; virtual int override_me(int a) {return 2*a;}; int calculate(int a) { return this->override_me(a) ;} };
我想做的是使用Cython将该类公开给Python,在Python中继承该类,并将正确的重写名为:我可以覆盖C++;Python中使用Cython的虚拟函数? 我有一个C++类,它有一个虚拟方法: //C++ class A { public: A() {}; virtual int override_me(int a) {return 2*a;}; int calculate(int a) { return this->override_me(a) ;} };,c++,python,cython,C++,Python,Cython,我想做的是使用Cython将该类公开给Python,在Python中继承该类,并将正确的重写名为: #python: class B(PyA): def override_me(self, a): return 5*a b = B() b.calculate(1) # should return 5 instead of 2 有办法做到这一点吗? 现在我在想,如果我们也能覆盖Cython中的虚拟方法(在pyx文件中),那也太好了,但是允许用户在纯python中这样做更重要
#python:
class B(PyA):
def override_me(self, a):
return 5*a
b = B()
b.calculate(1) # should return 5 instead of 2
有办法做到这一点吗?
现在我在想,如果我们也能覆盖Cython中的虚拟方法(在pyx文件中),那也太好了,但是允许用户在纯python中这样做更重要
编辑:如果这有帮助,可以使用此处给出的伪代码:
但有两个问题:
- 我不知道如何用Cython编写这个伪代码
- 也许有更好的方法
- 解决方案有点复杂,但这是可能的。这里有一个完全有效的示例:
以下是该技术的概述:
创建
类a
的专用子类,其目的是与cython扩展交互:
// created by cython when providing 'public api' keywords:
#include "mycymodule_api.h"
class CyABase : public A
{
public:
PyObject *m_obj;
CyABase(PyObject *obj);
virtual ~CyABase();
virtual int override_me(int a);
};
CyABase::CyABase(PyObject *obj) :
m_obj(obj)
{
// provided by "mycymodule_api.h"
if (import_mycymodule()) {
} else {
Py_XINCREF(this->m_obj);
}
}
CyABase::~CyABase()
{
Py_XDECREF(this->m_obj);
}
构造函数接受一个python对象,它是cython扩展的实例:
// created by cython when providing 'public api' keywords:
#include "mycymodule_api.h"
class CyABase : public A
{
public:
PyObject *m_obj;
CyABase(PyObject *obj);
virtual ~CyABase();
virtual int override_me(int a);
};
CyABase::CyABase(PyObject *obj) :
m_obj(obj)
{
// provided by "mycymodule_api.h"
if (import_mycymodule()) {
} else {
Py_XINCREF(this->m_obj);
}
}
CyABase::~CyABase()
{
Py_XDECREF(this->m_obj);
}
在cython中创建此子类的扩展,以标准方式实现所有非虚拟方法
cdef class A:
cdef CyABase* thisptr
def __init__(self):
self.thisptr = new CyABase(
<cpy_ref.PyObject*>self)
#------- non-virutal methods --------
def calculate(self):
return self.thisptr.calculate()
在C++的中间层中使用这些函数:
int
CyABase::override_me(int a)
{
if (this->m_obj) {
int error;
// call a virtual overload, if it exists
int result = cy_call_override_me(this->m_obj, a, &error);
if (error)
// call parent method
result = A::override_me(i);
return result;
}
// throw error?
return 0;
}
我很快根据您的示例修改了代码,因此可能会出现错误。请看一下存储库中的完整示例,它将回答您的大多数问题。你可以自由地用叉子叉它,添加你自己的实验,它还远远没有完成 太好了
不完整但足够。
我已经能够为我自己的目的做这个把戏了。将这篇文章与上面链接的来源相结合。
这并不容易,因为我是Cython的初学者,但我确认这是我能在www上找到的唯一方法
非常感谢你们
很抱歉,我没有太多时间来讨论文本细节,但这里是我的文件(可能有助于获得关于如何将所有这些放在一起的其他观点)
setup.py:
from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
setup(
cmdclass = {'build_ext': build_ext},
ext_modules = [
Extension("elps",
sources=["elps.pyx", "src/ITestClass.cpp"],
libraries=["elp"],
language="c++",
)
]
)
测试类:
#ifndef TESTCLASS_H_
#define TESTCLASS_H_
namespace elps {
class TestClass {
public:
TestClass(){};
virtual ~TestClass(){};
int getA() { return this->a; };
virtual int override_me() { return 2; };
int calculate(int a) { return a * this->override_me(); }
private:
int a;
};
} /* namespace elps */
#endif /* TESTCLASS_H_ */
ITestClass.h:
#ifndef ITESTCLASS_H_
#define ITESTCLASS_H_
// Created by Cython when providing 'public api' keywords
#include "../elps_api.h"
#include "../../inc/TestClass.h"
namespace elps {
class ITestClass : public TestClass {
public:
PyObject *m_obj;
ITestClass(PyObject *obj);
virtual ~ITestClass();
virtual int override_me();
};
} /* namespace elps */
#endif /* ITESTCLASS_H_ */
ITestClass.cpp:
#include "ITestClass.h"
namespace elps {
ITestClass::ITestClass(PyObject *obj): m_obj(obj) {
// Provided by "elps_api.h"
if (import_elps()) {
} else {
Py_XINCREF(this->m_obj);
}
}
ITestClass::~ITestClass() {
Py_XDECREF(this->m_obj);
}
int ITestClass::override_me()
{
if (this->m_obj) {
int error;
// Call a virtual overload, if it exists
int result = cy_call_func(this->m_obj, (char*)"override_me", &error);
if (error)
// Call parent method
result = TestClass::override_me();
return result;
}
// Throw error ?
return 0;
}
} /* namespace elps */
EDIT2:关于纯虚拟方法的说明(这似乎是一个经常出现的问题)。如上面的代码所示,以这种特定的方式,“TestClass::override_me()”不能是纯的,因为它必须是可调用的,以防在Python的扩展类中没有重写该方法(也就是说:一个不属于“ITestClass::override_me()”主体的“error”/“override not found”部分)
扩展名:elps.pyx:
cimport cpython.ref as cpy_ref
cdef extern from "src/ITestClass.h" namespace "elps" :
cdef cppclass ITestClass:
ITestClass(cpy_ref.PyObject *obj)
int getA()
int override_me()
int calculate(int a)
cdef class PyTestClass:
cdef ITestClass* thisptr
def __cinit__(self):
##print "in TestClass: allocating thisptr"
self.thisptr = new ITestClass(<cpy_ref.PyObject*>self)
def __dealloc__(self):
if self.thisptr:
##print "in TestClass: deallocating thisptr"
del self.thisptr
def getA(self):
return self.thisptr.getA()
# def override_me(self):
# return self.thisptr.override_me()
cpdef int calculate(self, int a):
return self.thisptr.calculate(a) ;
cdef public api int cy_call_func(object self, char* method, int *error):
try:
func = getattr(self, method);
except AttributeError:
error[0] = 1
else:
error[0] = 0
return func()
这将使之前的链接工作更直接地指向我们在这里讨论的点
编辑:另一方面,可以使用“hasattr”而不是try/catch块来优化上述代码:
cdef public api int cy_call_func_int_fast(object self, char* method, bint *error):
if (hasattr(self, method)):
error[0] = 0
return getattr(self, method)();
else:
error[0] = 1
当然,上面的代码只有在不重写“override_me”方法的情况下才起作用。当然可以。它返回2。您是否也需要pyx源代码(这显然是错误的,但我还没有找到修复程序)?不,我想我帮不了忙。我认为boost.python支持这一点。事实上,几年前我就用boost.python实现了这一点。现在,我想尝试boost.python的替代方案(编译太长,导致模块太大,…)。如果Cython能够处理这个问题,我认为其余的工作会顺利进行。我不认为这是直接支持的,但已经有了一个解决方法。另一个解决方法是使用策略模式或类似的方法,而不是方法重载。请注意,对于那些想尝试此示例的人:TestClass()和~TestClass()的实现他失踪了。这将导致类似“ImportError:./elps.so:未定义的符号:_ZTIN4elps9TestClassE”的错误。只需添加一个空的内联实现,您的解决方案中是否有一种将虚拟方法(即override_me())公开到Python端的方法?只要您更改名称,您应该能够:
def call_override_me(self):返回self.thisptr.override_me()
?我希望保持相同的名称。但我现在有了一个想法:cy_call_func_int_fast可以检查override_me是否已被重写。它需要比较类和实例对象的方法override\u me,如if PyTestClass.override\u me!=self.\uuuuu class\uuuuu.override\u me
。也许它能起作用……好的,请随时通知我们。感谢您的编辑(内联constr/dest);)这是一个很好的开始,非常感谢。但是Python脚本可以调用override_me()方法吗?如果这个方法在C++中不是纯虚的,那么应该能够从Python Pipe调用它。