gpt4 book ai didi

boost - 使用 Boost Python 将 Python 列表输入到接受向量的函数中

转载 作者:行者123 更新时间:2023-12-02 01:52:57 25 4
gpt4 key购买 nike

我有一个带有签名的函数:

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

我已经公开了它,但它不接受 Python 列表。我已经查看了其他 SO 答案,大多数涉及更改函数以接受 boost::python::lists,但我不想更改该函数。我想我可以使用 vector_indexing_suite 围绕这个函数编写一个简单的包装器,但我有很多这种形式的函数,并且不想为每个函数编写一个包装器。有没有办法自动进行Python list->std::vector 映射?

最佳答案

有几种解决方案可以实现此目的,而无需修改原始功能。

要使用少量样板代码和对 python 的透明度来实现此目的,请考虑注册 custom converter 。在 C++ 和 Python 类型之间转换时,Boost.Python 使用已注册的转换器。有些转换器是在创建绑定(bind)时隐式创建的,例如 class_ 时。导出类型。

以下完整示例使用 iterable_converter允许从支持 python iterable protocol 的 python 类型注册转换函数的类型。该示例启用以下转换:

  • 内置类型集合:std::vector<double>
  • 二维字符串集合: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 列表,而不是允许任何支持可迭代协议(protocol)的类型。然而,该扩展可能会因此变得有点不Python。
  • 转换是基于 C++ 类型注册的。因此,注册只需完成一次,因为在接受 C++ 类型作为参数的任意数量的导出函数上都会选择相同的注册转换。
  • 它不会将不必要的类型引入 example扩展命名空间。
  • 元编程可以允许多维类型递归地注册每个维度类型。但是,示例代码已经足够复杂,因此我不想增加额外的复杂性。
<小时/>

替代方法包括:

  • 创建接受 boost::python::list 的自定义函数或模板函数对于每个接受 std::vector 的函数。这种方法会导致绑定(bind)根据导出的函数数量而不是需要转换的类型数量进行缩放。
  • 使用 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])

    虽然这种方法扩展到类型而不是函数,但它会导致Python风格的扩展减少,并使 example 变得臃肿。具有不必要类型的命名空间。

关于boost - 使用 Boost Python 将 Python 列表输入到接受向量的函数中,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15842126/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com