views:

48

answers:

3

The problem: I've wrapped some c++ code in python using SWIG. On the python side, I want to take a wrapped c++ pointer and down-cast it to be a pointer to a subclass. I've added a new c++ function to the SWIG .i file that does this down-casting, but when I call it from python, I get a TypeError.

Here are the details:

I have two c++ classes, Base and Derived. Derived is a subclass of Base. I have a third class, Container, which contains a Derived, and provides an accessor to it. The accessor returns the Derived as a const Base&, as shown:

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

  private:
    Derived derived_;
};

I've wrapped these classes in python using SWIG. In my python code, I would like to down-cast the Base reference back down to a Derived. To do this, I've written into the swig .i file a helper function in c++ which does the down-casting:

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

In my python code, I call this down-casting function:

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

When I do so, I get the following error:

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

Why might this be happening?

For reference, here are the relevant bits of the .cxx file generated by SWIG; namely the original function, and its python-interface-ified 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;
}

Any help would be greatly appreciated.

-- Matt

A: 

2 things I notice in your code 1st GetBase returns a reference to const and second that CastToDerived expects a pointer to non-const Base.

Even in C++ you'd have enough trouble making this work. I can't tell what else should be wrong but I would try to get this fxied first.

Vinzenz
That's not the problem, I think. The original CastToDerived took a const reference, and returned a const pointer, as you might suggest. I had the same error even then. SWIG takes all references and turns them into pointers, and drops constness. This is why my CastToDerived now takes and returns non-const pointers. Re: swig docs here: http://www.swig.org/Doc2.0/SWIGPlus.html#SWIGPlus_nn18 and here: http://www.swig.org/Doc2.0/SWIGPlus.html#SWIGPlus_const
SuperElectric
A: 

Is it possible that you are defining the Base class multiple times? I've had similar problems with ctypes where I unwittingly defined the same structure class in two different modules. I've also had something similar happen in pure Python, where I used imp.load_module to load a plugin class, created an object of that class, then reloaded the module - poof! the created object would no longer pass an isinstance test of the class, since the reloaded class, even though it had the same name, was a different class, with different id. (More complete description in this blog entry.)

Paul McGuire
+1  A: 

As I commented above, this seems to work ok with swig 1.3.40.

Here are my files:

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"

A run:

$ 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
Chris Card