【问题标题】:Using luabind and std::shared_ptr with inheritance使用 luabind 和 std::shared_ptr 进行继承
【发布时间】:2012-05-13 01:55:00
【问题描述】:

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

luabind::class_<Button, std::shared_ptr<Button>>("Button")

虽然我是这样声明的,但我可以公开和使用像 void foo(std::shared_ptr&lt;Button&gt; const&amp;) 这样的函数。

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

luabind::class_<BaseWidget, std::shared_ptr<BaseWidget>>("BaseWidget"),
luabind::class_<Button, BaseWidget, std::shared_ptr<BaseWidget>>("Button")

现在我不能再调用foo - 它将无法从 Lua 中找到函数。我能否以某种方式让 luabind 仍然支持在shared_ptrs 中传递按钮?另外,我想知道为什么 luabind 要求您对层次结构中的所有类使用相同的智能指针,而不是仅仅将它们转换为基类指针。

【问题讨论】:

  • 第二行不应该使用std::shared_ptr&lt;BaseWidget&gt; 而不是std::shared_ptr&lt;Button&gt;吗?
  • 是的 - 谢谢!只是一种类型!

标签: c++ c++11 lua shared-ptr luabind


【解决方案1】:

我认为你必须像这样绑定你的派生类:

luabind::class_<Button, BaseWidget, std::shared_ptr<Button>> ("Button")

例如:

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;
    }
};

现在您可以将它与 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)
        ];
    }
};

在 lua 中:

b = Button()

a = Action()

a:DoClick(b)

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

luabind::detail::static_class_id<T>(nullptr);

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

  • BaseWidget = 3
  • std::shared_ptr = 6
  • 按钮 = 4
  • std::shared_ptr = 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
    );
}

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

luabind::class_<Button, BaseWidget,std::shared_ptr<BaseWidget>>("Button")

然后实例将作为一个 shared_ptr 保存到 BaseWidget,因此 cast 函数将尝试从 BaseWidget (3) 转换到 std::shared_ptr (7) 并且失败。如果 luabind 支持 base-to-derived 转换,它可以工作,但它似乎不支持。

如果您将 Button 类声明为

luabind::class_<Button, BaseWidget, std::shared_ptr<Button>> ("Button")

然后实例将作为一个 shared_ptr 保存到 Button 然后目标 id 将匹配注册的类型。 get 函数将在第一次返回时分支,从不打扰演员表。

您也可以找到我使用的自包含程序here at pastebin

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

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

【讨论】:

  • 我实际上为 get_pointer 添加了这些重载。它确实通过了 smart_pointer(我可以知道,因为如果我多次调用这些函数,引用计数是正确的)
  • 另外,传递原始指针更加笨拙,因为我想存储指针,而不仅仅是在它们上执行函数。对于这项工作,我必须修改 GUI 对象层次结构以从 enable_shared_from_this 派生。
  • 但是使用您当前的解决方案,您忽略了 luabind 手册要求使用基类 shared_ptr 类型:请参阅rasterbar.com/products/luabind/docs.html#smart-pointers,最后一段。如果这“有效”,我想知道为什么......
  • 我认为这是 luabind 文档的错误。我将提交一份错误报告,并在我的回答中详细说明它为何如此工作。我做了一些相当广泛的调试来解决这个问题!
  • 在我看来,唯一可能发生的问题是,如果 luabind 尝试将原始指针分配给另一个智能指针,以便实例由两个不同的引用计数主体持有。 .. 如果您尝试将按钮传递给采用 std::shared_ptr 的函数,这可能会发生吗?
猜你喜欢
  • 2014-08-27
  • 2021-08-10
  • 1970-01-01
  • 1970-01-01
  • 2018-05-23
  • 2023-03-11
  • 1970-01-01
  • 2023-04-03
  • 1970-01-01
相关资源
最近更新 更多