lua 给userdata设置元表_C++/Lua高级交互

C代码:

g_Runtime->set_on_connection([](TCPConnection& conn) {
       //调用lua函数 on_connection(conn)
});

lua函数:

function on_connection(conn)
 print(conn:is_connect())
end

问题:

怎么在C/C++里调用一个参数是C++对象的lua函数,并且lua函数里,可以调用这个对象的C方法。

Solution:

通过userdata把对象的指针传递到lua的函数参数里。

lua_getglobal(L, "on_connection");
TCPConnection** ptr =(TCPConnection**) lua_newuserdata(L,sizeof(TCPConnection*));
*ptr=&conn;
lua_pcall(L, 1, 0, 0)

接下来lua代码里的conn:is_connect()其实是一个语法糖

conn:is_connect() = conn.is_connect(conn)

这样就很清晰了,conn是lua中的一个值,conn:is_connect()就是用 is_connect 作为key来索引conn。显然,这个值需要是一个函数。然后这个函数把conn当作参数做了一次调用。

那么我们需要做两件事情,

  1. C里存在一个符合is_connect需求的luac函数,参数为conn
  2. 让conn的is_connect能索引到这个函数

这样print(conn:is_connect())就能够正常工作了。

第一件事情简单,

int conn_is_connected(lua_State* L)
{
	TCPConnection* conn = *(TCPConnection**)lua_touserdata(L,1);
	bool state = conn->connected();
	lua_pushboolean(L,state);
	return 1;
}

第二件事情就有点复杂了,需要用到lua里metatable(元表)的机制。

元表官网是这么描述的:

Lua 中的每个值都可以有一个 元表。 这个 元表就是一个普通的 Lua 表, 它用于定义原始值在特定操作下的行为。 如果你想改变一个值在特定操作下的行为,你可以在它的元表中设置对应域。 例如,当你对非数字值做加操作时, Lua 会检查该值的元表中的 " __add" 域下的函数。 如果能找到,Lua 则调用这个函数来完成加这个操作。

以上可以看出,元表就是用来改变一个值在特定操作下的行为的,跟我们的需求对应。具体来说,我们需要把userdata这个值被“is_connect”当作key索引时,返回一个函数。这对应了lua元表里的“__index”元方法。

"index":索引table[key]。 当table不是表或是表table中不存在key这个键时,这个事件被触发。 此时,会读出table相应的元方法。
尽管名字取成这样, 这个事件的元方法其实可以是一个函数也可以是一张表。 如果它是一个函数,则以 table 和 key 作为参数调用它。 如果它是一张表,最终的结果就是以 key 取索引这张表的结果。

那么,我们要做的就是,创建一张元表,设置到conn上。这张元表的“_index”元方法在key为“is_connect”的时候,返回上面的conn_is_connected函数。

最终C代码:

static const struct luaL_Reg tcp_connection_meta[] =
{
	{ "is_connect",conn_is_connected },
	{ nullptr,nullptr }
};

void attach_tcpconnection(lua_State* L)
{
	luaL_newlib(L, tcp_connection_meta);
	lua_pushvalue(L, -1);
	lua_setfield(L, -2, "__index");
	lua_setmetatable(L, -2);
}

g_Runtime->set_on_connection([](TCPConnection& conn) {
    lua_getglobal(L, "on_connection");
    lua_pushuserdata(L, (void*)&conn);
    attach_tcpconnection(L);
    lua_pcall(L, 1, 0, 0);
});

这里我们把__index元方法赋值为了一张表tcp_connection_meta而不是一个函数,因为这样可以随意给lua里的conn对象添加新的函数。问题解决。

以上,打完,收工,撒花~

-----更新-----

文章原来用的是lightuserdata,后来被坑惨,lua里lightuserdata作用很有限,而且所有lightuserdata共享同一份元表,这一点官网文档也没写。在luawiki里有提到:

lua-users wiki: Light User Data​lua-users.org
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值