簡體   English   中英

Boost :: Python,將元組轉換為Python工作,矢量 <tuple> 才不是

[英]Boost::Python, converting tuple to Python works, vector<tuple> does not

我已經使用Boost :: Python了一段時間,而且一切都很好。 然而昨天我試圖找出為什么我認為我注冊的特定類型(一個元組)在我嘗試從Python訪問它時給了我錯誤。

事實證明,雖然元組實際上已經注冊,但是當試圖通過vector_indexing_suite包裹的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);
}

通過Python訪問生成的.so導致:

>>> print makeMyTuple()
(0,)
>>> print makeTuples()[0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: No Python class registered for C++ class std::tuple<int>
>>> 

編輯:我已經意識到如果將vector_indexing_suiteNoProxy參數設置為true一起使用,則不會發生錯誤。 但是,我更喜歡這不是必需的,因為它使導出的類在Python中不直觀。

TupleToPython注冊了C ++-to-Python轉換器和Python-to-C ++轉換器。 這可以。

另一方面,您希望通過引用返回向量元素。 但是Python方面沒有任何東西可以作為你的元組的參考。 轉換為Python的元組可以保存相同的值,但它與原始的C ++元組完全分離。

看起來為了通過引用導出元組,需要為它創建一個索引套件,而不是為/ from-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);
}

然后暴露特定的元組:

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

請注意,與真正的Python元組不同,這些准元組是可變的(通過C ++)。 由於元組不變性,通過轉換器和NoProxy輸出看起來像是一種可行的替代方案。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM