2013-12-14 23 views
20

私はかなりboost.pythonの新機能で、関数の戻り値をPythonに公開しようとしています。Boost.Python:std :: unique_ptrを公開する方法

は、関数のシグネチャは次のようになります。

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

Pythonで関数を呼び出す、私は次のエラーを取得する:

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

Pythonで私の関数呼び出しは次のようになります。

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

私はstd :: unique_ptrを公開しようとしましたが、動作しません。 誰かがポインタクラスを適切に公開する方法を知っていますか? ありがとう!

編集: 私は次のことを試してみました:

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

; 

この例ではコンパイルが、私はまだエラーが前述ます。 はまた、私は、それ自体が

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

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

答えて

14

つまり、Boost.Pythonは移動セマンティクスをサポートしていないため、std::unique_ptrをサポートしていません。 Boost.Pythonのnews/change logには、C++ 11の移動セマンティクスのために更新された兆候はありません。さらに、unique_ptrサポートのこのfeature requestは、1年以上にわたって触れられていません。

しかし、Boost.Pythonは、std::auto_ptrでPythonとの間でオブジェクトの排他的所有権を転送することをサポートしています。

  • のPythonにC++の転送の所有権、C++の関数がなければなりません:
      unique_ptrは、本質的に auto_ptrのより安全なバージョンであるとして、それは auto_ptrを使用してAPIに unique_ptrを使用してAPIを適応させるために、かなりまっすぐ進むべきです
    • boost::python::return_value_policyのCallPolicyで公開され、boost::python::manage_new_object結果コンバータが付いています。
    • 必要release()を介しunique_ptr放出制御を有し、PythonはC++、C++関数に所有権を転送する場合、生のポインタ
  • を返す:
    • auto_ptr介しインスタンスを受け入れます。 FAQは、manage_new_objectポリシーでC++から返されたポインタは、std::auto_ptrで管理されることに言及しています。

      /// @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>); 
      }; 
      

      SpamFactory::make()SpamFactory::consume()を経由してラップする必要があります

    • を変更することはできませんAPI /ライブラリ考えるrelease()

経由unique_ptrからauto_ptrリリース制御を持っています補助機能。パイソンにC++から譲渡

機能を総称Python関数オブジェクトを作成する関数でラップすることができる:パイソンに

/// @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...>() 
    ); 
} 

ラムダ元の関数に委譲し、インスタンスのreleases()所有権、および呼び出しポリシーは、Pythonがラムダから返された値の所有権を取得することを示します。 mpl::vectorはBoost.Pythonの呼び出しシグネチャを記述し、言語間の関数のディスパッチを適切に管理できるようにします。

adapt_uniqueの結果はSpamFactory.make()として公開されています

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

総称的にはSpamFactory::consume()を適応させるには、より困難であるが、簡単な補助関数を記述するために十分に簡単です:

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

補助機能Boost.Pythonによって提供されたauto_ptrをAPIが要求するunique_ptrに変換します。SpamFactory_consume補助機能はSpamFactory.consume()として公開されている:ここ

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

は、完全なコードの例である:

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

インタラクティブな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

詳細な答えをありがとう!私はできるだけ早くこれを試すが、コードはよく見える! – schlimpf

+0

そして、add_propertyとして定義されているstd :: unique_ptrはどうでしょうか?私は実際にプロパティを追加しているクラス定義、またはto_python_converterを定義する方がよい方法でしょうか? –

3

私の提案はget()std::unique_ptrコンテナからの生のポインタを取得することですMessageクラスを公開してみました。 unique_ptrは、生のポインタ値を使用したいと思っている間、スコープ内に慎重に置かなければなりません。さもなければ、オブジェクトは削除され、無効なメモリ領域へのポインタがあります。

+0

あなたは小さな例をしてください追加することができますか? – schlimpf

+1

bost :: noncopyable( "Message"、init <>()); ' 'メッセージ* msg_ptr = return_value.getこの自動返り値を使用すると、 (); ' – Nicole

+0

このコードは意味がありますか?それはどこに置くべきですか? – schlimpf

-1

私は、あなたが探しているものを行うための方法はありません... std::unique_ptr<Message> someFunc(const std::string &str)は、2つのうちのいずれかを意味した値で返しているためであるこの頃だと思う:戻り値はするつもりです

  1. コピーされる(ただし、unique_ptr is not copyable)。
  2. 戻り値は移動されます(今問題はboost :: pythonがセマンティクスの移動をサポートしていないことです)。 (ヘイ、私はブースト1,53を使用しています、最新版ではわかりません)。

someFunc()はオブジェクトを作成していますか?

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

も機能:

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

がクラスを公開:場合はYES、私はあなたが参照によって返すことができ、解決策がNOの場合には、ラッパーを作成することではないと思います

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

残念ながら、私はライブラリ内の何も変更することはできません。関数はオブジェクトを作成しています(オブジェクトを作成する別の関数を呼び出しています) – schlimpf

1

ブーストmovable semanticsunique_ptrsince v.1.55をサポートします。 しかし、私のプロジェクトで、私は以前のバージョンを使用して、このような単純なラッパー製:Pythonのshierarchyとしてunique_ptr<HierarchyT>を作成し、参照することにより、それを受け入れる関数に渡す

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") 
    ; 

を。
Pythonコード:C++関数であるfloat clusterize(unique_ptr<HierarchyT>& hier,...)

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


は、次にPythonでのアクセス結果にunique_ptrをからラップされたオブジェクトを取得するための呼び出しhier()を行います

output(hier(), nds) 
関連する問題