pybind11, compile fails on binding Trampolines and multiple inheritance with std::enable_shared_from_this

322 views Asked by At

I have a compile problem with multiple inhertance combined with trampolines. The next code explains the problem:

#include <pybind11/pybind11.h>
#include <memory>

namespace py = pybind11;

// classes
class A {
public:
    virtual int fA() const = 0;
};

class B : public A, public std::enable_shared_from_this< B > {
public:
    int fA() const override {return 1;}
    virtual int fB() const {return 2;}
};

// trampolines
class PyA : public A {
public:
    using A::A;

    int fA() const override {
        PYBIND11_OVERLOAD_PURE(int, A, fA);
    }
};

class PyB : public B {
public:
    using B::B;

    int fA() const override {
        PYBIND11_OVERLOAD(int, B, fA);
    }
    int fB() const override {
        PYBIND11_OVERLOAD(int, B, fB);
    }
};

// bindings
void Bind(py::module_& m)
{
    py::class_<A, PyA>(m, "A" )
        .def( py::init<>() )
        .def( "fA", &A::fA );

//  py::class_<B, PyB, std::enable_shared_from_this< B > >(m, "B")
//  py::class_<B, PyB, A, std::enable_shared_from_this< B > >(m, "B", py::multiple_inheritance())
//  py::class_<B, PyB>(m, "B", py::multiple_inheritance())
    py::class_<B, PyB>(m, "B")
        .def( py::init<>() )
        .def( "fA", &B::fA )
        .def( "fB", &B::fB );
}

Shortly,

  • class A has a pure virtual method - so needs a tranpoline (PyA).
  • class B inherits from A, has virtual methods - so need a trampoline (PyB).
  • class B also inherits from std::enable_shared_from_this - so I have a multiple inheritance
  • Whatever bind statement I do for class B - compiler complains.

The next is output from c++17:

1>------ Build started: Project: AWB, Configuration: Debug x64 ------
1>Microsoft (R) C/C++ Optimizing Compiler Version 19.16.27045 for x64
1>Copyright (C) Microsoft Corporation.  All rights reserved.
1>
1>cl /c /IE:\Projects\AWB\pybind11\include /ID:\Python38\include /Zi /W1 /WX- /diagnostics:classic /Od /Ob0 /D _WINDLL /D _UNICODE /D UNICODE /D WIN32 /D _WINDOWS /Gm- /EHsc /RTC1 /MDd /GS /fp:precise /Zc:wchar_t /Zc:forScope /Zc:inline /std:c++17 /Fo"AWB.dir\Debug\\" /Fd"AWB.dir\Debug\vc141.pdb" /Gd /TP /errorReport:prompt  /bigobj Source.cpp
1>
1>Source.cpp
1>E:\Projects\AWB\pybind11\include\pybind11/pybind11.h(1512): error C2664: 'std::unique_ptr<B,std::default_delete<_Ty>>::unique_ptr(const std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': cannot convert argument 1 from 'std::shared_ptr<_Ty>' to 'std::nullptr_t'
1>        with
1>        [
1>            _Ty=B
1>        ]
1>E:\Projects\AWB\pybind11\include\pybind11/pybind11.h(1507): note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
1>E:\Projects\AWB\pybind11\include\pybind11/pybind11.h(1549): note: see reference to function template instantiation 'void pybind11::class_<B,PyB>::init_holder<B>(pybind11::detail::instance *,pybind11::detail::value_and_holder &,const std::unique_ptr<B,std::default_delete<_Ty>> *,const std::enable_shared_from_this<_Ty> *)' being compiled
1>        with
1>        [
1>            _Ty=B
1>        ]
1>E:\Projects\AWB\pybind11\include\pybind11/pybind11.h(1555): note: see reference to function template instantiation 'void pybind11::class_<B,PyB>::init_holder<B>(pybind11::detail::instance *,pybind11::detail::value_and_holder &,const std::unique_ptr<B,std::default_delete<_Ty>> *,const std::enable_shared_from_this<_Ty> *)' being compiled
1>        with
1>        [
1>            _Ty=B
1>        ]
1>E:\Projects\AWB\pybind11\include\pybind11/pybind11.h(1549): note: while compiling class template member function 'void pybind11::class_<B,PyB>::init_instance(pybind11::detail::instance *,const void *)'
1>E:\Projects\AWB\pybind11\include\pybind11/pybind11.h(1284): note: see reference to function template instantiation 'void pybind11::class_<B,PyB>::init_instance(pybind11::detail::instance *,const void *)' being compiled
1>Source.cpp(50): note: see reference to class template instantiation 'pybind11::class_<B,PyB>' being compiled
1>e:\projects\awb\pybind11\include\pybind11\cast.h(1638): note: see reference to class template instantiation 'pybind11::detail::descr<8>' being compiled
1>e:\projects\awb\pybind11\include\pybind11\cast.h(1637): note: see reference to class template instantiation 'pybind11::detail::descr<3>' being compiled
1>e:\projects\awb\pybind11\include\pybind11\cast.h(1636): note: see reference to class template instantiation 'pybind11::detail::descr<5>' being compiled
1>e:\projects\awb\pybind11\include\pybind11\cast.h(1177): note: see reference to class template instantiation 'pybind11::detail::descr<7>' being compiled
1>Done building project "AWB.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========

If I try with another simple class (C) instead of std::enable_shared_from_this all goes fine. So the problem might be in a propper formulatiopn at line py:class_<B, PyB, ? >... or possibly somewhere else...

What am I doing wrong?

Thanks

1

There are 1 answers

1
lr1985 On BEST ANSWER

You nailed the issue. Per pybind's documentation, if you don't specify any holder for a class, the default is std::unique_ptr which, as far as I understand, is not compatible with std::enable_shared_from_this.

Therefore, using py:class_<B, PyB, std::shared_ptr<B>> should fix your compilation issue. I have tried this solution and it works on my box.