c中获取lua的函数(不lua回调函数 带参数),只为在c中将lua的函数传递下去

C模块回调Lua函数的两种方法 - 简书
C模块回调Lua函数的两种方法
lua和C通过虚拟栈这种交互方式简单而又可靠,缺点就是C做栈平衡稍微会多写一点代码。今天分享学到的C模块回调Lua函数的两种方法,都是炒冷饭,大侠勿喷。
1. C保存函数对象
C模块可以通过注册表保存Lua里面的对象,等适当时候取出再调用即可。
static int lua_callback = LUA_REFNIL;
static int setnotify(lua_State *L)
lua_callback = luaL_ref(L, LUA_REGISTRYINDEX);
static int testnotify(lua_State *L)
lua_rawgeti(L, LUA_REGISTRYINDEX, lua_callback);
lua_call(L, 0, 0);
luaL_ref把栈顶的值取出,方的指定的tabel中,然后返回一个索引(目测是数组的index)。lua_rawgeti把之前保存的function对象取出,再由lua_call调用。
function callback(
print "Callback"
cb.setnotify(callback)
cb.testnotify()
2. C访问Lua全局环境
第二种方法更简便,C自己直接Lua中的函数,就像Lua调用C一样
static int testenv(lua_State *L)
lua_getglobal(L, "defcallback");
lua_call(L, 0, 0);
该方法的缺点就是如果C模块独立编写,方法名就不太灵活。用这种方法一般会在Lua端再封装一层,以隔离全局环境。
3. 完整例子
#include &stdio.h&
#include &stdlib.h&
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
static int lua_callback = LUA_REFNIL;
static int setnotify(lua_State *L)
printf("111 %d\n", lua_callback);
lua_callback = luaL_ref(L, LUA_REGISTRYINDEX);
printf("111 %d\n", lua_callback);
static int testnotify(lua_State *L)
lua_rawgeti(L, LUA_REGISTRYINDEX, lua_callback);
lua_call(L, 0, 0);
static int testenv(lua_State *L)
lua_getglobal(L, "defcallback");
lua_call(L, 0, 0);
static const luaL_Reg cblib[] = {
{"setnotify", setnotify},
{"testnotify", testnotify},
{"testenv", testenv},
{NULL, NULL}
int luaopen_cb(lua_State *L)
luaL_register(L, "cb", cblib);
{% endcodeblock %}
{% codeblock test.lua lang:lua %}
require("cb")
function callback(
print "Callback"
function defcallback()
print "Predef callback"
cb.setnotify(callback)
cb.testnotify()
print "Done"
cb.testenv()这些东西是平时遇到的, 觉得有一定的价值, 所以记录下来, 以后遇到类似的问题可以查阅, 同时分享出来也能方便需要的人, 转载请注明来自RingOfTheC[ring.of.the.]
这里, 简单的记录一下lua中闭包的知识和C闭包调用
前提知识: 在lua api小记2中已经分析了lua中值的结构, 是一个 TValue{value, tt}组合, 如果有疑问, 可以去看一下
一些重要的数据结构
&&& lua中有两种闭包, c闭包和lua闭包
&&& 两种闭包的公共部分:
&&&&&& #define ClosureHeader CommonH&&&& lu_byte isC;&&&&&&& lua_ GCObject*&&&&&& struct Table env
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& /*是否是C闭包*/&&&& /*upval的个数*/&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& /* 闭包的env, set/getenv就是操纵的它 */
&&& C闭包的结构
&&&&&&& struct CClosure{
&&&&&&&&&&&& ClosureH
&&&&&&&&&&&& lua_CF
&&&&&&&&&&&& TValue upvalue[1];
&&&&& 结构比较简单, f是一个满足 int lua_func(lua_State*) 类型的c函数
&&&&& upvalue是创建C闭包时压入的upvalue, 类型是TValue, 可以得知, upvalue可以是任意的lua类型
&&& Lua闭包结构
&&&&&& struct LClosure{
&&&&&&&&&& ClosureH
&&&&&&&&&& strcut Proto*
&&&&&&&&&& UpVal* upvals[1];
&&&&& Proto的结构比较复杂, 这里先不做分析
&& 统一的闭包结构, 一个联合体, 说明一个闭包要么是C闭包, 要么是lua闭包, 这个是用isC表识出来的.
&&&&&& union Closure{
&&&&&&&&&&& CC
&&&&&&&&&&& LClosure&
纠结的闭包
&&&&&& 为什么大家叫闭包, 不叫它函数, 它看起来就是函数啊? 为什么要发明一个"闭包"这么一个听起来蛋疼的词呢? 我也纠结在这里好久了, 大概快一年半了吧~~~=.=我比较笨~~~随着看源码, 现在想通了, 拿出一些的自己在研究过程中的心得[尽量的通俗易懂]:
&&&&&& 1. c 语言中的函数的定义: 对功能的抽象块, 这个大家没什么异议吧.
&&&&&& 2. lua对函数做了扩展:
&&&&&&&&&&&&& a. 可以把几个值和函数绑定在一起, 这些值被称为upvalue.
&&&&&&&&&&&&& ps:& 可能有人觉得c++的函数对象也可以把几个值和函数绑定起来啊, 是这样的, 但是这个问题就像是"在汇编中也可以实现面向对象呀"一样, lua从语言层面对upvalue提供了支持, 就像c++/java从语言层面提供了对类, 对象的支持一样, 当然大大的解放了我们程序员的工作量, 而且配上lua动态类型, 更是让人轻松了不少.
&&&&&&&&&&&&& b. 每个函数可以和一个env(环境)绑定.
&&&&&&&&&&&&& ps:& 如果说上面的upvalue还能在c++中coding出来, 那么env 上下文环境这种动态语言中特有的东西c++就没有明显的对应结构了吧? 可能有人觉得lua是c写的, 通过coding也可以实现, 好吧=.= , "能做和做"是两码事, 就想你能步行从北京到上海, 不表明你就必须要这么做. env是非常重要和有用的东西, 它可以轻松创造出一个受限的环境, 就是传说中的"沙盒", 我说的更通俗一点就是"一个动态名字空间机制". 这个先暂时不分析.
&&&&&& 好了, 现在我们看到
&&&&&&&&&&& c&&&&&& 函数&&& { 功能抽象 }
&&&&&&&&&&& lua&&& 闭包&&&& {功能抽象, upvalue, env}
&&&&&&&&&&& 重点: 闭包 == {功能抽象, upvalue, env}
&&&&&& 看到这里, 大家都明白了, 如果把lua中的{功能抽象, upvalue, env}也称为函数, 不但容易引起大家的误解以为它就是和c函数一样, 而且它确实不能很好的表达出lua函数的丰富内涵, 闭包, "闭" 是指的它是一个object, 一个看得见摸得着的东西, 不可分割的整体(first class); "包" 指的是它包含了功能抽象, upvalue, env. 这里一个很有趣的事实就是, {功能抽象, upvalue, env}是很多动态语言的一个实现特征, 比如lua, javascript都有实现这样的结构, 它是先被实现出来, 然后冠以"闭包"这样一个名称. 所以, 你单单想去理解闭包这个词的话, 基本是没有办法理解的, 去网上查闭包, 没用, 你能查到的就是几个用闭包举出的例子, 看完以后保证你的感觉是"这玩意挺神秘的, 但是还是不懂什么是闭包", 为什么不懂?& 因为它指的是一种实现结构特征, 是为了实现动态语言中的函数first class和上下文概念而创造出来的.
&&&&&& 宁可多说几句, 只要对加深理解有好处就行, 有这样两个个句子"我骑车去买点水果" "我用来闭包{功能抽象, upvalue, env}实现动态语言中的函数first class和上下文概念" , 闭包和"骑车"都是你达到目地的一种手段, 为了买水果你才想了"骑车"这样一个主意, 并不是为了骑车而去买水果. 只把把眼睛盯在骑车上是不对的, 它只是手段.
向lua中注册c函数的过程是通过lua_pushcclosure(L, f, n)函数实现的
&&&&&& 流程:& 1. 创建一个 sizeof(CClosure) + (n - 1) * sizeof(TValue)大小的内存, 这段内存是 CClosure + TValue[n], 并做gc簿记[这点太重要了, 为什么lua要控制自己世界中的所有变量, 就是因为它要做gc簿记来管理内存],& isC= 1 标示其是一个C闭包.
&&&&&&&&&&&& 2. c-&f = f绑定c函数.&&& ---------& 闭包.功能抽象 = f
&&&&&&&&&&&& 3. env = 当前闭包的env[这说明了被创建的闭包继承了创建它的闭包的环境].& ----------- 闭包.env = env
&&&&&&&&&&&& 4. 把栈上的n个元素赋值到c-&upvalue[]数组中, 顺序是越先入栈的值放在upvalue数组的越开始位置, c-&nupvalues指定改闭包upvalue的个数.& ---------- 闭包.upvalue = upvalue
&&&&&&&&&&&& 5. 弹出栈上n个元素, 并压入新建的Closure到栈顶.
&&&&&& 整个流程是比较简单的, 分配内存, 填写属性, 链入gc监控, 绑定c函数, 绑定upvalue, 绑定env一个C闭包就ok了, 请结合上面给的闭包的解释, 很清楚了.
现在来解析这个C闭包被调用的过程[注意, 这里只涉及C闭包的调用]
&&&&&& lua 闭包调用信息结构:
&&&&&&&&&&& struct CallInfo {&&&&&&&&&&&&&&&& StkI& /* base for this function */&&&& ---- 闭包调用的栈基&&&&&&&&&&&&&&&& StkI& /* function index in the stack */& ---- 要调用的闭包在栈上的位置&&&&&&&&&&&&&&&& StkId&&&& /* top for this function */&&&& ---- 闭包的栈使用限制, 就是lua_push*的时候得看着点, push太多就超了, 可以lua_checkstack来扩&&&&&&&&&&&&&&&& const Instruction *&&&&& ---- 如果在本闭包中再次调用别的闭包, 那么该值就保存下一条指令以便在返回时继续执行&&&&&&&&&&&&&&&&& /* expected number of results from this function */&& ---- 闭包要返回的值个数&&&&&&&&&&&&&&&&& /* number of tail calls lost under this entry */&& ---- 尾递归用, 暂时不管&&&&&&&&&&& }
&&&&&&& 从注释就可以看出来, 这个结构是比较简单的, 它的作用就是维护一个函数调用的有关信息, 其实和c函数调用的栈帧是一样的, 重要的信息base && ebp, func && 要调用的函数的栈index, savedpc && eip, top, nresults和tailcalls没有明显的对应.
&&&&&&& 在lua初始化的时候, 分配了一个CallInfo数组, 并用L-&base_ci指向该数组第一个元素, 用L-&end_ci指向该数组最后一个指针, 用L-&size_ci记录数组当前的大小, L-&ci记录的是当前被调用的闭包的调用信息.
&&&&&&& 下面讲解一个c闭包的调用的过程:
&&&&&&& 情景: c 函数 int lua_test(lua_State* L){
&&&&&&&&&&&&&&&&&&&&&&&&& int a = lua_tonumber(L, 1);
&&&&&&&&&&&&&&&&&&&&&&&&& int b = lua_tonumber(L, 2);
&&&&&&&&&&&&&&&&&&&&&&&&& a = a +
&&&&&&&&&&&&&&&&&&&&&&&&& lua_pushnumber(L, a);
&&&&&&&&&&&&&&&& }
&&&&&&&&&&&&&&&& 已经注册到了lua 中, 形成了一个C闭包, 起名为"test", 下面去调用它
&&&&&&&&&&&&&&&& luaL_dostring(L, "c = test(3, 4)")
&&&&&&&& 1. 首先, 我们把它翻译成对应的c api
&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 1. 最初的堆栈
&&&&&&&&&&&&&& lua_getglobal(L, &test&)
&&&&&&&&&&&&&& lua_pushnumber(L, 3)
&&&&&&&&&&&&&& lua_pushnumber(L, 4)&&&&&&
&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 2. 压入了函数和参数的堆栈
&&&&&&&&&&&&&& lua_call(L, 2, 1)
&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 3. 调用lua_test开始时的堆栈
&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 4. 调用结束的堆栈
&&&&&&&&&&&&&& lua_setglobal(L, &c&)&&&&&
&&&&&&&&&&&&
&&&&&&&&&&&&&&&&&&&&&&&&&&&&&& 5. 取出调用结果的堆栈
&&&&&&& 我们重点想要知道的是lua_call函数的过程
&&&&&&&&&&& 1. lua的一致性在这里再一次的让人震撼, 不管是dostring, 还是dofile, 都会形成一个闭包, 也就是说, 闭包是lua中用来组织结构的基本构件, 这个特点使得lua中的结构具有一致性, 是一种简明而强大的概念.
&&&&&&&&&&& 2. 根据1, a = test(3, 4)其实是被组织成为一个闭包放在lua栈顶[方便期间, 给这个lua闭包起名为bb], 也就说dostring真正调用的是bb闭包, 然后bb闭包执行时才调用的是test
&&&&&&&& [保存当前信息到当前函数的CallInfo中]
&&&&&&&&&&& 3. 在调用test的时刻, L-&ci记载着bb闭包的调用信息, 所以, 先把下一个要执行的指令放在L-&ci-&savedpc中, 以供从test返回后继续执行.
&&&&&&&&&&& 4. 取栈上的test C闭包 cl, 用 cl-&isC == 1断定它的确是一个C闭包
&&&&&&&& [进入一个新的CallInfo, 布置堆栈]
&&&&&&&&&&& 5. 从L中新分配一个CallInfo ci来记录test的调用信息, 并把它的值设置到L-&ci, 这表明一个新的函数调用开始了, 这里还要指定test在栈中的位置, L-&base = ci-&base = ci-&func+1, 注意, 这几个赋值很重要, 导致的堆栈状态由图2转化到图3, 从图中可以看出, L-&base指向了第一个参数, ci-&base也指向了第一个参数, 所以在test中, 我们调用lua_gettop函数返回的值就是2, 因为在调用它的时候, 它的栈帧上只有2个元素, 实现了lua向c语言中传参数.
&&&&&&& [调用实际的函数]
&&&&&&&&&&& 6. 安排好堆栈, 下面就是根据L-&ci-&func指向的栈上的闭包(及test的C闭包), 找到对应的cl-&c-&f, 并调用, 就进入了c函数lua_test
&&&&&&& [获取返回值调整堆栈, 返回原来的CallInfo]
&&&&&&&&&&& 7. 根据lua_test的返回值, 把test闭包和参数弹出栈, 并把返回值压入并调整L-&top
&&&&&&&&&&& 8. 恢复 L-&base, L-&ci 和 L-&savedpc, 继续执行.
&&&&&&& 总结: 调用一个新的闭包时 1. 保存当前信息到当前函数的CallInfo中 2. 进入一个新的CallInfo, 布置堆栈& 3. 调用实际的函数& 4. 获取返回值调整堆栈, 返回原来的CallInfo
短短续续一共写了6个小时......... 先写到这里吧, 对了, 我是开始写blog不久, 而且表达能力有限, 大家如果要看的话取其精华, 舍弃糟粕吧:)
阅读(...) 评论()用户名:hoytluo
文章数:19
评论数:18
访问量:54887
注册日期:
阅读量:1297
阅读量:3317
阅读量:582592
阅读量:467368
[匿名]goodidea:
51CTO推荐博文
近来研究Lua这个东西,官网为
主要的目的是通过lua来对短信发送过来的业务报文转换为业务系统支持的格式,设计的思想是Lua进行业务报文的转换,报文的通讯采用c实现,因为要通过C实现SGIP协议报文的封装和到后台业务的转换。
C调用Lua函数
首先要进行Lua的初始化,这个主要是lua_open和luaL_openlibs函数
然后是解析并编译lua的代码,这个主要是luaL_dofile函数
解析好之后使用lua_getglobal指明要调用的lua函数
如果有lua函数的参数,通过使用lua_pushstring函数传递参数
最后调用lua_pcall进行lua函数的调用
调用完成之后采用lua_tonumber类函数可以获取到函数的返回结果
Lua调用C函数
在Lua中调用C的函数,该函数必须进行注册,这个通过lua_register这个函数来完成
在Lua中调用注册的函数,会调用上面注册的函数(类似于回调),所有的处理在这个函数里面
这个函数里面可以使用lua_tostring类函数来获取函数的参数
如果有返回值,通过lua_pushnumber这个函数来返回。
参考的代码
下面的这个代码是用来将联通的短信进行报文的转换,同时对Lua注册了一个SendSms函数用来可以在Lua中发送短信。
#include &lua.h&
#include &lauxlib.h&
#include &lualib.h&
#define cutil_log_error&printf
#define cutil_log_debug&printf
&* LuaSendSms
&* 当Lua里面调用SendSms函数时候会触发该函数
static int LuaSendSms(lua_State *L)
&char *pszPhone = &&;
&char *pszMessage = &&;
&// 读取并判读参数
&if(lua_gettop(L) != 2)
&&lua_pushstring(L, &Incorrect argument number!&);
&&lua_error(L);
&if(!lua_isnumber(L, 1) || !lua_isstring(L, 2))
&&lua_pushstring(L, &Incorrect argument!&);
&&lua_error(L);
&pszPhone = lua_tostring(L, 1);
&pszMessage = lua_tostring(L, 2);
&// 发送消息 TODO
&cutil_log_debug(&Send Message %s to %s.\n&, pszMessage, pszPhone);
&// 返回结果
&&& lua_pushnumber(L, 0);
&// 只有一个返回结果
&return 1;
&* LuaProcessSms
&* 调用Lua里面的LuaMain函数进行消息的处理
static int LuaProcessSms(lua_State *L, char *pszPhone, char *pszMessage)
&char *pszRetI
&cutil_log_debug(&process phone %s message %s\n&, pszPhone, pszMessage);
&// 调用函数和参数
&lua_getglobal(L, &LuaMain&);
&lua_pushstring(L, pszPhone);
&lua_pushstring(L, pszMessage);
&if(nRet = lua_pcall(L, 2, 2, 0))
&&cutil_log_error(&lua_pcall error ret %d error '%s'. phone %s message %s\n&,
&&&nRet, lua_tostring(L, -1), pszPhone, pszMessage);
&&lua_settop(L, 0);
&&return -1;
&// 判读返回参数合法性
&if(lua_gettop(L) != 2)
&&cutil_log_error(&Incorrect return number. %d phone %s message %s\n&,
&&&lua_gettop(L), pszPhone, pszMessage);
&&lua_settop(L, 0);
&&return -1;&
&if(!lua_isnumber(L, 1) || !lua_isstring(L, 2))
&&cutil_log_error(&Incorrect return. arg1 %s arg2 %s phone %s message %s\n&,
&&&lua_tostring(L, 1), lua_tostring(L, 2),
&&&pszPhone, pszMessage);
&&lua_settop(L, 0);
&&return -1;
&// 获取并处理返回信息 TODO
&nRet = lua_tonumber(L, 1);
&pszRetInfo = strdup(lua_tostring(L, 2));
&lua_settop(L, 0);
&cutil_log_debug(&Ret %d Info %s\n&, nRet, pszRetInfo);
&if(nRet != 0)
&&cutil_log_error(&process info error phone %s message %s ret %d info %s\n&,
&&&pszPhone, pszMessage, nRet, pszRetInfo);
&&free(pszRetInfo);
&&return -1;
&free(pszRetInfo);
&return 0;
&* LuaInit
&* 初始化Lua库,并注册一个SendSms函数
static lua_State *LuaInit()
&lua_State *L = lua_open();
&if(L == NULL)
&&cutil_log_error(&Init the lua library error\n&);
&&return NULL;
&luaL_openlibs(L);
&&& /* register our function */
&&& lua_register(L, &SendSms&, LuaSendSms);
&return L;
int main()
&lua_State *L;
&L = LuaInit();
&if (luaL_dofile(L, &sms.lua&))
&&error(L, &cannot run configuration file: %s&,
& &&lua_tostring(L, -1));
&for(i = 0; i & 1; i ++)
&&LuaProcessSms(L, &&, &测试消息&);
&lua_close(L);
下面是参考的Lua程序
-- 函数名称必须为LuaMain
-- 函数里面可以调用SendSms进行消息发送,第一个参数是手机号码,第二个参数是内容
-- 函数的参数有两个phone是手机号码 message是接收到的短信内容
-- 返回参数有两个 第一个是返回值0表示成功,其他失败,第二个是要求发送的内容
function LuaMain(phone, message)
&print(&phone &, phone, &message&, message)
&SendSms('', '你好')
&return 0,'KZCZ函数的参数有两个phone是手机号码 message是接收到的短信内容'
-- 这个里面可以有全局的代码,不过建议不要编写
-- 全局的代码会在LuaMain函数之前运行
&本文出自 “” 博客,请务必保留此出处
了这篇文章
类别:┆阅读(0)┆评论(0)C模块回调Lua函数的两种方法(转)
我的图书馆
C模块回调Lua函数的两种方法(转)
版权所有,转载务必保留此链接&
C模块回调Lua函数的两种方法
lua和C通过虚拟栈这种交互方式简单而又可靠,缺点就是C做栈平衡稍微会多写一点代码。 今天分享学到的C模块回调Lua函数的两种方法,都是炒冷饭,大侠勿喷。
1. C保存函数对象
C模块可以通过注册表保存Lua里面的对象,等适当时候取出再调用即可。
static int lua_callback = LUA_REFNIL;
static int setnotify(lua_State *L)
lua_callback = luaL_ref(L, LUA_REGISTRYINDEX);
static int testnotify(lua_State *L)
lua_rawgeti(L, LUA_REGISTRYINDEX, lua_callback);
lua_call(L, 0, 0);
luaL_ref把栈顶的值取出,放到指定的tabel中,然后返回一个索引(目测是数组的index)。 lua_rawgeti把之前保存的function对象取出,再由lua_call调用。
function callback(
print "Callback"
cb.setnotify(callback)
cb.testnotify()
2. C访问Lua全局环境
第二种方法更简便,C直接调用Lua中的函数,就像Lua调用C一样
static int testenv(lua_State *L)
lua_getglobal(L, "defcallback");
lua_call(L, 0, 0);
该方法的缺点就是如果C模块独立编写,方法名就不太灵活。 用这种方法一般会在Lua端再封装一层,以隔离全局环境。
3. 完整例子
#include &stdio.h&
#include &stdlib.h&
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
static int lua_callback = LUA_REFNIL;
static int setnotify(lua_State *L)
lua_callback = luaL_ref(L, LUA_REGISTRYINDEX);
static int testnotify(lua_State *L)
lua_rawgeti(L, LUA_REGISTRYINDEX, lua_callback);
lua_call(L, 0, 0);
static int testenv(lua_State *L)
lua_getglobal(L, "defcallback");
lua_call(L, 0, 0);
static const luaL_Reg cblib[] = {
{"setnotify", setnotify},
{"testnotify", testnotify},
{"testenv", testenv},
{NULL, NULL}
int luaopen_cb(lua_State *L)
luaL_register(L, "cb", cblib);
require("cb")
function callback(
print "Callback"
function defcallback()
print "Predef callback"
cb.setnotify(callback)
cb.testnotify()
print "Done"
cb.testenv()
TA的最新馆藏[转]&[转]&[转]&[转]&[转]&[转]&
喜欢该文的人也喜欢}

我要回帖

更多关于 lua中的回调函数 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信