简体   繁体   中英

Cast a C++ abstract class from a Pyhton object derived from that C++ abstract class

I have been trying to cast an abstract class in a C++ program, trying different solution from this webpage and the official documentation, for example this and this , but nothing works. My C++ abstract class is:

class Base
{
    public:
    virtual ~Base(){};
    virtual void foo(){};
};

My wrapper to import to python is:

class BasePy: public Base 
{
public:
    void foo() override
    {
        PYBIND11_OVERLOAD_PURE(
                void, 
                Base, 
                foo);
    }
};

My import function and my import:

void import_base(pybind11::module m)
{
    using namespace pybind11;
    class_<Base, BasePy>(m, "Base")
        .def("foo", &Base::foo);
}
PYBIND11_MODULE(baselib, m)
{
    import_component(m);
}

I wrote a python class(derived.py):

import baselib

class test(baselib.Base):
    def __init__(self):
        self.x = 10;

    def foo(self):
        print(self.x)

And finally my main:

scoped_interpreter guard{};
auto obj = module::import("derived").attr("test")(); 
// obj = {<pybind11::handle> = 
//         {<pybind11::detail::object_api<pybind11::handle>> = 
//           {<pybind11::detail::pyobject_tag> = 
//             {<No data fields>}, <No data fields>
//           }, 
//           m_ptr = 0x7ffff706f1d0 
//         }, 
//         <No data fields>}
Base* bas = isinstance<Base>(obj) ? (Base*)obj.cast<void*>() : nullptr;
// bas = (Base*) 0x0

"baselib.so" and the executable compiles as "derived.py" works on the pyhton3 interpreter flawlessly. In the interpreter:

derived.test.__base__ : <class 'baselib.base'>
derived.test.__class__ : <class 'pybind11_builtins.pybind11_type'>
derived.baselib == baselib : true
baselib.base.__base__ : <class 'pybind11_builtins.pybind11_object'>
baselib.base.__class__ : <class 'pybind11_builtins.pybind11_type'>

What am I failing to understand?

Eureka, I find a solution: when I import the class I didn't add the init function of the trampoline class:

void import_base(pybind11::module m)
{
    using namespace pybind11;
    class_<Base,/*Holder*/ BasePy>(m, "Base")
    // You can add a holder like std::shared_ptr<Base> in "Holder"
        .def(init<>())// <--- THIS
        .def("foo", &Base::foo);
}

And in the python class we need to add the "abstract class initializer"(it is the trampoline initializer) like this:

class test(baselib.Base):
    def __init__(self):
        baselib.Base.__init__(self) // Adding this line
        self.x = 10;

And... Voilà. The problem is solved.

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