如何使用SWIG处理unique_ptr [英] How to handle unique_ptr's with SWIG
问题描述
我有一个实现发布-订阅模式的 EventDispatcher
类.它的界面看起来像这样(简化):
I have an EventDispatcher
class that implements the publish-subscribe pattern. It's interface looks something like this (simplified):
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文档指出:
I want to expose this class to Python. The latest SWIG documentation states that:
没有可用于std :: weak_ptr的特殊智能指针处理还有std :: unique_ptr.
There is no special smart pointer handling available for std::weak_ptr and std::unique_ptr yet.
我很想至少能够继续在c ++端使用unique_ptr.我有什么选择?
I would quite like to at least be able to continue using unique_ptr's on the c++ side. What are my options?
我考虑过使用SWIG的%extend功能扩展类,但是我无法使用此方法访问私有成员( m_subscriptions
).
I considered extending the class using SWIG's %extend feature, but I am unable to access private members (m_subscriptions
) using this method.
我看到的唯一另一个选择是使用SWIG预处理器定义其他方法 swig_publish
和 swig_subscribe
,但这会使我的界面文件杂乱无章.
The only other option I can see is to use the SWIG preprocessor to define extra methods, swig_publish
and swig_subscribe
, but this clutters my interface file.
推荐答案
使用简而言之,如果有 operator->
,那么SWIG会将pointee的成员合并到指针中,以使它们可以在目标语言中长时间互换使用.
In short if there's an operator->
then SWIG has merged the members of the pointee into the pointer to allow them to be used interchangeably within the the target language for a long time.
我已经使用下面的示例hader文件test.hh整理了一个完整的示例,说明了该方法如何为您工作:
I've put together a complete example of how this might work for you, using the example hader file test.hh below:
#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:
In order to use the unique_ptr sensibly inside Python I had to write the following SWIG file, 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中确切想要的语义来添加或删除构造函数,我在这里忽略了自定义删除器).
Which includes enough of a subset of the definition of std::unique_ptr
to be useful. (You can add or remove constructors depending on exactly what semantics you want within Python, I overlooked the custom deleters here).
它还添加了一个宏 wrap_unique_ptr
来设置支持.当按值返回时,类型映射只是强制SWIG的生成代码使用move构造函数而不是copy构造函数.
It also adds a macro wrap_unique_ptr
that sets up the support. The typemap just forces SWIG's generated code to use the move constructor instead of the copy constructor when returning by value.
我们可以通过以下方式使用它:
We can use it in the following way:
%module test
%{
#include "test.hh"
%}
%include "std_unique_ptr.i"
wrap_unique_ptr(FooUniquePtr, Foobar);
%include "test.hh"
我用以下方法构建了这个容器:
I built this with:
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:
Which allows us to use the following 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内部的借入指针,如您所愿.
Notice that despite being a unique_ptr<Foobar>
we can still say a.baz()
and a.wibble
. The release()
method also returns a usable 'raw' pointer, which is owned by Python now (since otherwise it wouldn't have an owner). get()
returns a borrowed pointer inside Python as you'd expect.
取决于您打算如何使用指针,这可能是您自己的类型映射和清理程序的一个好开始,而不是随处都有的%extend
和 release()
unique_ptrs.
Depending on quite how you plan to use the pointers this is probably a good start for your own typemaps and cleaner than a %extend
and release()
everywhere you have unique_ptrs.
与%shared_ptr
相比,它不会修改typemap中的in,并且不会像shared_ptr支持那样更改构造函数.您有责任选择在Python内原始指针何时仍变为unique_ptrs.
Compared to %shared_ptr
, this doesn't modify the in typemaps and it doesn't change the constructors in the same way the shared_ptr support would. It's your responsibility to choose when raw pointers become unique_ptrs within Python still.
我为使用 std :: weak_ptr
和SWIG 写了类似的答案
这篇关于如何使用SWIG处理unique_ptr的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!