views:

98

answers:

1

So I'm playing about with Python, C++0x, and SWIG 2.0. I've got a header that looks like this:

#include <string>
#include <iostream>
#include <memory>
using namespace std;

struct Base {
  virtual string name();
  int foo;
  shared_ptr<Base> mine;
  Base(int);  
  virtual ~Base() {}
  virtual void doit(shared_ptr<Base> b) {
    cout << name() << " doing it to " << b->name() << endl;
    mine = b;
  }
  virtual shared_ptr<Base> getit() {
    return mine;
  }
};

struct Derived : Base {
  virtual string name();
  int bar;
  Derived(int, int);
};

Meanwhile, the interface file looks like this:

%module(directors="1") foo
%feature("director");

%include <std_string.i>

%include <std_shared_ptr.i>
%shared_ptr(Base)
%shared_ptr(Derived)

%{
#define SWIG_FILE_WITH_INIT
#include "foo.hpp"
%}

%include "foo.hpp"

My Python session then goes like this:

>>> import foo
>>> b = foo.Base(42)
>>> d = foo.Derived(23,64)
>>> b.doit(d)
Base doing it to Derived 
>>> g = b.getit()
>>> g
<foo.Base; proxy of <Swig Object of type 'std::shared_ptr< Base > *' at 0x7f7bd1391930> >
>>> d
<foo.Derived; proxy of <Swig Object of type 'std::shared_ptr< Derived > *' at 0x7f7bd137ce10> >
>>> d == g
False
>>> d is g
False
>>> d.foo == g.foo
True
>>> d.bar
64
>>> g.bar
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'Base' object has no attribute 'bar'

I can't seem to figure out how to retrieve the "original" proxy object here. Must I produce a function for each and every base class to perform the dynamic_pointer_cast? And if so, what of Director subclasses implemented in Python?

I get the feeling there's a switch or feature I can turn on here to get SWIG to do the necessary table lookups and produce the object I want, but I haven't found it yet.

(Note: The behavior is similar if I use raw pointers instead of shared pointers, and I can't figure out how to get SWIG to dynamic_cast those either)

Update

If this sort of behavior (specifically, retrieving the most-derived proxy from a C++ container class that holds pointers to the base class) isn't possible in SWIG, then how about SIP or some other wrapper generator for Python?

Update #2

Since SIP4 looks like it works a bit better as far as retrieving the wrapped object sensibly, I'll change the question once again. Check my self answer below for details on my current issues. I'll still accept a good answer for the original SWIG question since I prefer it overall, but my new questions are, basically:

  • How can I deal sanely with wrappers around shared_ptrs rather than raw pointers? If it helps, all of my classes subclass enable_shared_from_this from their generic base classes and expose an appropriate function to get the shared pointer.

  • How can I, using either of SIP4's build systems (Makefile generator or distutils extension), build my little example project without having to first generate and install a shared library or manually edit the generated Makefile?

A: 

To (partially) answer my own question, SIP appears to do the right thing, both for the C++ "Derived" class as well as for a Python-level subclass — at least, when I use raw pointers.

Looks like I'll need to figure out how to get it to work with shared_ptrs (looks not quite as easy as %include <std_shared_ptr.i> in SWIG).

Also, both of SIPs "build system" options (Makefile generator and distutils extension) seem a little odd. Neither example in their manual "just works" - it looks like they expect it to be obvious that you're supposed to compile and install a shared library and header file in your library/include paths for the little Hello World library you're trying to wrap. Perhaps there's an "I just want to build and run this self-contained thing right here" option that I missed, but even python setup.py build_ext --inplace fails because it can't find the wrapped header that I've placed in the current directory, which is obviously a confusing place for it, I know.

kwatford