当前位置: 首页 > 工具软件 > LuaBind > 使用案例 >

luabind派生C++类及相关问题

夏侯自珍
2023-12-01

根据文档,可知可以在lua中派生c++类,进而在lua中实现多态性
https://www.rasterbar.com/products/luabind/docs.html#deriving-in-lua

然而lua类实例化的对象,实际分为两部分数据
https://www.rasterbar.com/products/luabind/docs.html#slicing

+--------------------+
| C++ object         |    <- ownership of this part is transferred
|                    |       to c++ when adopted
+--------------------+
| lua class instance |    <- this part is garbage collected when
| and lua members    |       instance is adopted, since it cannot
+--------------------+       be held by c++.

通常需要把lua派生的对象传回c++,让其调用虚接口virtual function
基类在C++中实现,并且导出到luabind

struct Base {
    virtual void foo();
}
struct Base_Wrapper : Base, luabind::wrap_base {
    virtual void foo() {
        call<void>("foo");
    }
    static void default_foo(Base * ptr) {
        return ptr->Base::foo();
    }
}
struct Manager {
    shared_ptr<Base> m_obj
    void dofoo() {
        if (m_obj)
            m_obj->foo(); // 这里希望调用A在lua中的派生类对象实现的foo
    }
}
Manager g_mgr;
module(L) [
    class_<Base, Base_Wrapper, shared_ptr<Base> >("Base")
        .def(constructor<>())
        .def("foo", &Base::foo, &Base_Wrapper::default_foo)
        
    , class_<Manager>("Manager")
        .def_readwrite("obj", &Manager::m_obj)
];
luabind::globals(L)["mgr"]=&g_mgr;

留意此处Manager的m_obj是shared_ptr,期望是维护obj生存期
lua中派生C++对象,实现如下

class 'Derived'(Base)
function Derived:__init()
    Base.__init(self)
end
function Derived:foo()
    print("hello")
end
mgr.obj=Derived() -- 这里实例化Derived对象,并保存到 mgr.obj

那么一段时间之后在C++中调用 g_mgr.dofoo(),会引发断言异常 !lua_isnil(L, -1),位置luabind\wrapper_base.hpp(124)

    template<class R BOOST_PP_COMMA_IF(BOOST_PP_ITERATION()) BOOST_PP_ENUM_PARAMS(BOOST_PP_ITERATION(), class A)>
        typename boost::mpl::if_<boost::is_void<R>
            , luabind::detail::proxy_member_void_caller<boost::tuples::tuple<BOOST_PP_ENUM(BOOST_PP_ITERATION(), LUABIND_TUPLE_PARAMS, _)> >
            , luabind::detail::proxy_member_caller<R, boost::tuples::tuple<BOOST_PP_ENUM(BOOST_PP_ITERATION(), LUABIND_TUPLE_PARAMS, _)> > >::type
        call(char const* name BOOST_PP_COMMA_IF(BOOST_PP_ITERATION()) BOOST_PP_ENUM(BOOST_PP_ITERATION(), LUABIND_OPERATOR_PARAMS, _), detail::type_<R>* = 0) const
        {
            ...
            assert(!lua_isnil(L, -1));
            

这是因为lua的垃圾回收机制,将Derived 对象的lua部分销毁了,只保留了c++部分,导致 call<void>(“foo”) 找不到实现体
解决方案就是使用adopt policy,但是adopt不支持smart ptr,所以需要重新定义导出接口

#include <luabind/adopt_policy.hpp>
struct Base {
    ...
}
struct Base_Wrapper : Base, luabind::wrap_base {
    ...
}
struct Manager {
    ...
    void setobj(Base * obj) { // 这里直接使用指针
        m_obj.reset(obj);
    }
}
Manager g_mgr;
module(L) [
    class_<Base, Base_Wrapper/*, shared_ptr<Base>*/ >("Base") // 使用raw ptr
        ...
        
    , class_<Manager>("Manager")
        .def("setobj", &Manager::setobj, adopt(_2)) // 使用adopt policy
];
luabind::globals(L)["mgr"]=&g_mgr;

在lua中直接传入创建的对象,生存期会被转移到mgr

class 'Derived'(Base)
...
mgr:setobj(Derived())

相关代码:
luabind/back_reference.hpp
luabind/detail/instance_holder.hpp

 类似资料: