Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/codeigniter/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
我可以覆盖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 - Fatal编程技术网

我可以覆盖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++类,它有一个虚拟方法: //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中这样做更重要

我想做的是使用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中这样做更重要

编辑:如果这有帮助,可以使用此处给出的伪代码:

但有两个问题:

  • 我不知道如何用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调用它。