2010-10-13 14 views
5

Il problema: ho avvolto del codice C++ in python usando SWIG. Dal lato python, voglio prendere un puntatore C++ avvolto e farlo ruotare verso il basso per essere un puntatore a una sottoclasse. Ho aggiunto una nuova funzione C++ al file SWI .i che esegue questo down-casting, ma quando lo chiamo da python, ottengo un TypeError.Come si esegue il down-cast di un oggetto C++ da un wrapper SWIG python?

Ecco i dettagli:

Ho due classi C++, di base e derivati. Derivato è una sottoclasse di Base. Ho una terza classe, Container, che contiene un derivato e fornisce un accesso ad esso. La funzione di accesso restituisce il Derivato come base const &, come mostrato:

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

    private: 
    Derived derived_; 
}; 

ho avvolto queste classi in Python utilizzando SWIG. Nel mio codice Python, vorrei ridiscendere il riferimento Base su un derivato. Per fare questo, ho scritto nella sorso .i presentare una funzione di supporto in C++ che fa il down-casting:

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

Nel mio codice python, Io chiamo questo down-colata funzione:

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

Quando lo faccio, ottengo il seguente errore:

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

Perché possa questo accadere?

Per riferimento, ecco i bit rilevanti del file .cxx generato da SWIG; cioè la funzione originale, e la sua doppelganger pitone-interfaccia-ified:

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; 
} 

Qualsiasi aiuto sarebbe molto apprezzato.

- Matt

+0

ciò che il codice generato per getBase() simile? L'errore indica che l'oggetto passato a CastToDerived non è il tipo giusto, di che tipo è? –

+0

Per quello che vale, ho appena provato a creare un esempio basato su quanto sopra, usando swig 1.3.40, e non ho ricevuto questo errore. –

+0

Così strano ... Ho provato a scrivere qualcosa come il tuo esempio, Chris, e in effetti ha funzionato. Come potreste aspettarvi, il codice con cui ho effettivamente dei problemi non è un problema con i giocattoli, ma ho solo pensato che fosse abbastanza breve da adattarsi a una domanda. La mia soluzione attuale consiste nel definire la funzione CastToDerived nella mia libreria C++, invece che in un blocco% inline% {...%} nel file .i. Questo risolve il problema. Forse quel dettaglio potrebbe essere un suggerimento per la persona giusta? Vorrei ancora poter fare a meno di aggiungere gli helper SWIG alla mia libreria C++. – SuperElectric

risposta

3

Come ho già commentato, sembra funzionare bene con swig 1.3.40.

Qui sono i miei file:

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 corsa:

$ 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 cose che ho notato nel codice prima getBase restituisce un riferimento a const e secondo che CastToDerived si aspetta un puntatore a base non-const.

Anche in C++ avresti abbastanza problemi per farlo funzionare. Non posso dire quale altro dovrebbe essere sbagliato, ma proverei prima a farlo.

+0

Questo non è il problema, penso. L'originale CastToDerived ha preso un riferimento const e ha restituito un puntatore const, come suggerito. Ho avuto lo stesso errore anche allora. SWIG prende tutti i riferimenti e li trasforma in puntatori, e diminuisce la costanza. Questo è il motivo per cui il mio CastToDerived ora prende e restituisce puntatori non const. Re: swig docs qui: http://www.swig.org/Doc2.0/SWIGPlus.html#SWIGPlus_nn18 e qui: http://www.swig.org/Doc2.0/SWIGPlus.html#SWIGPlus_const – SuperElectric

0

È possibile che si stia definendo la classe Base più volte? Ho avuto problemi simili con i ctypes dove ho involontariamente definito la stessa classe di struttura in due moduli diversi. Ho anche avuto qualcosa di simile in puro Python, dove ho usato imp.load_module per caricare una classe plugin, creato un oggetto di quella classe, quindi ho ricaricato il modulo - poof! l'oggetto creato non avrebbe più superato un test di istanza della classe, poiché la classe reloaded, anche se aveva lo stesso nome, era una classe diversa, con ID diverso. (Descrizione più completa in this blog entry.)

Problemi correlati