2014-12-29 5 views
5

У меня есть класс EventDispatcher, который реализует шаблон публикации-подписки. Интерфейс выглядит примерно так:Как обрабатывать unique_ptr's с SWIG

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 гласит:

Там нет специального смарта-указателя обработки для станд :: weak_ptr и станда :: unique_ptr еще.

Мне бы очень хотелось по крайней мере продолжить использование unique_ptr на стороне C++. Какие у меня варианты?

Я рассматривал возможность расширения класса с использованием функции% расширения SWIG, но я не могу получить доступ к закрытым членам (m_subscriptions), используя этот метод.

Единственный другой вариант, который я вижу, - использовать препроцессор SWIG для определения дополнительных методов, swig_publish и swig_subscribe, но это загромождает мой файл интерфейса.

ответ

10

Там довольно много возможностей, чтобы сделать полезные вещи, используя generic smart pointer support в SWIG, несмотря на отметили отсутствие поддержки в примечаниях C++ 11.

Вкратце, если есть 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(); 
} 

Для того, чтобы использовать unique_ptr благоразумно внутри Python я должен был написать следующий 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, это не изменяет в typemaps и не изменяет конструкторы таким же образом, как и поддержка shared_ptr. Вы несете ответственность за выбор, когда raw-указатели становятся уникальными в Python.

Я написал аналогичный ответ за using std::weak_ptr with SWIG некоторое время назад.

+0

. Одна из вещей, которую было бы легко добавить, чтобы улучшить это, будет в печатной карте для типа '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' или '' – Flexo

+0

Интересно. Я согласен с тем, что это намного чище, чем мутирование файлов интерфейса с дополнительными функциями для обработки конверсий между уникальными указателями unique_ptr и raw. Он также показывает явное намерение собственности. Спасибо за подробный ответ. – Homar

+0

Примечание: у моего класса был приватный 'std :: unique_ptr pImpl', и в этом случае я должен был ** не ** включать какие-либо' wrap_unique_ptr (RealImplUniquePtr, RealImpl) '(что бы приводило к ошибкам о' неполном типе 'от ' default_delete'), просто оберните типы, которые были полностью доступны из общедоступного API. – unhammer

-2

Пока нет поддержки unique_ptr. http://www.swig.org/Doc3.0/CPlusPlus11.html

Вы должны использовать смарт-указатели следующим образом: http://www.swig.org/Doc3.0/Library.html#Library_std_shared_ptr

+0

Я действительно не хочу использовать другой тип умного указателя. Меня интересует, как я могу продолжать использовать unique_ptr на стороне C++, но выставлять на Python различные функции, которые используют raw-указатели. – Homar

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(); 
    } 
} 
+0

Вы можете сделать это напрямую, не вызывая% ignore, пока вы не выставляете (объявлять) в прототипе оригинальной функции в файле .i – n00shie

Смежные вопросы