元の機能を変更することなくこれを達成するには、いくつかの解決策があります。
これを少量の定型コードで公開し、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。