找回密码
 立即注册
查看: 631|回复: 12

c++向Lua中传递复杂的结构该如何做?

[复制链接]
发表于 2021-9-25 19:21 | 显示全部楼层 |阅读模式
比如结构体,map之类的东西,希望有个例子
发表于 2021-9-25 19:29 | 显示全部楼层
我自己实现了一个C++的reflection机制,然后自动转化成表传过去。在lua中, map, array, object是同一类东西,都可以用表来表示。

当然如果你的数据结构比较大,而每次同步的部分数据量又比较少,直接用metatable 将一个C++对象域映射到lua的元方法上就好了。

思路:
由于C++没有原生的反射机制,所以你得照样子去做一个。举个例子,你有个类A:
class A : public LuaObject {
public:
     virtual void RetriveAllMethods(LuaState* L);
     int value;
     double another;
};

void A::RetriveAllMethods(luaState* L) {
      RegisterMember(value); // 主动反射所有成员
      RegisterMember(another);
}

其中RegisterMember可以这样实现:
#define RegisterMember(name) \
     _Register(L, this, &name, #name);

然后:
template <class P>
struct Proxy {
void OnGet(lua_State* L, void* v) {
     asset(false);
}
};

// 每个LUA支持的原生类型都写个
template <>
struct Proxy<double> {
void OnGet(lua_State* L, void* v) {
    *v = lua_tonumber(L, -1);
}
}

// 最后
template <class T, class P>
void _Register(lua_State* L, T* object, P* member, const char* name) {
       auto p = &Proxy<P>::OnGet;
       auto q = &Proxy<P>::OnSet; // 如法炮制
       // 把p, q指针保存下来,当有lua函数调用的时候调用它们
      BindLuaMethod(L, name, object, p, q, (long)member - (long)object);
}
发表于 2021-9-25 19:34 | 显示全部楼层
1,使用table比如在C++里有一个Point对象,内含x,y两个float,可以创建一个lua table,将字段名(x, y)设置为table的key,key的值设置为x, y的数值,涉及到lua_newtable合lua_setfield这几个API。这是很通用的方法,但是如果你要导出给Lua的对象非常多并且结构又不固定的话这种方法就得编写很多操作table的代码。
这个套路衍生出诸如luabind和luabridge这种桥接两端代码的库。
2,通用序列化协议要导出的数据结构变化大又很多的时候可以考虑使用JSON这样的通用序列化协议,C++里把map/vector序列化为JSON字符串(可以使用json11https://github.com/dropbox/json11),然后当做一整个字符串调用lua_pushstring传给Lua虚拟机,在Lua端解码JSON是非常方便的,比如使用dkjson LuaDist/dkjson · GitHub,收到字符串后只需要调用json.decode就可以解析出你要的table。
这个套路不适合需要高性能的场合。
3,FFI (Foreign function interface)这个就得使用LuaJIT了,参考它的ffi tutorial http://luajit.org/ext_ffi_tutorial.html简而言之,就是它可以不用你写C binding代码就可以调用你的C 函数,并且能够操作C struct,需要注意的是它支持的C ABI而不是C++。
4,模块间交互
当你的C++代码需要导出很多数据结构给Lua的时候,说明你的Lua跟C++之间的粘合层已经太重了,这会增加后期的维护成本,我个人是推荐把粘合层做薄一点,让lua逻辑代码和C++交户的API做到最小子集。
发表于 2021-9-25 19:44 | 显示全部楼层
把每个需要访问的成员的类型都包装一个lua类型,然后把成员名包装成访问函数。
最好把所有需要暴露的类型都做成内嵌引用计数的,这样能够以最自然的方式同时管理好脚本端和宿主端的对象所有权。
另外,Perl有magic机制,让你在Perl端用下标访问obj->{"member"}的时候,实际上是在调用访问函数。不知道Lua有没有类似的机制。
发表于 2021-9-25 19:49 | 显示全部楼层
对于结构体,用 struct 库,将数据 pack 到 string。lua 5.3 中已经加入到内建的 string 库中。
对于类,用 userdata + 元表。
发表于 2021-9-25 19:59 | 显示全部楼层
如果在应用中这种需求比较频繁,你需要使用一个lua binder。
Binder的作用就是将宿主语言的数据包装成Lua可访问的对象(table或userdata)。

http://lua-users.org/wiki/BindingCodeToLua
发表于 2021-9-25 20:08 | 显示全部楼层
用userdata, 参考 Programming in Lua : 28.1

还可以考虑直接ffi,参考FFI Tutorial
发表于 2021-9-25 20:12 | 显示全部楼层
idlcpp : C++混合编程辅助工具, 方便将C++功能导出到脚本语言如Lua,Python中使用,本人作品,详见 Kunana - 博客园
发表于 2021-9-25 20:19 | 显示全部楼层
    tolua++ 适合批量导出, stl支持不太好.sol2 目前最好的bind, 支持function stl 等复杂结构.
发表于 2021-9-25 20:24 | 显示全部楼层
userdata 转table
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-11-24 17:00 , Processed in 0.112684 second(s), 25 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表