Search code examples
c++pythontemplatesswigshared-ptr

SWIG_SHARED_PTR macro with templated class


I'm using SWIG with boost shared pointers to create python extensions. My current issue is that the SWIG_SHARED_PTR macro seems to work differently with templated classes. I'll give two examples, one without templates (example), and one with templates (example2).

First I'll include the code, and at the end show the difference in behavior of the two extensions within python.

The basic problem is that without templates the shared pointers appear in python as

<example.Derived; proxy of <Swig Object of type 'derived_sptr *' at 0xb772c4b8> >

whereas when the wrapped class is an instantiated template the appear as

<Swig Object of type 'derived_int_sptr *' at 0xb787e4b8>

I'm new to C++ templates so it's quite possible the problems I'm having have nothing to do with SWIG.

Without Templates (works fine)

example.h

#include <boost/shared_ptr.hpp>                                                                                                

#ifndef EXAMPLE_H                                                                                                              
#define EXAMPLE_H                                                                                                              

class Base {                                                                                                                   
 public:                                                                                                                       
  Base(int number1);                                                                                                           
  int get_number1();                                                                                                           
  virtual int get_number2() = 0;                                                                                               
 protected:                                                                                                                    
  int number1;                                                                                                                 
};                                                                                                                             

class Derived : public Base {                                                                                                  
 public:                                                                                                                       
  Derived(int number1, int number2);                                                                                           
  int get_number2();                                                                                                           
 private:                                                                                                                      
  int number2;                                                                                                                 
};                                                                                                                             

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

derived_sptr make_derived(int number1, int number2);                                                                           

int get_number1(base_sptr b);                                                                                                  

#endif

example.cc

#include "example.h"                                                                                                       

Base::Base(int number1) :                                                                                                  
  number1(number1)                                                                                                         
{}                                                                                                                         

int Base::get_number1() {                                                                                                  
  return number1;                                                                                                          
}                                                                                                                          

Derived::Derived(int number1, int number2) :                                                                               
  Base(number1),                                                                                                           
  number2(number2)                                                                                                         
{}                                                                                                                         

int Derived::get_number2() {                                                                                               
  return number2;                                                                                                          
}                                                                                                                          

derived_sptr make_derived(int number1, int number2) {                                                                      
  return derived_sptr(new Derived(number1, number2));                                                                      
}                                                                                                                          

int get_number1(base_sptr b) {                                                                                             
  return b->get_number1();                                                                                                 
}

example.i

%module example                                                                                                            
%{                                                                                                                         
  #include "example.h"                                                                                                     
%}                                                                                                                         

%include boost_shared_ptr.i                                                                                                

SWIG_SHARED_PTR(base_sptr, Base)                                                                                           
SWIG_SHARED_PTR_DERIVED(derived_sptr, Base, Derived)                                                                       

%include example.h                                                                                                         

With Templates (doesn't work)

example2.h

#include <boost/shared_ptr.hpp>                                                                                            

#ifndef EXAMPLE2_H                                                                                                          
#define EXAMPLE2_H                                                                                                          

template <typename T>                                                                                                      
class Base {                                                                                                               
 public:                                                                                                                   
  Base(T number1);                                                                                                         
  T get_number1();                                                                                                         
  virtual T get_number2() = 0;                                                                                             
 protected:                                                                                                                
  T number1;                                                                                                               
};                                                                                                                         

template <typename T>                                                                                                      
class Derived : public Base<T> {                                                                                           
 public:                                                                                                                   
  Derived(T number1, T number2);                                                                                           
  T get_number2();                                                                                                         
 private:                                                                                                                  
  T number2;                                                                                                               
};                                                                                                                         

typedef Base<int> base_int;                                                                                                
typedef Derived<int> derived_int;                                                                                          

typedef boost::shared_ptr<base_int> base_int_sptr;                                                                         
typedef boost::shared_ptr<derived_int> derived_int_sptr;                                                                   

derived_int_sptr make_derived_int(int number1, int number2);                                                               

int get_number1_int(base_int_sptr b);          

#endif

example2.cc

#include "example2.h"                                                                                                       

template <typename T>                                                                                                      
Base<T>::Base(T number1) :                                                                                                 
  number1(number1)                                                                                                         
{}                                                                                                                         

template <typename T>                                                                                                      
T Base<T>::get_number1() {                                                                                                 
  return number1;                                                                                                          
}                                                                                                                          

template <typename T>                                                                                                      
Derived<T>::Derived(T number1, T number2) :                                                                                
  Base<T>(number1),                                                                                                        
  number2(number2)                                                                                                         
{}                                                                                                                         

template <typename T>                                                                                                      
T Derived<T>::get_number2() {                                                                                              
  return number2;                                                                                                          
}                                                                                                                          

template class Base<int>;                                                                                                  
template class Derived<int>;                                                                                               

derived_int_sptr make_derived_int(int number1, int number2) {                                                              
  return derived_int_sptr(new Derived<int>(number1, number2));                                                             
}                                                                                                                          

int get_number1_int(base_int_sptr b) {                                                                                     
  return b->get_number1();                                                                                                 
} 

example2.i

%module example2                                                                                                            
%{                                                                                                                         
  #include "example2.h"                                                                                                     
%}                                                                                                                         

%include boost_shared_ptr.i                                                                                                

SWIG_SHARED_PTR(base_int_sptr, base_int)                                                                                   
SWIG_SHARED_PTR_DERIVED(derived_int_sptr, base_int, derived_int)                                                           

%include example.h                                                                                                         

%template(base_int) Base<int>;                                                                                             
%template(derived_int) Derived<int>;                                                                                       

Example of Difference:

> import example
> d = example.make_derived(4, 5)
> d
> <example.Derived; proxy of <Swig Object of type 'derived_sptr *' at 0xb77327a0> >
> d.get_number1()
4
> example.get_number1(d)
4
> import example2
> d = example2.make_derived_int(4, 5)
> d
<Swig Object of type 'derived_int_sptr *' at 0xb787e4b8>
> d.get_number1()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'SwigPyObject' object has no attribute 'get_number1'
> example.get_number1_int(d)
4

Solution

  • In example2.i the lines

    SWIG_SHARED_PTR(base_int_sptr, base_int) 
    SWIG_SHARED_PTR_DERIVED(derived_int_sptr, base_int, derived_int)
    

    should be replaced by

    SWIG_SHARED_PTR(base_int_sptr, Base<int>)                              
    SWIG_SHARED_PTR_DERIVED(derived_int_sptr, Base<int>, Derived<int>)