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 subclassenable_shared_from_thisfrom 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?
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 --inplacefails 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.