使用pybind11混合类型转换和不透明类型

时间:2019-09-30 14:20:31

标签: python c++ pybind11

我在pybind11中使用了不透明类型。例如,我为stl容器std::vector<uint32_t>定义了一个容器,这是方法FromVec的参数类型:

void FromVec(std::vector<uint32_t> vec);

PYBIND11_MAKE_OPAQUE(std::vector<uint32_t>);

PYBIND11_MODULE(tmap, m)
{
    py::bind_vector<std::vector<uint32_t>>(m, "VectorUint", 
      "Unsigned 32-bit int vector.");

    m.def("from_vec", &FromVec, py::arg("vec")
}

现在,我可以(在Python中)执行以下操作:

vec = VectorUint([2, 66, 262, 662, 26, 62])
from_vec(vec)

但是,类型转换不再起作用,因为该函数现在期望使用VectorUint并且不再接受list,例如:

l = [2, 66, 262, 662, 26, 62]
from_vec(l)

有没有一种方法可以同时允许倾斜类型和类型转换?也就是说,当传递list而不是斜绑定VectorUint时,它将转换为std::vector<uint32_t>而不是Python抛出“不兼容的函数参数”吗?

2 个答案:

答案 0 :(得分:2)

如果您需要从std::vector自动转换为list,则可以在绑定代码中添加此类重载:

m.def("from_vec", &FromVec, py::arg("vec")); // accepts VectorUnit 
m.def("from_vec", [](py::list& list){ /* do conversion manually and call FromVec */ } ); // accepts python list

答案 1 :(得分:2)

只需在您的py::bind_vector之后添加:

py::implicitly_convertible<py::list, std::vector<uint32_t>>();

它甚至可以与属性一起使用(例如,使用def_readwrite创建的属性)。您将两全其美:可以通过python的引用来访问向量,以便appendextend等可以正常工作;并且您可以从python列表中进行分配(该列表当然会被复制)。

编辑:不幸的是,此技巧不适用于std::map(不了解其他容器)。解决方法是,您可以使用其他类型的中间映射,例如:

namespace pybind11::workaround
{
  template<typename K, typename V>
  class Map : public std::map<K, V> {};
}

namespace pybind11::detail
{
  template<typename K, typename V>
  struct type_caster<workaround::Map<K, V>> : map_caster<workaround::Map<K, V>, K, V> {};
}

inline void bindCommonTypes(pybind11::handle scope)
{
  using IntMap = std::map<int, int>;
  py::bind_map<IntMap>(scope, "IntMap")
  .def(py::init([](const py::dict& dict) {
    return dict.cast<pybind11::workaround::Map<int, int>();
  }));
  py::implicitly_convertible<py::dict, IntMap>();
}