2

I'm trying to build a python interface to a C++ class I have, which unfortunately contains a mixture of both raw and shared pointer objects / return methods. Essentially, with this interface, I'm stuck with certain functions that return raw pointers due to design constraints (i.e., the data structure defines a descending hierarchy, i.e., A -> B -> C, with shared pointers moving down, and raw pointers pointing back up, in order to break cyclic ownership.)

The problem with this that I am observing however is that when I introduce a shared ptr template, i.e.:

%shared_ptr(fooType)

Then SWIG knows to wrap / create proxy classes for fooType, but then not for fooType*. (Conversely, if I don't include the %shared_ptr macro, SWIG does create the proxy classes for the raw pointer.)

My problem comes into play as follows - I need to return a vector of raw pointer objects. So for example, here is my MWE:

fooType.h

#include <iostream>
#include <vector>

class fooType{
  public:
  fooType() { };
  ~fooType() { };
  void printFoo() { std::cerr << "FOO!" << std::endl; }
  static std::shared_ptr<fooType> make_shared() { 
      return std::shared_ptr<fooType>(new fooType()); 
  }
  static fooType* newPtr() { return new fooType(); }
  static std::vector<fooType*> newVecPtr() {
      std::vector<fooType*> retVec;
      for( size_t i = 0; i < 3; ++i) { retVec.push_back(new fooType()); }
     return retVec;
  }
};

fooType.i

%module fooType

%include <std_map.i>
%include <std_shared_ptr.i>
%include <std_vector.i>

%{
#include "fooType.h"
%}

%shared_ptr(fooType);
%include "fooType.h"

%template(fooVec) std::vector<fooType>;
%template(fooPtrVec) std::vector<fooType*>;

testFooType.py

import fooType as fooMod

ft = fooMod.fooType.make_shared()
ftPtr = fooMod.fooType.newPtr()
fooVec = fooMod.fooType.newVecPtr()

print(ftPtr)
print(ft)
print(fooVec)

for foo in fooVec:
   print(foo)
   foo.printFoo()

This gives me:

<fooType.fooType; proxy of <Swig Object of type 'std::shared_ptr< fooType > *' at 0x7fd83ccfeb10> >
<fooType.fooType; proxy of <Swig Object of type 'std::shared_ptr< fooType > *' at 0x7fd83ccfec30> >
<fooType.fooPtrVec; proxy of <Swig Object of type 'std::vector< fooType * > *' at 0x7fd83ccfeba0> >
<Swig Object of type 'fooType *' at 0x7fd834c1dba0>
Traceback (most recent call last):
  File "testFooType.py", line 13, in <module>
    foo.printFoo()
AttributeError: 'SwigPyObject' object has no attribute 'printFoo'

If I comment out the %shared_ptr(fooType) macro, I get:

<fooType.fooType; proxy of <Swig Object of type 'std::vector< fooType * >::value_type' at 0x7feab54b42a0> >
<fooType.fooTypePtr; proxy of <Swig Object of type 'std::shared_ptr< fooType > *' at 0x7feab54b4240> >
<fooType.fooPtrVec; proxy of <Swig Object of type 'std::vector< fooType * > *' at 0x7feab54b4210> >
<fooType.fooType; proxy of <Swig Object of type 'std::vector< fooType * >::value_type' at 0x7feab54b4780> >
FOO!
<fooType.fooType; proxy of <Swig Object of type 'std::vector< fooType * >::value_type' at 0x7feab54b4c90> >
FOO!
<fooType.fooType; proxy of <Swig Object of type 'std::vector< fooType * >::value_type' at 0x7feab54b4780> >
FOO!

So, what am I doing incorrectly here? In other words, how can I get SWIG to produce a proxy class for my raw pointers and my shared pointers simultaneously, especially when I'm wrapping them into a vector?

Note that I don't necessarily see a way to use something like std::enabled_shared_from_this as illustrated here, namely because I'm stuck with the interface I have on the C++ side (i.e., I can't modify the class to add an inheritance from enable_shared_from_this).

Mostly, I'd like to know why the proxy class generation gets clobbered when I enable shared pointers and how I might work around that. (I've tried developing my own typemap to parse out the pointers and put them back into a list, but this doesn't seem to help; I still end up with SWIG Objects, which I can't deference.)

Professor Nuke
  • 363
  • 2
  • 11

1 Answers1

0

In the end, I couldn't determine a suitable way to get SWIG to create a proxy type for the raw pointer when the shared pointer is being wrapped (i.e., I have a %shared_pointer(fooType) macro enabled).

The workaround I ended up coming with was to clone the raw pointer into a new shared pointer object via a wrapper method in SWIG (taking some inspiration from this answer here). In other words, using the implicit copy constructor (fooType::fooType(fooType&)) with std::make_shared to create a new shared_ptr<fooType> object which doesn't try to claim ownership of the original raw pointer (which it doesn't control; thus avoiding a double delete when the shared pointer is deleted and attempts to free its pointer.)

If anyone has any suggestions as to how to develop an appropriate typemap for the raw pointer such to get SWIG to make a proxy class for both (i.e., to allow me to dereference / call methods on the raw pointer object as well as the shared pointer), I'm certainly interested; but in the meantime, it seems like creating a parallel shared pointer is the only viable workaround.

Community
  • 1
  • 1
Professor Nuke
  • 363
  • 2
  • 11