我有一个API(一个特定的GUI库),它依赖于std::shared_ptr
,即它们通常用作函数参数并存储在其他对象中。例如,容器小部件(例如拆分器和框)将其子小部件存储在shared_ptr
中。现在我想通过luabind将此API映射到Lua。在理想的世界中,Luabind将在shared_ptrs中创建新对象,并允许我将这些对象直接传递给采用shared_ptr参数的函数。这似乎适用于单个类,例如:
luabind::class_<Button, std::shared_ptr<Button>>("Button")
虽然我这样声明,但我可以公开和使用像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")
现在我再也无法调用foo
- 它将无法从Lua找到该函数。我可以以某种方式让luabind继续支持shared_ptr
中的传递按钮吗?另外,我想知道为什么luabind要求你对层次结构中的所有类使用相同的智能指针,而不是只能转换为基类指针。
答案 0 :(得分:7)
我认为为了工作,你必须像这样绑定你的派生类:
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使用带有整数的type-id系统(更准确地说是在inheritance.hpp中定义的std :: size_t)。
您可以使用以下函数获取任何已注册类型的type-id: / p>
luabind::detail::static_class_id<T>(nullptr);
其中T是注册类 在我的演示程序中,它们是:
因此,当您从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,因此强制转换函数将尝试从BaseWidget(3)转换为std :: shared_ptr&lt;按钮&gt; (7)然后失败了。如果luabind支持从基础到派生的转换,它可能会起作用,这似乎不是。
但是,如果您将Button类声明为
luabind::class_<Button, BaseWidget, std::shared_ptr<Button>> ("Button")
然后实例将作为一个shared_ptr保持到Button,然后目标id将匹配注册的类型。 get函数将在第一次返回时分支,从不打扰转换。
您还可以找到我使用的自包含程序here at pastebin。
这里有一个有趣的断点列表,你可以设置看看发生了什么(luabind版本900):