使用luabind和std :: shared_ptr继承

ltj*_*jax 9 c++ lua shared-ptr luabind c++11

我有一个依赖std::shared_ptr很多的API(一个特定的GUI库),即它们通常用作函数参数并存储在其他对象中.例如,容器小部件(例如拆分器和框)将其子小部件存储在shared_ptrs中.现在我想通过luabind将此API映射到Lua.在理想的世界中,Luabind将在shared_ptrs中创建新对象,并允许我将这些对象直接传递给采用shared_ptr参数的函数.这似乎适用于单个类,例如:

luabind::class_<Button, std::shared_ptr<Button>>("Button")
Run Code Online (Sandbox Code Playgroud)

虽然我这样声明,但我可以公开和使用像void foo(std::shared_ptr<Button> const&).

现在,luabind手册提到为了使用类的层次结构,我必须对层次结构中的所有类使用相同的shared_ptr模板实例,例如

luabind::class_<BaseWidget, std::shared_ptr<BaseWidget>>("BaseWidget"),
luabind::class_<Button, BaseWidget, std::shared_ptr<BaseWidget>>("Button")
Run Code Online (Sandbox Code Playgroud)

现在我再也无法打电话了foo- 它将无法从Lua找到该功能.我可以以某种方式让luabind继续支持shared_ptrs中的传递按钮吗?此外,我想知道为什么luabind要求您对层次结构中的所有类使用相同的智能指针,而不是它们只能转换为基类指针.

Jul*_*bot 7

我认为为了工作你必须绑定你的派生类,如下所示:

luabind::class_<Button, BaseWidget, std::shared_ptr<Button>> ("Button")
Run Code Online (Sandbox Code Playgroud)

例如:

class BaseWidget
{
public:
    static void Bind2Lua(lua_State* l)
    {
        luabind::module(l)
        [
            luabind::class_<BaseWidget, std::shared_ptr<BaseWidget>> ("BaseWidget")
            .def(luabind::constructor<>())
        ];
    }

    virtual ~BaseWidget()
    {

    }
};

class Button : public BaseWidget
{
public:
    static void Bind2Lua(lua_State* l)
    {
        luabind::module(l)
        [
            luabind::class_<Button, BaseWidget, std::shared_ptr<Button>> ("Button")
            .def(luabind::constructor<>())
            .def("Click", &Button::Click)
        ];
    }

    void Click()
    {
        std::cout << "Button::Click" << std::endl;
    }
};
Run Code Online (Sandbox Code Playgroud)

现在您可以将它与shared_ptr一起使用:

class Action
{
public:
    void DoClick(const std::shared_ptr<Button>& b)
    {
        // perform click action
        b->Click();
    }

    static void Bind2Lua(lua_State* l)
    {
        luabind::module(l)
        [
            luabind::class_<Action> ("Action")
            .def(luabind::constructor<>())
            .def("DoClick", &Action::DoClick)
        ];
    }
};
Run Code Online (Sandbox Code Playgroud)

在卢阿:

b = Button()

a = Action()

a:DoClick(b)
Run Code Online (Sandbox Code Playgroud)

原因是luabind使用带有整数的type-id系统(更准确地说是在inheritance.hpp中定义的std :: size_t).
您可以使用以下函数获取任何已注册类型的type-id:

luabind::detail::static_class_id<T>(nullptr);
Run Code Online (Sandbox Code Playgroud)

其中T是注册类.
在我的演示程序中,它们是:

  • BaseWidget = 3
  • std :: shared_ptr <BaseWidget> = 6
  • 按钮= 4
  • std :: shared_ptr <Button> = 7
  • 行动= 5

因此,当您从lua调用DoClick时,它将在instance_holder.hpp中调用模板类pointer_holder的get成员:

std::pair<void*, int> get(class_id target) const
{
    if (target == registered_class<P>::id)
        return std::pair<void*, int>(&this->p, 0);

    void* naked_ptr = const_cast<void*>(static_cast<void const*>(
        weak ? weak : get_pointer(p)));

    if (!naked_ptr)
        return std::pair<void*, int>((void*)0, 0);

    return get_class()->casts().cast(
        naked_ptr
      , static_class_id(false ? get_pointer(p) : 0)
      , target
      , dynamic_id
      , dynamic_ptr
    );
}
Run Code Online (Sandbox Code Playgroud)

如您所见,如果目标类与注册的类不同,它将尝试进行强制转换.
这是事情变得有趣的地方.如果您将Button类声明为

luabind::class_<Button, BaseWidget,std::shared_ptr<BaseWidget>>("Button")
Run Code Online (Sandbox Code Playgroud)

那么实例将作为shared_ptr保存到BaseWidget,因此强制转换函数将尝试从BaseWidget(3)转换为std :: shared_ptr <Button>(7)并且失败.如果luabind支持从基础到派生的转换,它可能会起作用,而这似乎并不适用.

但是,如果您将Button类声明为

luabind::class_<Button, BaseWidget, std::shared_ptr<Button>> ("Button")
Run Code Online (Sandbox Code Playgroud)

然后,实例将作为一个shared_ptr保持到Button,然后目标ID将匹配注册的类型.get函数将在第一次返回时分支,从不打扰转换.

您还可以在pastebin中找到我在此处使用的自包含程序.

这里有一个有趣的断点列表,你可以设置看看发生了什么(luabind版本900):

  • instance_holder.hpp中的第94行(pointer_holder :: get的第一行)
  • instance.cpp中的第143行(cast_graph :: impl :: cast的第一行)