How to handle unique_ptr with SWIG

I have an EventDispatcher class that implements a publish-subscribe template. The 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; } 

I want to expose this class in Python. The latest SWIG documentation states that:

There is no special handling of smart pointers for std :: weak_ptr and std :: unique_ptr.

I would really like to at least continue using unique_ptr on the C ++ side. What are my options?

I was considering expanding a class using the SWIG% extension function, but I cannot access private members ( m_subscriptions ) using this method.

The only thing I see is to use the SWIG preprocessor to define the additional methods swig_publish and swig_subscribe , but it swig_subscribe my interface file.

+5
source share
3 answers

There are quite a few possibilities in SWIG for using useful things, using the general support for smart pointers in SWIG, despite the lack of support in C ++ 11 notes noted.

In short, if there is an operator-> , then SWIG combined the members of the pointer into a pointer to allow them to be used for a long time within the target language.

I put together a complete example of how this might work for you using the hader test.hh example file 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(); } 

To use unique_ptr intelligently 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 

which includes a sufficient number of subsets of std::unique_ptr . (You can add or remove constructors depending on what kind of semantics you want to use in Python, I did not pay attention to custom removers here).

He also adds the wrap_unique_ptr macro, which installs support. A sample map simply forces the generated SWIG code to use the move constructor instead of the copy constructor when returning by value.

We can use it as follows:

 %module test %{ #include "test.hh" %} %include "std_unique_ptr.i" wrap_unique_ptr(FooUniquePtr, Foobar); %include "test.hh" 

I built it 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 

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

Note that despite unique_ptr<Foobar> , we can still say a.baz() and a.wibble . The release() method also returns a useful "raw" pointer, which is now owned by Python (since otherwise it would not have an owner). get() returns a borrowed pointer inside Python, as you expected.

Depending on how you plan to use pointers, this is probably a good start for your own types and cleanup than %extend and release() wherever you have unique_ptrs.

Compared to %shared_ptr this does not change in typemaps and does not change the constructors in the same way as shared_ptr support. It is your responsibility to choose when raw pointers become unique_ptrs inside Python.

I wrote a similar answer using std::weak_ptr with SWIG some time ago.

+10
source

Unusually, it seems that a %ignore function, %extend class is possible to define an alternative implementation of an ignored function and, finally, to call an initially ignored function from an alternative implementation of this function. For instance:

 %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(); } } 
+2
source

There is no support for unique_ptr yet. http://www.swig.org/Doc3.0/CPlusPlus11.html

You need to use smart pointers as shown below: http://www.swig.org/Doc3.0/Library.html#Library_std_shared_ptr

-2
source

Source: https://habr.com/ru/post/1210063/


All Articles