如何使用SWIG处理unique_ptr


问题内容

我有一个EventDispatcher实现发布-订阅模式的类。它的界面看起来像这样(简化):

class EventDispatcher
{
public:
    void publish(const std::string& event_name, std::unique_ptr<Event> event);

    std::unique_ptr<Subscription> subscribe(const std::string& event_name, std::unique_ptr<Callback> callback);

private:
    std::unordered_map<std::string, std::vector<std::unique_ptr<Callback>>> m_subscriptions;
}

我想将此类公开给Python。最新的SWIG文档指出:

还没有可用于std :: weak_ptr和std :: unique_ptr的特殊智能指针处理。

我很想至少能够继续在c ++端使用unique_ptr。我有什么选择?

我考虑过使用SWIG的%extend功能扩展该类,但是我无法m_subscriptions使用此方法访问私有成员()。

我可以看到的唯一其他选择是使用SWIG预处理器定义其他方法,swig_publishswig_subscribe,但这会使我的接口文件混乱。


问题答案:

尽管在C ++
11注释中指出缺乏支持,但是使用SWIG中的通用智能指针支持还有很多工作要做的范围。

简而言之,如果有一个operator->SWIG ,则SWIG会将pointe的成员合并到指针中,以使它们可以在目标语言中长时间互换使用。

我使用下面的示例性hader文件test.hh整理了一个完整的示例,说明了该方法如何为您服务:

#include <memory>
#include <iostream>

struct Foobar {
  void baz() { std::cout << "This works\n"; }
  int wibble;
};

std::unique_ptr<Foobar> make_example() { 
  return std::unique_ptr<Foobar>(new Foobar); 
}

void dump_example(const std::unique_ptr<Foobar>& in) {
  std::cout << in->wibble << "\n";
  in->baz();
}

为了在Python内合理地使用unique_ptr,我不得不编写以下SWIG文件std_unique_ptr.i:

namespace std {
  %feature("novaluewrapper") unique_ptr;
  template <typename Type>
  struct unique_ptr {
     typedef Type* pointer;

     explicit unique_ptr( pointer Ptr );
     unique_ptr (unique_ptr&& Right);
     template<class Type2, Class Del2> unique_ptr( unique_ptr<Type2, Del2>&& Right );
     unique_ptr( const unique_ptr& Right) = delete;


     pointer operator-> () const;
     pointer release ();
     void reset (pointer __p=pointer());
     void swap (unique_ptr &__u);
     pointer get () const;
     operator bool () const;

     ~unique_ptr();
  };
}

%define wrap_unique_ptr(Name, Type)
  %template(Name) std::unique_ptr<Type>;
  %newobject std::unique_ptr<Type>::release;

  %typemap(out) std::unique_ptr<Type> %{
    $result = SWIG_NewPointerObj(new $1_ltype(std::move($1)), $&1_descriptor, SWIG_POINTER_OWN);
  %}

%enddef

其中包括足够std::unique_ptr有用的定义子集。(您可以根据在Python中确切想要的语义来添加或删除构造函数,我在这里忽略了自定义删除器)。

它还添加了一个宏wrap_unique_ptr来设置支持。当按值返回时,类型映射仅强制SWIG的生成代码使用move构造函数而不是copy构造函数。

我们可以通过以下方式使用它:

%module test

%{
#include "test.hh"
%}

%include "std_unique_ptr.i"

wrap_unique_ptr(FooUniquePtr, Foobar);

%include "test.hh"

我用以下方法构建了这个:

swig3.0 -py3 -c++ -python -Wall test.i 
g++ -Wall -Wextra -Wno-missing-field-initializers test_wrap.cxx -std=c++11 -I/usr/include/python3.4/ -lpython3.4m -shared -o _test.so

这使我们可以使用以下Python:

from test import *

a = make_example()

print(a)
a.wibble = 1234567
a.baz()

dump_example(a)

a.baz()

print(bool(a))
print(bool(FooUniquePtr(None)))

b=a.release()
print(b)

请注意,尽管是a,unique_ptr<Foobar>我们仍然可以说a.baz()a.wibble。该release()方法还返回一个可用的“原始”指针,该指针现在由Python拥有(因为否则它将没有拥有者)。get()如您所料,在Python内部返回借入的指针。

根据您计划使用指针的方式,这可能是您自己的类型映射和清洁程序的一个不错的开始,而不是a%extendrelease()任何您拥有unique_ptrs的地方。

与相比%shared_ptr,这不会修改typemap中的in,也不会像shared_ptr支持那样更改构造函数。您有责任选择何时在Python中原始指针何时仍变为unique_ptrs。

不久前,我为使用std::weak_ptrSWIG编写了类似的答案。