2013-12-14 6 views
20

Ich bin ziemlich neu zu boost.python und versuchen, den Rückgabewert einer Funktion zu Python zu offenbaren.Boost.Python: Wie std :: unique_ptr

Die Funktion Signatur sieht wie folgt aus:

std::unique_ptr<Message> someFunc(const std::string &str) const; 

Wenn die Funktion in Python aufrufen, erhalte ich folgende Fehlermeldung:

TypeError: No to_python (by-value) converter found for C++ type: std::unique_ptr<Message, std::default_delete<Message> > 

in Python Meine Funktionsaufruf wie folgt aussieht:

a = mymodule.MyClass() 
a.someFunc("some string here") # error here 

Ich habe versucht, die std :: unique_ptr freizugeben, aber kann es einfach nicht funktionieren .. Weiß jemand, wie man die Zeigerklasse richtig aussetzt? Danke!

Edit: Ich habe versucht, die folgenden:

class_<std::unique_ptr<Message, std::default_delete<Message>>, bost::noncopyable ("Message", init<>()) 

; 

Dieses Beispiel kompiliert, aber ich habe immer noch die oben genannten Fehler. Auch habe ich versucht, die Klasse zu belichten Message selbst

class_<Message>("Message", init<unsigned>()) 

     .def(init<unsigned, unsigned>()) 
     .def("f", &Message::f) 
; 

Antwort

14

Kurz gesagt, unterstützt Boost.Python nicht move-semantics und unterstützt daher std::unique_ptr nicht. Boost.Pythons news/change log hat keinen Hinweis darauf, dass es für C++ 11 move-semantics aktualisiert wurde. Darüber hinaus wurde diese feature request für unique_ptr Unterstützung seit über einem Jahr nicht berührt.

Nichtsdestotrotz unterstützt Boost.Python die Übertragung der exklusiven Eigentümerschaft eines Objekts von und nach Python über std::auto_ptr.Als unique_ptr im Wesentlichen eine sicherere Version von auto_ptr ist, sollte es recht einfach sein, eine API mit unique_ptr an eine API anzupassen, die auto_ptr verwendet:

  • Wenn C++ überträgt das Eigentum an Python, die C++ Funktion muss:
  • Wenn Eigentum Python Transfers zu C++, die C++ Funktion muss:
    • die Instanz über auto_ptr akzeptieren. Die FAQ erwähnt, dass Zeiger, die von C++ mit einer manage_new_object Richtlinie zurückgegeben werden, über std::auto_ptr verwaltet werden.
    • haben auto_ptr Freigabesteuerung zu einem unique_ptr über release()

eine API/Bibliothek gegeben, die nicht geändert werden können:

/// @brief Mockup Spam class. 
struct Spam; 

/// @brief Mockup factory for Spam. 
struct SpamFactory 
{ 
    /// @brief Create Spam instances. 
    std::unique_ptr<Spam> make(const std::string&); 

    /// @brief Delete Spam instances. 
    void consume(std::unique_ptr<Spam>); 
}; 

Die SpamFactory::make() und SpamFactory::consume() gewickelt werden müssen, über Hilfsfunktionen.

Funktionen Eigentum von C++ zu Python übertragen können allgemein durch eine Funktion eingewickelt werden, die Python-Funktion Objekte erstellen wird:

/// @brief Adapter a member function that returns a unique_ptr to 
///  a python function object that returns a raw pointer but 
///  explicitly passes ownership to Python. 
template <typename T, 
      typename C, 
      typename ...Args> 
boost::python::object adapt_unique(std::unique_ptr<T> (C::*fn)(Args...)) 
{ 
    return boost::python::make_function(
     [fn](C& self, Args... args) { return (self.*fn)(args...).release(); }, 
     boost::python::return_value_policy<boost::python::manage_new_object>(), 
     boost::mpl::vector<T*, C&, Args...>() 
    ); 
} 

Die Lambda-Delegierten auf die ursprüngliche Funktion und releases() Besitz der Instanz zu Python, und Die Aufrufrichtlinie zeigt an, dass Python den vom Lambda zurückgegebenen Wert übernehmen wird. Die mpl::vector beschreibt die Anrufsignatur zu Boost.Python, die es ermöglicht, Funktionsverteilung zwischen den Sprachen ordnungsgemäß zu verwalten.

