¡Excelente!
No completo pero suficiente. He podido hacer el truco para mi propio propósito. Combinando esta publicación con las fuentes vinculadas anteriormente. No ha sido fácil, dado que soy un principiante en Cython, pero confirmo que es la única forma que puedo encontrar en el www.
Muchas gracias chicos.
lo siento que no tengo tanto tiempo entrar en detalles textuales, pero aquí están mis archivos (que podría ayudar a conseguir un punto de vista adicional sobre cómo poner todo esto junto)
instalación .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: Una nota acerca de los métodos virtuales puros (que parece ser una preocupación bastante recurrente). Como se muestra en el código anterior, de esa manera particular, "TestClass :: override_me()" NO PUEDE ser puro, ya que tiene que ser invocable en caso de que el método no se anule en la clase extendida de Python (también conocido como: uno no se incluye la parte "error"/"anulación no encontrada" del cuerpo "ITestClass :: override_me()").
Extensión: 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()
Por último, el pitón llamadas:
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)
Esto debería hacer que el trabajo previo relacionado con suerte más recta hasta el punto que estamos discutiendo aquí ..
EDITAR: Por otro lado, el código anterior podría optimizarse utilizando 'hasattr' en lugar de 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
El código anterior, por supuesto, hace la diferencia solo en el caso de que no anulemos el método 'override_me'.
¿Has probado tu código? –
sí, por supuesto. Devuelve 2. ¿Necesita también la fuente de pyx (lo cual es completamente incorrecto, pero no pude encontrar una solución aún)? – ascobol
No, no creo que pueda ayudar. Creo que boost.python lo admite. –