Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SWIG: Using std::map accessors with a shared_ptr?

Tags:

c++

python

swig

I'm experiencing an odd issue with a SWIG-generated Python wrapper to a C++ class, wherein I cannot seem to use the standard accessor functions of std::map when it is wrapped as a std::shared_ptr type. I managed to produce a MWE that reproduces the odd behavior I am observing.

TestMap.h

#include <iostream>
#include <map>
#include <memory>

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()); 
  }
};

class testMap : public std::map<int, std::shared_ptr<fooType> > {
  public:
   void printBar() { std::cerr << "bar." << std::endl; }
};

And then my SWIG interface file:

TestMap.i

%module TestMap

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

%{
#include "TestMap.h"
%}

%shared_ptr(fooType);
%shared_ptr(testMap);
%shared_ptr(std::map<int, std::shared_ptr<fooType> > );

%template(fooMap) std::map< int, std::shared_ptr<fooType> >;

%include "TestMap.h"

Finally, the test script I'm using to test out the interface:

test_interface.py

import TestMap as tm

ft = tm.fooType.make_shared()
myTestMap = tm.testMap()

myTestMap[1] = ft

As-written, I get the following error when I try to use the map accessor:

Traceback (most recent call last):
  File "test_interface.py", line 9, in <module>
    myTestMap[1] = ft
  File "/home/sskutnik/tstSWIG/TestMap.py", line 217, in __setitem__
    return _TestMap.fooMap___setitem__(self, *args)
 NotImplementedError: Wrong number or type of arguments for overloaded function 'fooMap___setitem__'.
   Possible C/C++ prototypes are:
     std::map< int,std::shared_ptr< fooType > >::__setitem__(std::map< int,std::shared_ptr< fooType > >::key_type const &)
     std::map< int,std::shared_ptr< fooType > >::__setitem__(std::map< int,std::shared_ptr< fooType > >::key_type const &,std::map< int,std::shared_ptr< fooType > >::mapped_type const &

When I check the type of ft and myTestMap, both are std::shared_ptr references of their respective classes:

<TestMap.fooType; proxy of <Swig Object of type 'std::shared_ptr< fooType > *' at 0x7fa812e80a80> >
<TestMap.testMap; proxy of <Swig Object of type 'std::shared_ptr< testMap > *' at 0x7fa812e80c90> >

Now for the odd part - if I omit the %shared_ptr(TestMap) declaration from my SWIG interface file and recompile, the map accessor (in test_interface.py) happily works. When I check the type of myTestMap, it's:

<TestMap.testMap; proxy of <Swig Object of type 'testMap *' at 0x7f8eceb50630> >

So, two questions:

  1. Why does my accessor call work correctly when I have a SWIG object pointer reference (testMap*), but not when I have a shared_ptr reference (e.g., std::shared_ptr< testMap > *)?
  2. How do I get around this, given that I need a shared_ptr for my derived map type?

Bonus question: why does SWIG automatically convert testMap* into a std::shared_ptr<testMap> type if I declare the existence of a shared_ptr type for the testMap type (even if it's not initialized as such?)

like image 314
Professor Nuke Avatar asked Jul 31 '17 15:07

Professor Nuke


1 Answers

First time myTestMap = tm.testMap() creates an transparent shared_ptr. So myTestMap[1] is a transparent dereferencing of the shared_ptr with subsequent assignment of the value to the key.
Second time myTestMap = tm.testMap() creates empty std::map, so myTestMap[1] is assignment of a value to the the key=1 of the map.

%shared_ptr(testMap) is semantically similar to %template(testMap) shared_ptr<testMap>. %template(testMapPtr) shared_ptr<testMap> will create a new shared_ptr type testMapPtr, which holds NULL initially (see the default constructor), so testMapPtr[1] would dereference a NULL value yielding some exception.
Update: %shared_ptr(testMap) creates a fully transparent shared_ptr initialized with the testMap default constructor.

like image 87
luart Avatar answered Nov 06 '22 23:11

luart