如何使用 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_publish
和 swig_subscribe
,但这会使我的接口文件变得混乱。
【问题讨论】:
【参考方案1】:尽管在 C++11 注释中指出缺乏支持,但在 SWIG 中使用 generic smart pointer support 可以做很多有用的事情。
简而言之,如果有 operator->
,那么 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<Foobar>
,我们仍然可以说a.baz()
和a.wibble
。 release()
方法还返回一个可用的“原始”指针,它现在归 Python 所有(否则它就没有所有者)。 get()
在 Python 中返回一个借用的指针,正如你所期望的那样。
取决于您计划如何使用指针,这对于您自己的类型映射可能是一个良好的开端,并且比 %extend
和 release()
在您拥有 unique_ptrs 的任何地方都更干净。
与%shared_ptr
相比,这不会修改 in 类型映射,也不会像 shared_ptr 支持那样更改构造函数。你有责任选择原始指针何时成为 Python 中的 unique_ptrs。
不久前我为using std::weak_ptr
with SWIG 写了一个类似的答案。
【讨论】:
可以轻松添加以增强此功能的其中一个内容是Type*
/Type&
的 in 类型映射,它可以处理 unique_ptr 或真实指针。
有趣。我同意这比使用附加函数来处理 unique_ptr 和原始指针之间的转换来混淆接口文件要干净得多。它还显示出明确的所有权意图。谢谢你的详细回答。
注意:我的班级有一个私人std::unique_ptr<RealImpl> 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的主要内容,如果未能解决你的问题,请参考以下文章