2010-10-13 18 views
5

El problema: he envuelto un código de C++ en python usando SWIG. En el lado de Python, quiero tomar un puntero C++ envuelto y convertirlo en un puntero a una subclase. He agregado una nueva función de C++ al archivo SWIG .i que realiza este down-casting, pero cuando lo llamo desde python, obtengo un TypeError.¿Cómo puedo descifrar un objeto de C++ desde un contenedor SWIG de python?

Aquí están los detalles:

Tengo dos clases de C++, la base y derivada. Derived es una subclase de Base. Tengo una tercera clase, Container, que contiene un Derived y proporciona un acceso a ella. El descriptor de acceso devuelve el Derivado como base const &, como se muestra:

class Container { 
    public: 
    const Base& GetBase() const { 
     return derived_; 
    } 

    private: 
    Derived derived_; 
}; 

me he envuelto estas clases en Python usando el TRAGO. En mi código de Python, me gustaría bajar la referencia Base de nuevo a Derived. Para ello, he escrito en el trago .i presentar una función auxiliar en C++, que hace la baja calidad:

%inline %{ 
    Derived* CastToDerived(Base* base) { 
    return static_cast<Derived*>(base); 
    } 
%} 

En mi código Python, llamo a esta función, derribando:

base = container.GetBase() 
derived = CastToDerived(base) 

Cuando lo hago, me sale el siguiente error:

TypeError: in method 'CastToDerived', argument 1 of type 'Base *' 

¿Por qué puede estar pasando esto?

Como referencia, aquí están los bits relevantes del archivo .cxx generado por SWIG; a saber, la función original, y su doppelganger ified-python-interface:

Derived* CastToDerived(Base* base) { 
    return static_cast<Derived*>(base); 
    } 

// (lots of other generated code omitted) 

SWIGINTERN PyObject *_wrap_CastToDerived(PyObject *SWIGUNUSEDPARM(self), PyObject *args) { 
    PyObject *resultobj = 0; 
    Base *arg1 = (Base *) 0 ; 
    void *argp1 = 0 ; 
    int res1 = 0 ; 
    PyObject * obj0 = 0 ; 
    Derived *result = 0 ; 

    if (!PyArg_ParseTuple(args,(char *)"O:CastToDerived",&obj0)) SWIG_fail; 
    res1 = SWIG_ConvertPtr(obj0, &argp1,SWIGTYPE_p_Base, 0 | 0); 
    if (!SWIG_IsOK(res1)) { 
    SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "CastToDerived" "', argument " "1"" of type '" "Base *""'"); 
    } 
    arg1 = reinterpret_cast< Base * >(argp1); 
    result = (Derived *)CastToDerived(arg1); 
    resultobj = SWIG_NewPointerObj(SWIG_as_voidptr(result), SWIGTYPE_p_Derived, 0 | 0); 
    return resultobj; 
fail: 
    return NULL; 
} 

Cualquier ayuda sería muy apreciada.

- Matt

+0

lo que hace el código generado para GetBase() parece? El error significa que el objeto que se pasa a CastToDerived no es el tipo correcto, ¿de qué tipo es? –

+0

Por lo que vale, intenté crear un ejemplo basado en lo anterior, usando el truco 1.3.40, y no obtuve este error. –

+0

Tan extraño ... Intenté escribir algo como tu ejemplo, Chris, y de hecho funcionó. Como era de esperar, el código con el que estoy teniendo problemas no es un problema de juguete, simplemente lo modifiqué para hacerlo lo suficientemente corto como para caber en una pregunta. Mi solución actual es definir la función CastToDerived en mi biblioteca C++, en lugar de un bloque% inline% {...%} en el archivo .i. Eso soluciona el problema. ¿Tal vez ese detalle podría ser una pista para la persona correcta? Todavía me gustaría poder hacerlo sin agregar ayudantes de SWIG a mi biblioteca de C++. – SuperElectric

Respuesta

3

Como he comentado anteriormente, esto parece funcionar bien con swig 1.3.40.

Éstos son mis archivos:

c.h:

#include <iostream> 
class Base {}; 
class Derived : public Base 
{ 
    public: 
     void f() const { std::cout << "In Derived::f()" << std::endl; } 
}; 
class Container { 
    public: 
    const Base& GetBase() const { 
     return derived_; 
    } 
    private: 
    Derived derived_; 
}; 

C.I.

%module c 

%{ 
#define SWIG_FILE_WITH_INIT 
#include "c.h" 
%} 

%inline %{ 
    Derived* CastToDerived(Base* base) { 
    return static_cast<Derived*>(base); 
    } 
%} 
class Base 
{ 
}; 

class Derived : public Base 
{ 
    public: 
     void f() const; 
}; 

class Container { 
    public: 
    const Base& GetBase() const; 
}; 

CTest.py

import c 

container = c.Container() 
b = container.GetBase() 
d = c.CastToDerived(b) 
d.f() 
print "ok" 

Una carrera:

$ swig -c++ -python c.i 
$ g++ -fPIC -I/usr/include/python2.6 -c -g c_wrap.cxx 
$ g++ -shared -o _c.so c_wrap.o 
$ python ctest.py 
In Derived::f() 
ok 
0

2 cosas que noto en el código primero GetBase devuelve una referencia a const y segundo que CastToDerived espera un puntero a la base no constante.

Incluso en C++ tendrías suficientes problemas para hacer este trabajo. No puedo decir qué más debería estar mal, pero trataría de obtener este fxied primero.

+0

Ese no es el problema, creo. El CastToDerived original tomó una referencia constante, y devolvió un puntero de const, como usted podría sugerir. Tuve el mismo error incluso entonces. SWIG toma todas las referencias y las convierte en punteros y elimina la constness. Esta es la razón por la que CastToDerived ahora toma y devuelve punteros no const. Re: Swim docs aquí: http://www.swig.org/Doc2.0/SWIGPlus.html#SWIGPlus_nn18 y aquí: http://www.swig.org/Doc2.0/SWIGPlus.html#SWIGPlus_const – SuperElectric

0

¿Es posible que esté definiendo la clase base varias veces? He tenido problemas similares con Ctypes donde inconscientemente he definido la misma clase de estructura en dos módulos diferentes. También he tenido algo similar en Python puro, donde utilicé imp.load_module para cargar una clase de complemento, creé un objeto de esa clase, luego recargué el módulo - poof! el objeto creado ya no pasaría una prueba de instancia de la clase, ya que la clase recargada, aunque tenía el mismo nombre, era una clase diferente, con una identificación diferente. (Descripción más completa en this blog entry.)

Cuestiones relacionadas