2014-12-29 8 views
5

Ich habe eine EventDispatcher Klasse, die das Publish-Subscribe-Muster implementiert. Die Benutzeroberfläche sieht in etwa so aus (vereinfacht):Umgang mit unique_ptrs mit 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; 
} 

Ich möchte diese Klasse für Python verfügbar machen. Die neueste SWIG Dokumentation besagt, dass:

Es gibt keinen speziellen Smart-Pointer ist für std :: verfügbar Handhabung weak_ptr und std :: unique_ptr noch.

Ich würde gerne zumindest weiterhin unique_ptr auf der C++ Seite verwenden können. Was sind meine Möglichkeiten?

Ich erwog Erweiterung der Klasse mit SWIFT% Extend Feature, aber ich kann nicht auf private Mitglieder (m_subscriptions) mit dieser Methode zugreifen.

Die einzige andere Option, die ich sehen kann, besteht darin, den SWIG-Präprozessor zu verwenden, um zusätzliche Methoden zu definieren, swig_publish und swig_subscribe, aber das überfüllt meine Interface-Datei.

Antwort

10

Es ist ziemlich viel Spielraum nützliche Dinge zu tun, um die generic smart pointer support in SWIG mit, trotz der erwähnten Mangel an Unterstützung in den C++ 11 Noten.

Kurz gesagt, wenn es eine operator-> gibt, hat SWIG die Mitglieder des pointee in den Zeiger zusammengeführt, damit sie für eine lange Zeit austauschbar innerhalb der Zielsprache verwendet werden können.

Ich habe ein vollständiges Beispiel zusammen, wie dies für Sie arbeiten könnte, unter dem Beispiel hader Datei test.hh mit:

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

Um die unique_ptr vernünftig innerhalb Python zu verwenden, schreibe ich musste die folgende SWIG Datei, 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 

Welche genug von einer Teilmenge der Definition von std::unique_ptr enthält, nützlich sein. (Sie können Konstruktoren hinzufügen oder entfernen, je nachdem, welche Semantik Sie in Python haben wollen, ich habe die benutzerdefinierten Deleters hier übersehen).

Es fügt auch ein Makro wrap_unique_ptr hinzu, das die Unterstützung einrichtet. Die Typenkarte erzwingt nur, dass der generierte SWIG-Code bei der Rückgabe nach Wert den Verschiebungskonstruktor anstelle des Kopierkonstruktors verwendet.

Wir können es auf folgende Weise verwenden:

%module test 

%{ 
#include "test.hh" 
%} 

%include "std_unique_ptr.i" 

wrap_unique_ptr(FooUniquePtr, Foobar); 

%include "test.hh" 

Ich baute diese mit:

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 

Was uns die folgende Python verwenden können:

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) 

Beachten Sie, dass Obwohl wir ein unique_ptr<Foobar> sind, können wir immer noch a.baz() und a.wibble sagen. Die Methode release() gibt auch einen verwendbaren 'rohen' Zeiger zurück, der jetzt Python gehört (da er sonst keinen Eigentümer hätte). get() gibt wie erwartet einen ausgeliehenen Zeiger in Python zurück.

Je nachdem, wie Sie die Zeiger verwenden möchten, ist dies wahrscheinlich ein guter Anfang für Ihre eigenen Typmaps und sauberer als ein %extend und release() überall wo Sie unique_ptrs haben.

Im Vergleich zu %shared_ptr ändert dies nicht die in typesmaps und es ändert nicht die Konstruktoren in der gleichen Weise die shared_ptr Unterstützung würde. Es liegt in Ihrer Verantwortung zu wählen, wann rohe Zeiger in Python immer noch unique_ptrs werden.

Ich schrieb eine ähnliche Antwort für using std::weak_ptr with SWIG eine Weile zurück.

+0

Eines der Dinge, die einfach hinzugefügt werden könnten, wäre in typemap für 'Type *'/'Type &', das entweder mit einem unique_ptr oder einem echten Zeiger umgehen kann. – Flexo

+0

Interessant. Ich stimme zu, dass dies viel sauberer ist, als die Interface-Dateien mit zusätzlichen Funktionen zu überfrachten, um Konvertierungen zwischen unique_ptrs und rohen Pointern zu handhaben. Es zeigt auch klare Eigentumsabsicht. Danke für die ausführliche Antwort. – Homar

+0

Anmerkung: Meine Klasse hatte eine private 'std :: unique_ptr pImpl', in diesem Fall musste ich ** nicht ** irgendwelche' wrap_unique_ptr (RealImplUniquePtr, RealImpl) 'einschließen (das würde Fehler über 'unvollständiger Typ' von' geben default_delete'), umschließen Sie einfach die Typen, die von der öffentlichen API vollständig verfügbar waren. – unhammer

-2

Es gibt noch keine Unterstützung für unique_ptr. http://www.swig.org/Doc3.0/CPlusPlus11.html

Sie benötigen intelligente Zeiger wie folgt verwenden: http://www.swig.org/Doc3.0/Library.html#Library_std_shared_ptr

+0

Ich möchte nicht wirklich eine andere Art von Smart-Pointer verwenden. Ich bin daran interessiert, wie ich weiterhin unique_ptrs auf der C++ Seite verwenden kann, aber verschiedene Funktionen zu Python offen legen, die rohe Zeiger verwenden. – Homar

2

Seltsamerweise scheint es, dass es zu %ignore möglich ist, eine Funktion, %extend die Klasse eine alternative Implementierung der ignoriert Funktion zu definieren, und die schließlich nennen anfänglich ignorierte Funktion innerhalb der alternativen Implementierung dieser Funktion. Zum Beispiel:

%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

Sie können dies direkt tun, ohne% ignore aufzurufen, solange Sie nicht im ursprünglichen Funktionsprototyp in der .i-Datei angeben (deklarieren). – n00shie