简体   繁体   中英

pybind11: Can't get std::list to work in Python

In my C++ project I have a method which returns std::list<ModelComponent*>* , where ModelComponent is a custom class I've defined.

My wrapper looks like this:

py::class_<ComponentManager>(m, "ComponentManager")
    .def(py::init<Model*>())
    .def("getAllComponents", &ComponentManager::getAllComponents,
        py::return_value_policy::reference);

When I try to use this method in Python I get the following error:

TypeError: Unable to convert function return value to a Python type! The signature was
        (self: libgenesys.ComponentManager) -> std::__cxx11::list<ModelComponent*, std::allocator<ModelComponent*> >

Did you forget to `#include <pybind11/stl.h>`? Or <pybind11/complex.h>,
<pybind11/functional.h>, <pybind11/chrono.h>, etc. Some automatic
conversions are optional and require extra headers to be included
when compiling your pybind11 module.

If I do #include <pybind11/stl.h> in this file though, all sorts of errors are thrown during compilation, starting with:

/usr/include/pybind11/cast.h:1408:73: error: no matching function for call to ‘get<0>(std::pair<ModelComponent*, unsigned int>*&)’
In file included from /usr/include/c++/9.2.0/bits/unique_ptr.h:36,
                 from /usr/include/c++/9.2.0/memory:80,
                 from /usr/include/c++/9.2.0/thread:39,
                 from main.cpp:15:

This error seems to be thrown because of another class (which also has a wrapper) that defines typedef std::pair<ModelComponent*, unsigned int> Connection; (even though I ignore the methods returning this particular type).

I can't seem to find a way to get the method returning a std::list to work, the documentation says including pybind11/stl.h should do the trick, but for me it's only throwing more errors.

Edit: The code on which std::pair<ModelComponent*, unsigned int> is used is posted here: https://pastebin.com/AX2XBYEd

The problem is your binding of std::list<Connection*>* getList() . SWIG doesn't know what to do with Connection* , only with Connection . You'll need to write a binding for Connection* .

In your pastebin code, you have:

typedef std::pair<ModelComponent*, unsigned int> Connection;

replace that with:

template<typename T1, typename T2>
struct MyPair {
    T1 first;
    T2 second;
};

typedef MyPair<ModelComponent*, unsigned int> Connection;

while leaving the #include "pybind11/stl.h" in place should do the trick.

At issue is that none of the pre-provided specializations that were written for conversions match. You can of course also provide your own specialization.

It's one of the reasons why in cppyy I prefer to leave objects as-is, and simply provide a pythonistic interface, and if you truly want a python object (a list as it may be here), it's trivially converted. Example below (I added inline definitions for class ConnectionManager to keep it simple; you hadn't any on pastebin), to compare:

>>> import ConnectionManager
>>> cm = ConnectionManager.ConnectionManager()
>>> cm.getList()
[]
>>>

versus:

>>> import cppyy
>>> cppyy.include("ConnectionManager.h")
>>> cm = cppyy.gbl.ConnectionManager()
>>> cm.getList()
<cppyy.gbl.std.list<pair<ModelComponent*,unsigned int>*> object at 0x7faa345759c0>
>>> list(_)
[]
>>> 

And because of the pythonizations, it's a drop-in replacement in for-loops etc. as well (it implements the __iter__ protocol, for example). Either which way, this approach certainly saves you from having to wade through those long C++ template error messages. :)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM