如何使用 SWIG 处理 unique_ptr

Posted

技术标签:

【中文标题】如何使用 SWIG 处理 unique_ptr【英文标题】:How to handle unique_ptr's with SWIG 【发布时间】:2014-12-29 18:05:12 【问题描述】:

我有一个实现发布-订阅模式的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,但这会使我的接口文件变得混乱。

【问题讨论】:

【参考方案1】:

尽管在 C++11 注释中指出缺乏支持,但在 SWIG 中使用 generic smart pointer support 可以做很多有用的事情。

简而言之,如果有 operator-&gt;,那么 SWIG 已将指针对象的成员合并到指针中,以允许它们在目标语言中长期互换使用。

我已经使用下面的示例 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 生成的代码在按值返回时使用移动构造函数而不是复制构造函数。

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

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

请注意,尽管是unique_ptr&lt;Foobar&gt;,我们仍然可以说a.baz()a.wibblerelease() 方法还返回一个可用的“原始”指针,它现在归 Python 所有(否则它就没有所有者)。 get() 在 Python 中返回一个借用的指针,正如你所期望的那样。

取决于您计划如何使用指针,这对于您自己的类型映射可能是一个良好的开端,并且比 %extendrelease() 在您拥有 unique_ptrs 的任何地方都更干净。

%shared_ptr 相比,这不会修改 in 类型映射,也不会像 shared_ptr 支持那样更改构造函数。你有责任选择原始指针何时成为 Python 中的 unique_ptrs。

不久前我为using std::weak_ptr with SWIG 写了一个类似的答案。

【讨论】:

可以轻松添加以增强此功能的其中一个内容是 Type*/Type&amp; 的 in 类型映射,它可以处理 unique_ptr 或真实指针。 有趣。我同意这比使用附加函数来处理 unique_ptr 和原始指针之间的转换来混淆接口文件要干净得多。它还显示出明确的所有权意图。谢谢你的详细回答。 注意:我的班级有一个私人std::unique_ptr&lt;RealImpl&gt; pImpl,在这种情况下,我必须包含任何wrap_unique_ptr(RealImplUniquePtr, RealImpl)(这会导致来自@987654345 的关于incomplete type 的错误@),只需包装从公共 API 中完全可用的类型。【参考方案2】:

奇怪的是,似乎有可能 %ignore 一个函数 %extend 类定义被忽略函数的替代实现,并最终从该函数的替代实现中调用最初被忽略的函数。例如:

%ignore EventDispatcher::subscribe(const std::string&, std::unique_ptr<Callback>);

%include "EventDispatcher.hpp"

%extend suborbital::EventDispatcher

    EventSubscription* EventDispatcher::subscribe(const std::string& event_name, PyObject* callback)
    
        std::unique_ptr<Callback> callback_ptr(new Callback(callback));
        return $self->subscribe(event_name, std::move(callback_ptr)).release();
    

【讨论】:

你可以不调用%ignore直接这样做,只要你不在.i文件中的原始函数原型中暴露(声明)【参考方案3】:

目前尚不支持 unique_ptr。 http://www.swig.org/Doc3.0/CPlusPlus11.html

您需要使用智能指针,如下所示: http://www.swig.org/Doc3.0/Library.html#Library_std_shared_ptr

【讨论】:

我真的不想使用不同类型的智能指针。我感兴趣的是如何继续在 c++ 端使用 unique_ptr,但向 Python 公开使用原始指针的不同函数。

以上是关于如何使用 SWIG 处理 unique_ptr的主要内容,如果未能解决你的问题,请参考以下文章

如何编写一个函数来接受 SWIG 中的 Fraction 对象?

SWIG C++ TCL:处理内存中预先存在的对象

如何在 Perl 中使用 SWIG?

如何使用 SWIG 从 C++ 调用 Java?

如何使用 SWIG 枚举枚举成员

如何使用 SWIG 从 C 调用 C# 方法?