Das Ergebnis adapt_unique als SpamFactory.make() ausgesetzt:

boost::python::class_<SpamFactory>(...) 
    .def("make", adapt_unique(&SpamFactory::make)) 
    // ... 
    ; 

generisch SpamFactory::consume() Anpassung ein schwieriger, aber es ist einfach genug, um eine einfache Hilfsfunktion zu schreiben:

/// @brief Wrapper function for SpamFactory::consume_spam(). This 
///  is required because Boost.Python will pass a handle to the 
///  Spam instance as an auto_ptr that needs to be converted to 
///  convert to a unique_ptr. 
void SpamFactory_consume(
    SpamFactory& self, 
    std::auto_ptr<Spam> ptr) // Note auto_ptr provided by Boost.Python. 
{ 
    return self.consume(std::unique_ptr<Spam>{ptr.release()}); 
} 

Die Hilfsfunktion delegiert an die ursprüngliche Funktion und konvertiert das von Boost.Python bereitgestellte auto_ptr in das von der API benötigte unique_ptr.Die SpamFactory_consume Hilfsfunktion wird als SpamFactory.consume() ausgesetzt:

boost::python::class_<SpamFactory>(...) 
    // ... 
.def("consume", &SpamFactory_consume) 
; 

Hier ist ein komplettes Code-Beispiel:

#include <iostream> 
#include <memory> 
#include <boost/python.hpp> 

/// @brief Mockup Spam class. 
struct Spam 
{ 
    Spam(std::size_t x) : x(x) { std::cout << "Spam()" << std::endl; } 
    ~Spam() { std::cout << "~Spam()" << std::endl; } 
    Spam(const Spam&) = delete; 
    Spam& operator=(const Spam&) = delete; 
    std::size_t x; 
}; 

/// @brief Mockup factor for Spam. 
struct SpamFactory 
{ 
    /// @brief Create Spam instances. 
    std::unique_ptr<Spam> make(const std::string& str) 
    { 
    return std::unique_ptr<Spam>{new Spam{str.size()}}; 
    } 

    /// @brief Delete Spam instances. 
    void consume(std::unique_ptr<Spam>) {} 
}; 

/// @brief Adapter a non-member function that returns a unique_ptr to 
///  a python function object that returns a raw pointer but 
///  explicitly passes ownership to Python. 
template <typename T, 
      typename ...Args> 
boost::python::object adapt_unique(std::unique_ptr<T> (*fn)(Args...)) 
{ 
    return boost::python::make_function(
     [fn](Args... args) { return fn(args...).release(); }, 
     boost::python::return_value_policy<boost::python::manage_new_object>(), 
     boost::mpl::vector<T*, Args...>() 
    ); 
} 

/// @brief Adapter a member function that returns a unique_ptr to 
///  a python function object that returns a raw pointer but 
///  explicitly passes ownership to Python. 
template <typename T, 
      typename C, 
      typename ...Args> 
boost::python::object adapt_unique(std::unique_ptr<T> (C::*fn)(Args...)) 
{ 
    return boost::python::make_function(
     [fn](C& self, Args... args) { return (self.*fn)(args...).release(); }, 
     boost::python::return_value_policy<boost::python::manage_new_object>(), 
     boost::mpl::vector<T*, C&, Args...>() 
    ); 
} 

/// @brief Wrapper function for SpamFactory::consume(). This 
///  is required because Boost.Python will pass a handle to the 
///  Spam instance as an auto_ptr that needs to be converted to 
///  convert to a unique_ptr. 
void SpamFactory_consume(
    SpamFactory& self, 
    std::auto_ptr<Spam> ptr) // Note auto_ptr provided by Boost.Python. 
{ 
    return self.consume(std::unique_ptr<Spam>{ptr.release()}); 
} 

BOOST_PYTHON_MODULE(example) 
{ 
    namespace python = boost::python; 
    python::class_<Spam, boost::noncopyable>(
     "Spam", python::init<std::size_t>()) 
    .def_readwrite("x", &Spam::x) 
    ; 

    python::class_<SpamFactory>("SpamFactory", python::init<>()) 
    .def("make", adapt_unique(&SpamFactory::make)) 
    .def("consume", &SpamFactory_consume) 
    ; 
} 

