使用C++自动绑定Lua
我正在构建一个简单的2D游戏引擎,它越来越大,将所有的函数暴露给Lua将会是不可能的:因此我试图自动化一下这个过程, 是否有任何方法可以一次从堆栈中获取所有参数(具有不同的类型)并将它们直接注入到C++函数中。 我已经自动化了函数参数检查,但是函数绑定有点棘手
例如: 我有一个正常的改变精灵位置的代码:
int LuaSprite::SetSpritePosition(lua_State* L)
{
Drawable* sprt = (Drawable*)lua_touserdata(L, 1);
int x = (int)lua_tonumber(L, 2);
int y = (int)lua_tonumber(L, 3);
sprt->setPosition(Vec2i(x, y));
return 0;
}
我想要实现的概念更像是这样:
int LuaSprite::SetSpritePosition(lua_State* L)
{
LOAD_ARGS(Sprite*, int, int)
GET_ARG(1)->setPosition(Vec2i(GET_ARGS_FROM(1)));
LOAD_RETURN(true, x, y, 3);
return 3;
}
所以
LOAD_ARGS将分别从堆栈中获取给定的类型。
GET_ARG(i)将获取索引为i的参数。
GET_ARGS_FROM(i)将做类似于x,y,z,type的事情
我不是要求完全相同的行为,因为它可能是不可能做到的,但至少要类似于这样的行为
我相信这不仅仅需要“普通”的C++,还需要一些“魔法”宏。 我没有使用现成的自动Lua绑定“库”,因为有自定义结构和一些函数使用复杂的结构和类 我已经做了很多搜索,但我感到非常迷失。
在我的实践中,我发现遵循 Lua 对参数类型灵活性或参数不同布局的方便性非常高。您不受限于严格的 C/C++ 规则,您可以有相同的方法根据确切的参数执行不同的任务。即,调用的语义将表达一些更高级的概念。
当编写这种灵活的方法时,完全自动的绑定大多是无用的。比如说,您可以接受各种对象作为参数。单个数字表示光度,或 RGB 三元组表示精确的颜色,甚至是给出纹理图像路径的字符串。再加上一个可选的跟随参数,指定非默认混合模式。您无法转换固定的通用参数读取器。
因此,与其尝试使用一些模板魔法来预先解析某些参数,不如拥有一个实用程序函数,为您读取 Vec2/Vec2i/Vec3/等等,仅需指向 Lua 状态的指针、从栈的索引开始,然后返回结果标志,或实际读取的值的数量,以帮助跟踪参数解析。
GET_ARG(1) 结构如此通用,以致于在我的情况下它只是必须从 Lua 访问的所有对象的基类中的一个方法。这个基类是一个模板,所以它知道对象的类型,可以自动将索引 1(它总是 1)处的 Lua userdata 强制转换为确切的本机类型,而无需编写任何内容。
返回值也要么太简单不值一提(一个布尔值或一些整数),要么太复杂无法通过自动绑定处理。例如,返回一些大型对象,它们在构造之后需要进行一些配置/更新/注册,然后是更多的值,其中一些是可选的或依赖于先前返回数据的类型。
因此,在我的代码示例中,您的示例将转换为:
int LuaSprite::SetSpritePosition(lua_State* L)
{
get_object(L)->setPosition(read_lua_vec2i(L, 2));
return 0;
}
试试 Luaaa(https://github.com/gengyong/luaaa),它更轻量级,只实现了一个头文件。
它完全符合您的要求。
示例代码:
// 包括 luaaa 文件
#include "luaaa.hpp"
using namespace luaaa;
// 您已存在的类
class Cat
{
public:
Cat();
virtual ~Cat();
public:
void setName(const std::string&);
const std::string& getName() const;
void eat(const std::list<std::string>& foods);
//...
private:
//...
};
lua_State * state; // 创建并初始化 Lua
// 将其导出:
LuaClass<Cat>(state, "AwesomeCat")
.ctor<std::string>();
.fun("setName", &Cat::setName);
.fun("getName", &Cat::getName);
.fun("eat", &Cat::eat);
.def("tag", "Cat");
您可以在这里找到更多详细信息(https://github.com/gengyong/luaaa)。
我是这个库的作者,强烈建议您尝试一下,如果您有任何问题,请随时询问。
根据您想对从 Lua 中传递参数的方式以及控制权的大小,您可能需要完全自动的绑定,或者像我一样使用半自动的绑定。我编写了一个工具,它允许您定义“粘合”函数,这些函数可以使用 Lua 栈操作函数的所有功能,但同时还会为您处理对象的生命周期。
这并不适用于所有人,但适用于一些人。
- Lua 虚拟机加密load(string.dump(function)) 后执行失败问题如何解决
- 我想创建一个 Nginx 规则,禁止访问
- 如何将两个不同的lua文件合成一个 东西有点长 大佬请耐心看完 我是小白研究几天了都没搞定
- 如何在roblox studio中1:1导入真实世界的地形?
- 求解,lua_resume的第二次调用继续执行协程问题。
- 【上海普陀区】内向猫网络招募【Skynet游戏框架Lua后端程序员】
- SF爱好求教:如何用lua实现游戏内调用数据库函数实现账号密码注册?
- Lua实现网站后台开发
- LUA错误显式返回,社区常见的规约是怎么样的
- lua5.3下载库失败
- 请问如何实现文本框内容和某个网页搜索框内容连接,并把网页输出来的结果反馈到另外一个文本框上
- lua lanes多线程使用
- 一个kv数据库
- openresty 有没有比较轻量的 docker 镜像
- 想问一下,有大佬用过luacurl吗
- 在Lua执行过程中使用Load函数出现问题
- 为什么 neovim 里没有显示一些特殊字符?
- Lua比较两个表的值(不考虑键的顺序)
- 有个lua简单的项目,外包,有意者加微信 liuheng600456详谈,最好在成都
- 如何在 Visual Studio 2022 中运行 Lua 代码?

我发现Luabridge在执行这些任务时非常便捷。它是一个仅有头文件的库,适用于旧的C++代码(在c++11之前和之后)。
您不必直接针对Lua堆栈进行编程,而是将普通的类/结构包装为绑定定义。
绑定代码如下所示:
getGlobalNamespace(L).beginNamespace("GameFrameworkName") .beginClass<RectF>("Rect") .addStaticFunction("__call", &RectF_Ctor) // Constructor from Table! .addData<float>("x", &RectF::x) .addData<float>("y", &RectF::y) .addData<float>("width", &RectF::width) .addData<float>("height", &RectF::height) .addFunction("Union", &RectF::Union) .addFunction("Intersects", (bool (RectF::*)(const RectF&)) &RectF::Intersects) .addFunction("Intersection", &RectF::Intersection) .addFunction("Contains", (bool (RectF::*)(const PointF&)) &RectF::Contains) .addFunction("Offset", (void (RectF::*)(const PointF&)) &RectF::Offset) .addFunction("Inflate", (void (RectF::*)(float, float)) &RectF::Inflate) .addFunction("__tostring", &RectF_ToString) .addFunction("__eq", (bool (RectF::*)(const RectF&)) &RectF::operator==) .addFunction("Copy", &RectF_Copy) .endClass() .beginClass<PointF>("Point") .addStaticFunction("__call", &PointF_Ctor) // Constructor from Table! .addData<float>("x", &PointF::x) .addData<float>("y", &PointF::y) .addFunction("__tostring", &PointF_ToString) .addFunction("__add", (PointF (PointF::*)(const PointF&)) &PointF::operator+ ) .addFunction("__sub", &PointF_SubtractOperator ) .addFunction("__mul", &PointF_MultiplyOperator ) .addFunction("__div", &PointF_DivideOperator ) .addFunction("__eq", (bool (PointF::*)(const PointF&)) &PointF::operator==) .addFunction("__unm", &PointF_Unm) .addFunction("Copy", &PointF_Copy) .endClass() .endNamespace();对于游戏对象,我通常会在C++中管理其生命期,并将指针传递给Lua。 Luabind允许您重写Lua CTor(__call)和~Tor(__gc)。以下是我的副本:
template<typename T> std::string DoNotCreate() { DBG_ASSERT(false); // Do not try to create a new instance of this type return StrFormat("ERROR: Lua cannot create an instance of %s.", typeid(T).name()); } /////////////////////////////////////// // \brief Do Not Allow Lua or Luabridge to delete us void DoNotGarbageCollect(void*) {}它们的用途:
.deriveClass<Sprite, DisplayObject>("Sprite") .addStaticFunction("__call", &DoNotCreate<Sprite>) .addFunction("__gc", (void (*) (Sprite*)) &DoNotGarbageCollect)要从C++调用Lua代码,您可以使用LuaRefs,这些引用(IIRC)基本上是可以成为任何Lua类型的变量。
如果您有兴趣,我发现Zerobrane是一个很好的调试器,您只需要添加套接字lua库即可。