通过栈恢复脚本函数间参数传递

来源:互联网 发布:屏蔽短信端口发送0000 编辑:程序博客网 时间:2024/06/06 15:46

最近处理了一个客户端同事的需求,大体上是这样的: 在一个脚本里调用另一个窗口的 方法 button_down 一类的, 但如果所调用窗口没有被初始化过则需要先绕到c里面去初始化这个窗口,并且通过窗口对象加载其相应的脚本( 即包含 button_down 定义 的脚本) ,然后再调用 此脚本函数。 换一种描述 就是 当函数不存在时自动加载此函数。

第一反应是通过脚本做一个调用壳,在壳里实现判断 函数是不是 nil ,然后对应加载处理。 实现上简单,但面临2个问题,第一个是 按需求来讲,光加载函数并不能达到期望,因为窗口还没初始化, 第二个是 传递给 窗口函数的参数类型和个数不确定,怎么在壳里在加载这个函数的同时能原封不动的传递一下上层的参数,实现一个 类似C++里为传递变参容器va_list的功能 。

在处理第一个问题上,虽然可以额外提供给脚本一个 初始化窗口的接口, 但这样一来窗口和脚本 谁更早存在的 逻辑顺序就得对 脚本 透明,否则会面临初始化一个窗口时其依赖的父窗口还未初始化这种可能,不是一个太安全的方法。 所以这块壳操作还是得交个C里面的封装去做,在uiengine里面维护一份树形信息,保存各个窗口的关系及其对应脚本,方便在加入的时候能只针对本次调用加载所需的未加入脚本,达到最小加载的目的

处理第二个问题就没这么容易了,因为脚本里面 缺乏能传递变参的机制 (或是说目前我还没找到合适的),即便在加载完了所需脚本以后,之前传递的参数已经被壳给拦下来了想要再传递一次就只有调用窗口函数的上层才知道类型和个数了。按处理第一个问题时确定的方式,这层壳是在C里实现的,其实窗口函数所需的参数已经压入栈里了,能不能由壳函数在栈上只提取出窗口函数名这几个参数,而把剩下的参数利用栈再次返回给lua呢(即 lua 通过栈 传给 c的参数 c用完头几个所需的 剩下的参数 仍旧按原来的顺序 再次让lua函数调用) 经过测试证明 这种方式是可行的:

void shell_call(char* funcname){lua_remove(L,1);int count = lua_gettop(L);lua_pushstring(L,funcname);lua_gettable(L,LUA_GLOBALSINDEX);while(false == lua_isfunction(L,-1)){string _filename = findfuncfile(funcname);lua_tinker::dofile(L,_filename.c_str());lua_remove(L,-1);lua_pushstring(L,funcname);lua_gettable(L,LUA_GLOBALSINDEX);}lua_insert(L,1);if (lua_pcall(L,count,0,0) != 0){lua_pop(L,1);}}
其中L 为你脚本所在的 Lua_state

第一个lua_remove是从栈底上去掉函数名字 ,接着用lua_gettop 算出传递给窗口函数的参数个数,然后把名字重新压入栈顶,从全局域下取得对应的函数

while 循环的作用是从 findfuncfile里面对 未找到的 funcname的加载其对应的的脚本, 理论上可以用if 替换while,但 由于 findfuncfile也可能是一个和用户交互的过程而用户可能输入错误脚本所以用while循环实现比较好

接下来的 lua_insert 是 把找到的lua functor 对象 从栈顶交换到栈底, 因为接下来的 lua_pcall要求 栈上的顺序是 functor + 参数1 + 参数2 +... 的排列顺序

lua_pcall 里面的 count 是刚刚取得的参数个数, 调用函数成功以后 lua_pop(L,1)将栈清空


通过lua_tinker::def(L,"shell_call",shell_call) 将这个壳注入脚本环境, 就可以通过 shell("button_down ",1,posx,posy) 这种调用到未加载的脚本函数了


原创粉丝点击