如何使用SWIG处理unique_ptr

How to handle unique_ptr's with SWIG

本文关键字:ptr unique 处理 何使用 SWIG      更新时间:2023-10-16

我有一个实现发布-订阅模式的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 和标准::unique_ptr还没有。

我非常希望至少能够在 c++ 端继续使用 unique_ptr。我有哪些选择?

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

我能看到的唯一其他选择是使用 SWIG 预处理器来定义额外的方法,swig_publishswig_subscribe,但这会使我的界面文件混乱。

尽管 C++11 注释中指出缺乏支持,但使用 SWIG 中的通用智能指针支持做有用的事情的空间很大。

简而言之,如果有operator->那么SWIG将pointee的成员合并到指针中,以允许它们在目标语言中

互换使用很长时间。

我整理了一个完整的示例,说明这可能如何为您工作,使用下面的示例 hader file test.hh:

#include <memory>
#include <iostream>
struct Foobar {
  void baz() { std::cout << "This worksn"; }
  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<Foobar>但我们仍然可以说 a.baz()a.wibble .release() 方法还返回一个可用的"原始"指针,该指针现在归 Python 所有(因为否则它就没有所有者)。 get()如您所期望的那样在 Python 中返回一个借用的指针。

根据您计划如何使用指针,对于您自己的排版图和清洁器来说,这可能是一个好的开始,而不是%extendrelease()您unique_ptrs的任何地方。

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

不久前,我为将std::weak_ptr与SWIG一起使用写了一个类似的答案。

奇怪的

是,似乎可以%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();
    }
}

SWIG-4.1 添加了 std::unique_ptr 支持。https://swig.org/Doc4.1/Library.html#Library_std_unique_ptr 的文档。

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

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