Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SWIG, boost shared pointers and inheritance

I'm having trouble with SWIG, shared pointers, and inheritance.

I am creating various c++ classes which inherit from one another, using Boost shared pointers to refer to them, and then wrapping these shared pointers with SWIG to create the python classes.

My problem is the following:

  • B is a subclass of A
  • sA is a shared pointer to A
  • sB is a shared pointer to B
  • f(sA) is a function expecting a shared pointer to A

  • If I pass sB to f() then an error is raised.

  • This error only occurs at the python level.
  • At the C++ level I can pass sB to f() without a problem.

I have boost 1.40 and swig 1.3.40.

Below are the contents of 5 files which will reproduce the problem with:

python setup.py build_ext --inplace
python test.py

swig_shared_ptr.h

#ifndef INCLUDED_SWIG_SHARED_PTR_H
#define INCLUDED_SWIG_SHARED_PTR_H

#include <boost/shared_ptr.hpp>

class Base {};

class Derived : public Base {};

typedef boost::shared_ptr<Base> base_sptr;
typedef boost::shared_ptr<Derived> derived_sptr;

void do_something (base_sptr bs);

base_sptr make_base();
derived_sptr make_derived();

#endif

swig_shared_ptr.cc

#include <iostream>
#include "swig_shared_ptr.h"

void do_something (base_sptr bs)
{
  std::cout << "Doing something." << std::endl;
}

base_sptr make_base() { return base_sptr(new Base ()); };
derived_sptr make_derived() { return derived_sptr(new Derived ()); };

swig_shared_ptr.i

%module(docstring="
Example module showing problems I am having with SWIG, shared pointers
and inheritance.
") swig_shared_ptr

%{
#include "swig_shared_ptr.h"
%}

%include <swig_shared_ptr.h>
%include <boost_shared_ptr.i>
%template(base_sptr) boost::shared_ptr<Base>;
%template(derived_sptr) boost::shared_ptr<Derived>;

setup.py

"""
setup.py file for swig_shared_ptr
"""

from distutils.core import setup, Extension

swig_shared_ptr_module = Extension('_swig_shared_ptr',
                         include_dirs = ['/usr/include/boost'],
                         sources=['swig_shared_ptr.i', 'swig_shared_ptr.cc'],
                         )

setup (name = 'swig_shared_ptr',
       version = '0.1',
       author = "Ben",
       description = """Example showing problems I am having with SWIG, shared
                        pointers and inheritance.""",
       ext_modules = [swig_shared_ptr_module],
       py_modules = ["swig_shared_ptr"],
       )

test.py

import swig_shared_ptr as ssp

bs = ssp.make_base()
dr = ssp.make_derived()

# Works fine.
ssp.do_something(bs) 
# Fails with "TypeError: in method 'do_something', argument 1 of type 'base_sptr'"
ssp.do_something(dr) 
like image 525
Ben Reynwar Avatar asked Jan 17 '11 04:01

Ben Reynwar


2 Answers

The following change appears to solve the problem.

In swig_shared_ptr.i the two lines:

%template(base_sptr) boost::shared_ptr<Base>;
%template(derived_sptr) boost::shared_ptr<Derived>;

are moved so that they are above the line

%include <swig_shared_ptr.h>

and are then replaced (in SWIG 1.3) by:

SWIG_SHARED_PTR(Base, Base)
SWIG_SHARED_PTR_DERIVED(Derived, Base, Derived)    

or (in SWIG 2.0) by:

%shared_ptr(Base)
%shared_ptr(Derived)
like image 58
Ben Reynwar Avatar answered Oct 19 '22 14:10

Ben Reynwar


SWIG doesn't know anything about the boost::shared_ptr<T> class. It therefore can't tell that derived_sptr can be "cast" (which is, I believe, implemented with some crazy constructors and template metaprogramming) to derived_sptr. Because SWIG requires fairly simple class definitions (or inclusion of simple files with %include), you won't be able to accurately declare the shared_ptr class, because Boost is incredibly non-simple with its compiler compensation and template tricks.

As to a solution: is it absolutely necessary to hand out shared pointers? SWIG's C++ wrappers basically function as shared pointers. Boost and SWIG are very, very difficult to get to work together.

like image 33
Seth Johnson Avatar answered Oct 19 '22 12:10

Seth Johnson