2010-10-13 4 views
5

Das Problem: Ich habe einige C++ - Code in Python mit SWIG gewickelt. Auf der Python-Seite möchte ich einen eingepackten C++ - Zeiger nehmen und ihn als Zeiger auf eine Unterklasse abwandeln. Ich habe der SWIG-.i-Datei, die das Down-Casting durchführt, eine neue C++ - Funktion hinzugefügt, aber wenn ich sie von Python aus anrufe, erhalte ich einen TypeError.Wie kann ich ein C++ - Objekt aus einem Python-SWIG-Wrapper down-Cast?

Hier sind die Details:

Ich habe zwei C++ Klassen, Basis und abgeleitet. Abgeleitet ist eine Unterklasse von Base. Ich habe eine dritte Klasse, Container, die ein Abgeleitetes enthält und einen Accessor dafür bereitstellt. Der Accessor gibt als const Basis der abgeleiteten &, wie gezeigt:

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

    private: 
    Derived derived_; 
}; 

ich diese Klassen in Python mit SWIG gewickelt haben. In meinem Python-Code möchte ich die Base-Referenz zurück in eine abgeleitete Klasse umwandeln. Dazu habe ich in die swig geschrieben .i eine Hilfsfunktion in c-Datei ++, die den unten Guss tut:

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

In meinem Python-Code, nenne ich diese Abwärts Casting-Funktion:

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

Wenn ich dies tun, bekomme ich folgende Fehlermeldung:

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

Warum dies geschehen könnte?

Als Referenz sind hier die relevanten Bits der von SWIG erzeugten .cxx-Datei; nämlich die ursprüngliche Funktion, und seine Python-Schnittstelle-ified Doppelgängerin:

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

Jede Hilfe wäre sehr dankbar.

- Matt

+0

was macht der generierte Code für GetBase() aussehen? Der Fehler bedeutet, dass das Objekt, das an CastToDerived übergeben wird, nicht der richtige Typ ist - welcher Typ ist das? –

+0

Für was es wert ist, habe ich nur versucht, ein Beispiel basierend auf dem oben genannten zu erstellen, mit swig 1.3.40, und ich habe diesen Fehler nicht erhalten. –

+0

So seltsam ... Ich habe versucht, etwas wie dein Beispiel zu schreiben, Chris, und tatsächlich hat es funktioniert. Wie du vielleicht erwartest, ist der Code, mit dem ich eigentlich Probleme habe, kein Spielzeugproblem, ich habe einfach nur versucht, es so kurz zu machen, dass es in eine Frage passt. Meine aktuelle Problemumgehung besteht darin, die CastToDerived-Funktion in meiner C++ - Bibliothek statt in einem% inline% {...%} -Block in der .i-Datei zu definieren. Das behebt das Problem. Vielleicht könnte dieses Detail ein Hinweis für die richtige Person sein? Ich wünschte immer noch, ich könnte ohne Hinzufügen von SWIG-Helfern zu meiner C++ - Bibliothek auskommen. – SuperElectric

Antwort

3

Wie ich oben kommentiert, scheint dies mit swig 1.3.40 ok zu funktionieren.

Hier sind meine Dateien:

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" 

Ein Lauf:

$ 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 Dinge, die ich in Ihrem Code 1. bemerken GetBase gibt einen Verweis auf const und zweitens, dass CastToDerived einen Zeiger auf nicht konstante Basis erwartet.

Selbst in C++ hätten Sie genug Probleme damit. Ich kann nicht sagen, was sonst noch falsch sein sollte, aber ich würde versuchen, das zuerst zu bekommen.

+0

Das ist nicht das Problem, denke ich. Das ursprüngliche CastToDerived hat einen const-Verweis verwendet und einen const-Zeiger zurückgegeben, wie Sie vielleicht vermuten. Ich hatte den gleichen Fehler schon damals. SWIG nimmt alle Referenzen und verwandelt sie in Zeiger und lässt die Konstanz fallen. Das ist der Grund, warum CastToDerived jetzt nicht-konstante Zeiger annimmt und zurückgibt. Re: swig docs hier: http://www.swig.org/Doc2.0/SWIGPlus.html#SWIGPlus_nn18 und hier: http://www.swig.org/Doc2.0/SWIGPlus.html#SWIGPlus_const – SuperElectric

0

Ist es möglich, dass Sie die Basisklasse mehrmals definieren? Ich hatte ähnliche Probleme mit Ctypes, wo ich unwissentlich dieselbe Strukturklasse in zwei verschiedenen Modulen definiert habe. Ich hatte auch etwas Ähnliches in reinem Python, wo ich mit imp.load_module eine Plugin-Klasse geladen, ein Objekt dieser Klasse erstellt und dann das Modul neu geladen habe - poof! Das erstellte Objekt würde keinen Isinstanz-Test der Klasse mehr bestehen, da die neu geladene Klasse, obwohl sie den gleichen Namen hatte, eine andere Klasse mit anderer ID war. (Ausführliche Beschreibung in this blog entry.)

Verwandte Themen