2010-10-13 50 views
5

Le problème: J'ai enveloppé du code C++ en python en utilisant SWIG. Du côté de python, je veux prendre un pointeur c + + enveloppé et le transformer en un pointeur vers une sous-classe. J'ai ajouté une nouvelle fonction C++ au fichier SWIG .i qui fait ce down-casting, mais quand je l'appelle depuis python, j'obtiens un TypeError.Comment est-ce que je descends un objet C++ à partir d'un wrapper SWX python?

Voici les détails:

J'ai deux classes C++, de base et dérivés. Derived est une sous-classe de Base. J'ai une troisième classe, Container, qui contient un dérivé, et lui fournit un accesseur. L'accesseur retourne la base Derived comme const &, comme le montre:

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

    private: 
    Derived derived_; 
}; 

J'ai enveloppé ces classes en python utilisant SWIG. Dans mon code python, je souhaite redescendre la référence de base vers un dérivé. Pour ce faire, je l'ai écrit dans le rasade .i fichier une fonction d'assistance en C++ qui fait le bas-casting:

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

Dans mon code python, j'appelle cette fonction vers le bas-casting:

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

Quand je le fais, je reçois l'erreur suivante:

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

Pourquoi pourrait-il se passer?

Pour référence, voici les bits pertinents du fichier .cxx généré par SWIG; à savoir la fonction d'origine, et son interface ified-python Doppelganger:

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

Toute aide serait grandement appréciée.

- Matt

+0

ce que fait le code généré pour getBase() ressembler? L'erreur signifie que l'objet transmis à CastToDerived n'est pas le bon type - de quel type s'agit-il? –

+0

Pour ce que ça vaut, j'ai juste essayé de créer un exemple basé sur ce qui précède, en utilisant swig 1.3.40, et je n'ai pas eu cette erreur. –

+0

Si étrange ... J'ai essayé d'écrire quelque chose comme votre exemple, Chris, et en effet cela a fonctionné. Comme vous pouvez vous y attendre, le code avec lequel j'ai des problèmes n'est pas un problème de jouet, je l'ai juste fait pour le rendre assez court pour répondre à une question. Ma solution de contournement actuelle consiste à définir la fonction CastToDerived dans ma bibliothèque C++, au lieu d'un bloc% inline% {...%} dans le fichier .i. Cela résout le problème. Peut-être que ce détail pourrait être un indice à la bonne personne? Je souhaite toujours que je pourrais faire sans ajouter des aides de SWIG à ma bibliothèque de C++. – SuperElectric

Répondre

3

Comme je l'ai commenté ci-dessus, cela semble fonctionner correctement avec swig 1.3.40.

Voici mes fichiers:

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" 

Une course:

$ 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 choses que je remarque dans votre 1er code getBase renvoie une référence à const et seconde qui CastToDerived attend un pointeur vers la base non-const.

Même en C++, vous auriez assez de mal à faire ce travail. Je ne peux pas dire quoi d'autre devrait être mauvais mais j'essaierais d'obtenir ceci fxied d'abord.

+0

Ce n'est pas le problème, je pense. L'original CastToDerived a pris une référence const, et a renvoyé un pointeur const, comme vous pouvez le suggérer. J'ai eu la même erreur même alors. SWIG prend toutes les références et les transforme en pointeurs, et laisse tomber la constance. C'est pourquoi mon CastToDerived prend maintenant et retourne des pointeurs non-const. Re: swig docs ici: http://www.swig.org/Doc2.0/SWIGPlus.html#SWIGPlus_nn18 et ici: http://www.swig.org/Doc2.0/SWIGPlus.html#SWIGPlus_const – SuperElectric

0

Est-il possible que vous définissiez la classe de base plusieurs fois? J'ai eu des problèmes similaires avec ctypes où j'ai involontairement défini la même classe de structure dans deux modules différents. J'ai aussi eu quelque chose de similaire en Python pur, où j'ai utilisé imp.load_module pour charger une classe de plugin, créé un objet de cette classe, puis rechargé le module - pouf! l'objet créé ne passerait plus un test isinstance de la classe, puisque la classe reloaded, même si elle avait le même nom, était une classe différente, avec un identifiant différent. (Description plus complète dans this blog entry.)