2013-04-05 14 views
14

とベクトルを取り込む機能にPythonのリストを餌私が署名を持つ関数持っている:は、ブーストのPython

function(std::vector<double> vector); 

を、私はそれを公開してきましたが、それはPythonのリストにはなりません。 。私は他のSOの答えを見てきましたが、ほとんどの場合、boost :: python :: listsを使うように関数を変更する必要がありますが、関数を変更したくありません。私はvector_indexing_suiteを使ってこの関数の周りに単純なラッパーを書くことができると思いますが、私はこのフォームの多くの機能を持っていて、すべての単一のラッパーを書くのではないでしょう。 Pythonのlist-> std :: vectorのマッピングを自動的に行う方法はありますか?

答えて

22

元の機能を変更することなくこれを達成するには、いくつかの解決策があります。

これを少量の定型コードで公開し、Pythonへの透過性を実現するには、custom converterの登録を検討してください。 Boost.Pythonは、C++とPythonの間を移動するときに登録されたコンバータを使用します。一部のコンバータは、class_が型をエクスポートするときなど、バインディングを作成するときに暗黙的に作成されます。

次の完全な例では、python iterable protocolをサポートするpython型からの変換関数の登録を可能にするiterable_converter型を使用しています。ビルトインタイプの

  • コレクション:文字列のstd::vector<double>
  • 2次元コレクション:ユーザタイプのstd::vector<std::vector<std::String> >
  • コレクション:std::list<foo>
#include <iostream> 
#include <list> 
#include <vector> 
#include <boost/python.hpp> 
#include <boost/python/stl_iterator.hpp> 

/// @brief Mockup model. 
class foo {}; 

// Test functions demonstrating capabilities. 

void test1(std::vector<double> values) 
{ 
    for (auto&& value: values) 
    std::cout << value << std::endl; 
} 

void test2(std::vector<std::vector<std::string> > values) 
{ 
    for (auto&& inner: values) 
    for (auto&& value: inner) 
     std::cout << value << std::endl; 
} 


void test3(std::list<foo> values) 
{ 
    std::cout << values.size() << std::endl; 
} 

/// @brief Type that allows for registration of conversions from 
///  python iterable types. 
struct iterable_converter 
{ 
    /// @note Registers converter from a python interable type to the 
    ///  provided type. 
    template <typename Container> 
    iterable_converter& 
    from_python() 
    { 
    boost::python::converter::registry::push_back(
     &iterable_converter::convertible, 
     &iterable_converter::construct<Container>, 
     boost::python::type_id<Container>()); 

    // Support chaining. 
    return *this; 
    } 

    /// @brief Check if PyObject is iterable. 
    static void* convertible(PyObject* object) 
    { 
    return PyObject_GetIter(object) ? object : NULL; 
    } 

    /// @brief Convert iterable PyObject to C++ container type. 
    /// 
    /// Container Concept requirements: 
    /// 
    /// * Container::value_type is CopyConstructable. 
    /// * Container can be constructed and populated with two iterators. 
    ///  I.e. Container(begin, end) 
    template <typename Container> 
    static void construct(
    PyObject* object, 
    boost::python::converter::rvalue_from_python_stage1_data* data) 
    { 
    namespace python = boost::python; 
    // Object is a borrowed reference, so create a handle indicting it is 
    // borrowed for proper reference counting. 
    python::handle<> handle(python::borrowed(object)); 

    // Obtain a handle to the memory block that the converter has allocated 
    // for the C++ type. 
    typedef python::converter::rvalue_from_python_storage<Container> 
                   storage_type; 
    void* storage = reinterpret_cast<storage_type*>(data)->storage.bytes; 

    typedef python::stl_input_iterator<typename Container::value_type> 
                    iterator; 

    // Allocate the C++ type into the converter's memory block, and assign 
    // its handle to the converter's convertible variable. The C++ 
    // container is populated by passing the begin and end iterators of 
    // the python object to the container's constructor. 
    new (storage) Container(
     iterator(python::object(handle)), // begin 
     iterator());      // end 
    data->convertible = storage; 
    } 
}; 

BOOST_PYTHON_MODULE(example) 
{ 
    namespace python = boost::python; 

    // Register interable conversions. 
    iterable_converter() 
    // Build-in type. 
    .from_python<std::vector<double> >() 
    // Each dimension needs to be convertable. 
    .from_python<std::vector<std::string> >() 
    .from_python<std::vector<std::vector<std::string> > >() 
    // User type. 
    .from_python<std::list<foo> >() 
    ; 

    python::class_<foo>("Foo"); 

    python::def("test1", &test1); 
    python::def("test2", &test2); 
    python::def("test3", &test3); 
} 

対話の例では、のための変換を有効にします用法:

>>> import example 
>>> example.test1([1, 2, 3]) 
1 
2 
3 
>>> example.test1((4, 5, 6)) 
4 
5 
6 
>>> example.test2([ 
... ['a', 'b', 'c'], 
... ['d', 'e', 'f'] 
... ]) 
a 
b 
c 
d 
e 
f 
>>> example.test3([example.Foo(), example.Foo()]) 
2 

このアプローチには、いくつかのコメント:

  • iterable_converter::convertible機能は、むしろ反復可能なプロトコルをサポートしている任意の型を許すよりも、唯一のpythonのリストを許可に変更することができます。しかし、その結果、拡張はわずかに不快になることがあります。
  • 変換はC++タイプに基づいて登録されます。したがって、登録は、C++型を引数として受け入れるエクスポートされた関数の任意の数に対して同じ登録された変換が選択されるため、一度だけ行う必要があります。
  • 拡張名前空間に不要な型を導入しません。
  • メタプログラミングでは、多次元型が各次元型を再帰的に登録することができます。しかし、サンプルコードはすでに複雑なので、私は複雑さのレベルを追加したくありませんでした。

別のアプローチは、次のとおりです。

  • std::vectorを受け入れる機能ごとboost::python::listを受け入れるカスタム関数またはテンプレート関数を作成します。この手法では、変換が必要な型の量ではなく、エクスポートされる関数の量に基づいてバインディングがスケールされます。
  • Boost.Pythonの使用vector_indexing_suite*_indexing_suiteクラスは、に適合するタイプを、Pythonのリストまたは辞書のいくつかのセマンティクスに一致するようにエクスポートします。したがって、Pythonコードは、提供する正確なコンテナの種類を知る必要があり、その結果、Pythonの拡張が少なくなりました。 std::vector<double>VecDoubleとしてエクスポートされる場合、次いで、得られたPythonの使用法は次のようになります。ただし

    v = example.VecDouble() 
    v[:] = [1, 2, 3] 
    example.test1(v) 
    

    は、正確な型が一致しなければならないため、クラスをエクスポートするように、動作しない以下のみVecDoubleとの間の変換を登録しますstd::vector<double>

    example.test1([4, 5, 6]) 
    

    このアプローチはタイプではなく、機能にスケールが、それはより少ないニシキヘビ拡張をもたらし、不要タイプのexample名前空間をbloats。

関連する問題