Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mixing type conversions and opaque types with pybind11

I am making use of opaque types with pybind11. For example, I defined one for the stl container std::vector<uint32_t>, which is a argument type of the method 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")
}

With this I can now (in Python) do the following:

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

However, type conversion does not work anymore, as the function now expects a VectorUint and doesn't accept list anymore, e.g.:

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

Is there a way to allow for both, oblique types and type conversion? That is, when a list is passed instead of a oblique bound VectorUint it is converted to a std::vector<uint32_t> rather than Python throwing "incompatible function arguments"?

like image 680
Dänu Avatar asked Dec 06 '25 08:12

Dänu


1 Answers

Just add this after your py::bind_vector:

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

It even works with properties (e.g. created with def_readwrite). You get the best of both worlds: The vector is accessed by reference from python, so that append, extend, etc work as expected; and you can assign from a python list (which will of course be copied).

EDIT: Unfortunately this trick doesn't work with std::map (don't know about other containers). As a workaround, you can use an intermediate map of a different type, e.g.:

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>();
}
like image 74
Max Avatar answered Dec 08 '25 23:12

Max