Python,将元组转换为Python works,vector<;元组>;不
我已经使用Boost::Python一段时间了,结果一切正常。然而,昨天我试图找出,当我试图从Python访问某个特定类型(元组)时,为什么我认为我已经注册了该类型(元组)会给我带来错误 事实证明,虽然元组实际上是注册的,但当试图通过Python,将元组转换为Python works,vector<;元组>;不,python,c++,boost,tuples,boost-python,Python,C++,Boost,Tuples,Boost Python,我已经使用Boost::Python一段时间了,结果一切正常。然而,昨天我试图找出,当我试图从Python访问某个特定类型(元组)时,为什么我认为我已经注册了该类型(元组)会给我带来错误 事实证明,虽然元组实际上是注册的,但当试图通过vector\u index\u套件包装的std::vector访问元组时,这已经不够了 我在想,为什么它不起作用?有什么办法可以让这一切顺利进行吗?我应该试着用手把向量包起来吗 以下是我的MVE: #include <tuple> #include &
vector\u index\u套件
包装的std::vector
访问元组时,这已经不够了
我在想,为什么它不起作用?有什么办法可以让这一切顺利进行吗?我应该试着用手把向量包起来吗
以下是我的MVE:
#include <tuple>
#include <vector>
#include <boost/python.hpp>
#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
template <typename T>
struct TupleToPython {
TupleToPython() {
boost::python::to_python_converter<T, TupleToPython<T>>();
}
template<int...>
struct sequence {};
template<int N, int... S>
struct generator : generator<N-1, N-1, S...> { };
template<int... S>
struct generator<0, S...> {
using type = sequence<S...>;
};
template <int... I>
static boost::python::tuple boostConvertImpl(const T& t, sequence<I...>) {
return boost::python::make_tuple(std::get<I>(t)...);
}
template <typename... Args>
static boost::python::tuple boostConvert(const std::tuple<Args...> & t) {
return boostConvertImpl(t, typename generator<sizeof...(Args)>::type());
}
static PyObject* convert(const T& t) {
return boost::python::incref(boostConvert(t).ptr());
}
};
using MyTuple = std::tuple<int>;
using Tuples = std::vector<MyTuple>;
MyTuple makeMyTuple() {
return MyTuple();
}
Tuples makeTuples() {
return Tuples{MyTuple()};
}
BOOST_PYTHON_MODULE(h)
{
using namespace boost::python;
TupleToPython<MyTuple>();
def("makeMyTuple", makeMyTuple);
class_<std::vector<MyTuple>>{"Tuples"}
.def(vector_indexing_suite<std::vector<MyTuple>>());
def("makeTuples", makeTuples);
}
编辑:
我已经意识到,如果
vector\u indexing\u suite
与NoProxy
参数一起使用,则不会发生错误。但是,如果没有必要,我更愿意这样做,因为这会使导出的类在Python中变得不直观。TupleToPython
注册C++-to-Python转换器和Python-to-C++转换器。这很好
另一方面,您希望通过引用返回向量元素。但是Python方面没有任何东西可以作为对元组的引用。转换为Python元组可以保持相同的值,但它完全脱离了原始C++元组。
看起来,为了通过引用导出元组,需要为它创建一个索引套件,而不是从Python转换器到/从Python转换器。我从来没有这样做过,也不能保证它会起作用
下面介绍如何将元组公开为类似Python对象的最小元组(仅使用len()和索引)。首先定义一些辅助函数:
template <typename A>
int tuple_length(const A&)
{
return std::tuple_size<A>::value;
}
template <int cidx, typename ... A>
typename std::enable_if<cidx >= sizeof...(A), boost::python::object>::type
get_tuple_item_(const std::tuple<A...>& a, int idx, void* = nullptr)
{
throw std::out_of_range{"Ur outta range buddy"};
}
template <int cidx, typename ... A, typename = std::enable_if<(cidx < sizeof ...(A))>>
typename std::enable_if<cidx < sizeof...(A), boost::python::object>::type
get_tuple_item_(const std::tuple<A...>& a, int idx, int = 42)
{
if (idx == cidx)
return boost::python::object{std::get<cidx>(a)};
else
return get_tuple_item_<cidx+1>(a, idx);
};
template <typename A>
boost::python::object get_tuple_item(const A& a, int index)
{
return get_tuple_item_<0>(a, index);
}
模板
整数元组长度(常数A&)
{
返回std::tuple_size::value;
}
模板
typename std::enable_if=sizeof…(A),boost::python::object>::type
获取(常量std::tuple&a,int-idx,void*=nullptr)
{
抛出std::超出范围{“超出范围的伙伴”};
}
模板
typename std::enable_if::type
获取元组项(const std::tuple&a,int idx,int=42)
{
if(idx==cidx)
返回boost::python::object{std::get(a)};
其他的
返回get\u tuple\u item(a,idx);
};
模板
boost::python::object get\u tuple\u项(const A&A,int index)
{
返回get\u tuple\u item(a,索引);
}
然后公开特定元组:
using T1 = std::tuple<int, double, std::string>;
using T2 = std::tuple<std::string, int>;
BOOST_PYTHON_MODULE(z)
{
using namespace boost::python;
class_<T1>("T1", init<int, double, std::string>())
.def("__len__", &tuple_length<T1>)
.def("__getitem__", &get_tuple_item<T1>);
class_<T2>("T2", init<std::string, int>())
.def("__len__", &tuple_length<T2>)
.def("__getitem__", &get_tuple_item<T2>);
}
使用T1=std::tuple;
使用T2=std::tuple;
BOOST_PYTHON_模块(z)
{
使用名称空间boost::python;
类(“T1”,init())
.def(“\uuuu len\uuuuu”,&tuple\u长度)
.def(“\uuuu getitem\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;
类(“T2”,init())
.def(“\uuuu len\uuuuu”,&tuple\u长度)
.def(“\uuuu getitem\uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuu;
}
注意,这些准元组不同于真正的Python元组,它们是可变的(通过C++)。由于元组不变性,通过转换器和
NoProxy
导出看起来是一种可行的替代方法。不应该Tuples-makeTuples(){return Tuples{MyTuple()};}
beTuples-makeTuples(){return Tuples();}
而是@fedepad我这样做是为了让你可以调用makeTuples()[0]
而不触发越界
错误,因为向量将为空,您将看不到元组错误。不过,Boost已经通过引用处理任意类。为什么元组有什么不同?当然,如果导出类,元组会有不同。请注意,您必须使用类{“Tuples”}
导出向量。因此,基本上,转换函数仅在需要按值传递某些内容时才有用,对吗?我们必须创建一个采用元组类型的模板类,并使用与本机Python元组相同的接口注册一个类
,然后它才能工作。对吗?是的,这似乎是对的。我不太确定如何为std::tuple正确注册\uuu len\uuuu
和\uuuu getitem\uuuuu
,但如果你能管理好这一点,这看起来是正确的方法。@Svalorzen我用一些工作代码更新了答案。
using T1 = std::tuple<int, double, std::string>;
using T2 = std::tuple<std::string, int>;
BOOST_PYTHON_MODULE(z)
{
using namespace boost::python;
class_<T1>("T1", init<int, double, std::string>())
.def("__len__", &tuple_length<T1>)
.def("__getitem__", &get_tuple_item<T1>);
class_<T2>("T2", init<std::string, int>())
.def("__len__", &tuple_length<T2>)
.def("__getitem__", &get_tuple_item<T2>);
}