SWIG:使用带有 shared_ptr 的 std::map 访问器?

Posted

技术标签:

【中文标题】SWIG:使用带有 shared_ptr 的 std::map 访问器?【英文标题】:SWIG: Using std::map accessors with a shared_ptr? 【发布时间】:2017-07-31 15:47:54 【问题描述】:

我在使用 SWIG 生成的 Python 包装器到 C++ 类时遇到了一个奇怪的问题,当它被包装为 std::shared_ptr 类型时,我似乎无法使用 std::map 的标准访问器函数。我设法制作了一个 MWE,它重现了我观察到的奇怪行为。

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; 
;

然后是我的 SWIG 接口文件:

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"

最后是我用来测试界面的测试脚本:

test_interface.py

import TestMap as tm

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

myTestMap[1] = ft

正如所写,当我尝试使用地图访问器时出现以下错误:

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 &

当我检查ftmyTestMap 的类型时,它们都是各自类的std::shared_ptr 引用:

<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> >

现在是奇怪的部分 - 如果我从 SWIG 接口文件中省略 %shared_ptr(TestMap) 声明并重新编译,地图访问器(在 test_interface.py 中)可以正常工作。当我检查myTestMap的类型时,它是:

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

那么,两个问题:

    为什么当我有一个 SWIG 对象指针引用 (testMap*) 时,我的访问器调用可以正常工作,但当我有一个 shared_ptr 引用(例如,std::shared_ptr&lt; testMap &gt; *)时却不能? 鉴于我的派生地图类型需要shared_ptr,我该如何解决?

额外问题:如果我声明 shared_ptr 类型存在 testMap 类型(即使它没有初始化),为什么 SWIG 会自动将 testMap* 转换为 std::shared_ptr&lt;testMap&gt; 类型这样吗?)

【问题讨论】:

【参考方案1】:

myTestMap = tm.testMap() 第一次创建一个透明的 shared_ptr。所以myTestMap[1] 是对shared_ptr 的透明解引用,随后将值分配给键。 第二次myTestMap = tm.testMap() 创建空的std::map,所以myTestMap[1] 是为映射的key=1 赋值。

%shared_ptr(testMap) 在语义上类似于%template(testMap) shared_ptr&lt;testMap&gt;%template(testMapPtr) shared_ptr&lt;testMap&gt; 将创建一个新的 shared_ptr 类型 testMapPtr,它最初持有 NULL(请参阅 default constructor),因此 testMapPtr[1] 将取消引用 NULL 值,从而产生一些异常。更新: %shared_ptr(testMap) 创建一个使用 testMap 默认构造函数初始化的完全透明的 shared_ptr。

【讨论】:

至于“奖金问题”:%shared_ptr(testMap)%template(testMap) shared_ptr&lt;testMap&gt; 相同。使用%template(testMapPtr) shared_ptr&lt;testMap&gt; 来避免testMap 语义的这种变化。 这个解释对我来说很有意义。我注意到的是,即使我创建了一个为 testMap 显式创建非空 shared_ptr 的方法,我也遇到了同样的问题。但是,我注意到,当我将 testMap 的定义从共享指针模板(例如,%template(testMapPtr) std::shared_ptr&lt;testMap&gt;)中分离出来时,我得到了一个(有用的)错误,即项目赋值运算符不受支持。将智能指针模板与基类,然后在我必须使用智能指针类型时使用备用函数。谢谢! 不客气,很高兴回答对您有帮助!对我的解释稍作修正:%template(testMapPtr) shared_ptr&lt;testMap&gt; 导致最初为空的testMapPtr 共享指针,但%shared_ptr(testMap) 实际上创建了一个透明的shared_ptr,它已经用testMap 实例初始化。我会将其添加到答案中。

以上是关于SWIG:使用带有 shared_ptr 的 std::map 访问器?的主要内容,如果未能解决你的问题,请参考以下文章

swig shared_ptr 导致一个不透明的对象

SWIG:如何从 SwigPyobject 获取包装的 std::shared_ptr 的值

SWIG 输入文件和带有 numpy 的向量。使用 % 应用?

如何使用 SWIG 包装带有可变参数的 C 函数

如何在带有 Swig 的 Python 中使用 float **?

带有 swig 的模块名称