我可以用Cython覆盖Python中的C ++虚拟函数吗? [英] Can I override a C++ virtual function within Python with Cython?

查看:386
本文介绍了我可以用Cython覆盖Python中的C ++虚拟函数吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个有虚拟方法的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并正确覆盖调用:

  #python:
class B(PyA):
def override_me(self,a):
return 5 * a
b = B()
b.calculate(1)#应该返回5而不是2

有办法吗?
现在我想,如果我们可以覆盖Cython中的虚拟方法(在pyx文件中),但是允许用户在纯Python中做到这一点也很重要。



编辑:如果这有帮助,解决方案可能是使用这里提供的伪码: http://docs.cython.org/src/userguide/pyrex_differences.html#cpdef-functions



但是有两个问题:




  • 我不知道如何在Cython中编写这个伪代码

  • 也许有更好的方法。


解决方案



不完整但足够。
我已经能够为自己的目的做的伎俩。将此帖与上面链接的来源组合。
这不容易,因为我是Cython的初学者,但我确认它是我能在www找到的唯一方法。



谢谢很多给你们。



对不起,我没有这么多时间进入文本细节,但这里是我的文件(可能有助于获得额外的关于如何将所有这些放在一起的观点)



setup.py:

 从distutils.core导入安装
从distutils.extension导入扩展
从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 ++,

]

TestClass:

  #ifndef TESTCLASS_H_ 
#define TESTCLASS_H_


命名空间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;

};

} / *命名空间elps * /
#endif / * TESTCLASS_H_ * /

ITestClass.h:

  #ifndef ITESTCLASS_H_ 
#define ITESTCLASS_H_

//在提供'public api'关键字时由Cython创建
#include../elps_api.h

#include../../inc/TestClass.h

命名空间elps {

类ITestClass:public TestClass {
public:
PyObject * m_obj;

ITestClass(PyObject * obj);
virtual〜ITestClass();
virtual int override_me();
};

} / *命名空间elps * /
#endif / * ITESTCLASS_H_ * /

ITestClass.cpp:

  #includeITestClass.h

命名空间{

ITestClass :: ITestClass(PyObject * obj):m_obj(obj){
//由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;
//如果存在则调用虚拟重载
int result = cy_call_func(this-> m_obj,(char *)override_me,& error)
if(error)
//调用父方法
result = TestClass :: override_me();
return result;
}
//抛出错误?
return 0;
}

} / *命名空间elps * /

EDIT2 :关于PURE虚拟方法的注释(它似乎是一个相当经常性的问题)。如上面的代码所示,在这种特定的方式,TestClass :: override_me()不能是纯的,因为它必须是可调用的,如果该方法不被重写在Python的扩展类(aka:一个不落在



扩展名:elps.pyx:

$ b
$ b

  cimport cpython.ref as cpy_ref 

cdef extern从src / ITestClass.h命名空间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):
## printin TestClass:allocate thisptr
self.thisptr =新的ITestClass(< cpy_ref.PyObject *> self)
def __dealloc __(self):
if self.thisptr:
## printin 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()



最后,python调用:

 从elps import PyTestClass为TC; 

a = TC();
print a.calculate(1);

class B(TC):
#pass
def override_me(self):
return 5

b = B $ b print b.calculate(1)

这应该使以前的链接工作更加直接点我们在这里讨论...



编辑:另一方面,上面的代码可以通过使用'hasattr'而不是try / catch块来优化:

  cdef public api int cy_call_func_int_fast(object self,char * method,bint * error):
if(hasattr方法)):
error [0] = 0
return getattr(self,method)();
else:
error [0] = 1

上述代码当然,只有在我们不重写override_me方法的情况下才会有所不同。


I have a C++ class with a virtual method:

//C++
class A
{

    public:
        A() {};
        virtual int override_me(int a) {return 2*a;};
        int calculate(int a) { return this->override_me(a) ;}

};

What I would like to do is to expose this class to Python with Cython, inherit from this class in Python and have the correct overridden called:

#python:
class B(PyA):
   def override_me(self, a):
       return 5*a
b = B()
b.calculate(1)  # should return 5 instead of 2

Is there a way to do this ? Now I'm thinking, it could also be great if we could override the virtual method in Cython as well (in a pyx file), but allowing users to do this in pure python is more important.

Edit: If this helps, a solution could be to use the pseudocode given here: http://docs.cython.org/src/userguide/pyrex_differences.html#cpdef-functions

But there are two problems then :

  • I don't know how to write this pseudocode in Cython
  • maybe there is a better approach

解决方案

Excellent !

Not complete but sufficient. I've been able to do the trick for my own purpose. Combining this post with the sources linked above. It's not been easy, since I'm a beginner at Cython, but I confirm that it is the only way I could find over the www.

Thanks a lot to you guys.

I am sorry that I don't have so much time go into textual details, but here are my files (might help to get an additional point of view on how to put all of this together)

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++",
              )
    ]
)

TestClass :

#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 : A note about PURE virtual methods (it appears to be a quite recurrent concern). As shown in the above code, in that particular fashion, "TestClass::override_me()" CANNOT be pure since it has to be callable in case the method is not overridden in the Python's extended class (aka : one doesn't fall in the "error"/"override not found" part of the "ITestClass::override_me()" body).

Extension : 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()

Finally, the python calls :

from elps import PyTestClass as TC;

a = TC(); 
print a.calculate(1);

class B(TC):
#   pass
    def override_me(self):
        return 5

b = B()
print b.calculate(1)

This should make the previous linked work hopefully more straight to the point we're discussing here...

EDIT : On the other hand the above code could be optimized by using 'hasattr' instead of try/catch block :

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

The above code, of course, makes a difference only in the case where we don't override the 'override_me' method.

这篇关于我可以用Cython覆盖Python中的C ++虚拟函数吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