Interactive Python:

>>> import example 
>>> factory = example.SpamFactory() 
>>> spam = factory.make("a" * 21) 
Spam() 
>>> spam.x 
21 
>>> spam.x *= 2 
>>> spam.x 
42 
>>> factory.consume(spam) 
~Spam() 
>>> spam.x = 100 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
Boost.Python.ArgumentError: Python argument types in 
    None.None(Spam, int) 
did not match C++ signature: 
    None(Spam {lvalue}, unsigned int) 
+0

Vielen Dank für die ausführliche Antwort! Ich werde das so schnell wie möglich versuchen, aber der Code sieht gut aus! – schlimpf

+0

Und was ist mit std :: unique_ptr, die als add_property definiert ist. Muss ich es in die Klassendefinition einbinden, wo ich eigentlich eine Eigenschaft hinzufüge oder die Definition von to_python_converter wäre eine bessere Übung? –

3

Mein Vorschlag ist mit get() den rohen Zeiger aus dem std::unique_ptr Behälter zu erhalten. Sie müssen vorsichtig sein, um die unique_ptr im Bereich für die ganze Zeit zu behalten, dass Sie den rohen Zeigerwert verwenden möchten, andernfalls wird das Objekt gelöscht und Sie haben einen Zeiger auf einen ungültigen Speicherbereich.

+0

Sie bitte ein kleines Beispiel hinzufügen könnte? – schlimpf

+1

Probieren Sie dies aus: auto return_value = class_ >, bost :: nichtkopierbar ("Nachricht", init <>()); ' ' Nachricht * msg_ptr = return_value.get(); ' – Nicole

+0

macht dieser Code Sinn? Wo soll ich das hinstellen? – schlimpf

-1

ich heute denke, es gibt keine Möglichkeit zu tun, was Sie suchen ... Der Grund ist, weil std::unique_ptr<Message> someFunc(const std::string &str) nach Wert zurückgibt, die eine von zwei Dinge bedeutet:

  1. Der Rückgabewert wird zu kopiert werden (aber unique_ptr is not copyable);
  2. Der Rückgabewert wird verschoben (das Problem ist, dass boost :: python keine Unterstützung für das Verschieben von Semantiken bietet). (Hey, ich benutze Boost 1,53, nicht sicher in den neuesten Versionen);

Ist someFunc() das Objekt erstellen? Für den Fall, ja, ich glaube, die Lösung ist ein Wrapper, zu erstellen, falls nein, Sie durch Verweis zurückgeben kann:

std::unique_ptr<Message>& someFunc(const std::string &str) 

die Klasse aussetzen:

class_<std::unique_ptr<Message, std::default_delete<Message>>, boost::noncopyable>("unique_ptr_message") 
    .def("get", &std::unique_ptr<Message>::get, return_value_policy<reference_existing_object>()) 
; 

und auch die Funktionen:

def("someFunc", someFunc, return_value_policy<reference_existing_object>()); 
+0

Ich kann leider nichts in der Bibliothek ändern. Die Funktion erstellt das Objekt (es ruft eine andere Funktion auf, die das Objekt erzeugt) – schlimpf

1

-Boost unterstützt movable semantics und unique_ptrsince v.1.55. Aber in meinem Projekt habe ich Vorversion und machte so einfache Wrapper:

class_<unique_ptr<HierarchyT>, noncopyable>(typpedName<LinksT>("hierarchy", false) 
, "hierarchy holder") 
    .def("__call__", &unique_ptr<HierarchyT>::get, 
     return_internal_reference<>(), 
     "get holding hierarchy") 
    .def("reset", &unique_ptr<HierarchyT>::reset, 
     "reset holding hierarhy") 
    ; 

unique_ptr<HierarchyT> als Python shierarchy zu erstellen und an die Funktion übergeben, die sie durch Verweis akzeptiert.
Python Code:

hier = mc.shierarchy() 
mc.clusterize(hier, nds) 

wobei C++ Funktion float clusterize(unique_ptr<HierarchyT>& hier,...) ist.
Dann Ergebnisse in Python zugreifen einen Anruf hier() den verpackten Gegenstand aus dem unique_ptr zu erhalten:

output(hier(), nds)