首页IT科技lua调用c++函数(ua5.4源码剖析:三. C++与Lua相互调用)

lua调用c++函数(ua5.4源码剖析:三. C++与Lua相互调用)

时间2025-08-05 17:19:54分类IT科技浏览5227
导读:概述 从本质上来看,其实说是不存在所谓的C++与lua的相互调用。lua是运行在C上的,简单来说lua的代码会被编译成字节码在被C语言的语法运行。在C++调用lua时,其实是解释运行lua文件编译出来的字节码。lua调用C++其实还是解释运行lua文件编译出来的字节码的语义是调用lua栈上的C++函数。...

概述

从本质上来看              ,其实说是不存在所谓的C++与lua的相互调用               。lua是运行在C上的                      ,简单来说lua的代码会被编译成字节码在被C语言的语法运行                     。在C++调用lua时       ,其实是解释运行lua文件编译出来的字节码       。lua调用C++其实还是解释运行lua文件编译出来的字节码的语义是调用lua栈上的C++函数        。

示例

来看下面这段代码:

C++ #include "Inc/lua.h" #include "Inc/lauxlib.h" #include "Inc/lualib.h" #include "Inc/lobject.h" } using std::cout; using std::endl; int CAdd(lua_State* L) { int a = lua_tonumber(L, 2); int b = lua_tonumber(L, 1);; int sum = a + b; lua_pushnumber(L, sum); return 1; } int main() { lua_State* L = luaL_newstate(); luaL_openlibs(L); lua_register(L, "CAdd", CAdd); int stat = luaL_loadfile(L, "Test.lua") | lua_pcall(L, 0, 0, 0); if (stat) { cout << "error" << endl; } else { cout << "succ" << endl; } lua_close(L); return 0; }

lua

local x = CAdd(1, 2) print("x = " .. tostring(x))

运行结果:

考虑上述C++代码luaL_loadfile去加载并调用lua              ,lua又调用了C++注册到lua虚拟机里的CAdd函数并正确打印了返回值                      ,结果如图所示                     。到底发生了什么?

C++调用lua

C++调用lua时       ,是对lua代码进行编译生成字节码       ,在运行时对字节码使用C的语法解释运行              。

对luaL_loadfile调试                      ,跟到f_parser: static void f_parser (lua_State *L, void *ud) { LClosure *cl; struct SParser *p = cast(struct SParser *, ud); int c = zgetc(p->z); /* read first character */ if (c == LUA_SIGNATURE[0]) { checkmode(L, p->mode, "binary"); cl = luaU_undump(L, p->z, p->name); } else { checkmode(L, p->mode, "text"); cl = luaY_parser(L, p->z, &p->buff, &p->dyd, p->name, c); } lua_assert(cl->nupvalues == cl->p->sizeupvalues); luaF_initupvals(L, cl); }

简单来说               ,parser根据输入进行词法       ,语法分析进行编码生成闭包                     ,然后推入栈中等待调用        。来看几个用到的数据结构                      。

LClosure typedef struct LClosure { ClosureHeader; struct Proto *p; UpVal *upvals[1]; //被捕获的外局部变量 } LClosure;

这是lua的闭包               ,此外还有CClosure是c的闭包,下面lua调用C++会提到                     ,它们被Closure联合体包裹              。

Proto

typedef struct Proto { CommonHeader; lu_byte numparams; /* number of fixed parameters */ lu_byte is_vararg; lu_byte maxstacksize; /* number of registers needed by this function */ int sizeupvalues; /* size of upvalues */ int sizek; /* size of k */ int sizecode; int sizelineinfo; int sizep; /* size of p */ int sizelocvars; int linedefined; /* debug information */ int lastlinedefined; /* debug information */ TValue *k; /* constants used by the function */ Instruction *code; //codes struct Proto **p; /* functions defined inside the function */ int *lineinfo; /* map from opcodes to source lines (debug information) */ LocVar *locvars; /* information about local variables (debug information) */ Upvaldesc *upvalues; /* upvalue information */ struct LClosure *cache; /* last-created closure with this prototype */ TString *source; /* used for debug information */ GCObject *gclist; } Proto;

Instruction *code;注意这个变量                      ,这个变量就是指向我们编译后生成字节码数组的指针。

FuncState

typedef struct FuncState { Proto *f; /* current function header */ struct FuncState *prev; /* enclosing function */ struct LexState *ls; /* lexical state */ struct BlockCnt *bl; /* chain of current blocks */ int pc; /* next position to code (equivalent to ncode) */ int lasttarget; /* label of last jump label */ int jpc; /* list of pending jumps to pc */ int nk; /* number of elements in k */ int np; /* number of elements in p */ int firstlocal; /* index of first local var (in Dyndata array) */ short nlocvars; /* number of elements in f->locvars */ lu_byte nactvar; /* number of active local variables */ lu_byte nups; /* number of upvalues */ lu_byte freereg; /* first free register */ } FuncState;

FuncState互相是嵌套的,外部FuncState保存了内部的部分信息              ,最外部的FuncState的f成员保存了编译的所有字节码                      ,并传递给闭包LClosure                      。

编译lua流程

以加载lua脚本为例                     。

f_parser调用luaY_parser分析       ,并初始化Upvalues(外局部变量)。 luaY_parser 使用LexState包裹FuncState调用luaX_next进行进一步分析              ,其结果保存到Proto结构的code数组中                      ,传递给LClosure并推入栈中               。 luaX_next循环分析       ,依据词法       ,语法规则调用luaK_code生成字节码                     。

部分代码: static void statement (LexState *ls) { int line = ls->linenumber; /* may be needed for error messages */ enterlevel(ls); switch (ls->t.token) { case ;: { /* stat -> ; (empty statement) */ luaX_next(ls); /* skip ; */ break; } case TK_IF: { /* stat -> ifstat */ ifstat(ls, line); break; } //..................... } }

运行

编译代码后                      ,便可对闭包进行解析运行了       。调试代码上述 lua_pcall(L, 0, 0, 0) 代码               ,跟到luaD_call:

void luaD_call (lua_State *L, StkId func, int nResults) { if (++L->nCcalls >= LUAI_MAXCCALLS) stackerror(L); if (!luaD_precall(L, func, nResults)) /* is a Lua function? */ luaV_execute(L); /* call it */ L->nCcalls--; } }

首先调用luaD_precall进行预备工作       ,lua_state扩展base_ci(CallInfo类型)数组创建一个新元素保存括虚拟机的指令指针(lua_state->savedpc)在内的调用堆栈的状态以便调用结束后恢复调用堆栈                     ,并把指令指针指向该闭包的指令数组(Closure->p->codes)               。

然后调用luaV_execute循环取出指令运行                     。        。

luaV_execute解释执行部分代码: void luaV_execute (lua_State *L) { CallInfo *ci = L->ci; LClosure *cl; TValue *k; StkId base; ci->callstatus |= CIST_FRESH; /* fresh invocation of luaV_execute" */ newframe: /* reentry point when frame changes (call/return) */ lua_assert(ci == L->ci); cl = clLvalue(ci->func); /* local reference to functions closure */ k = cl->p->k; /* local reference to functions constant table */ base = ci->u.l.base; /* local copy of functions base */ /* main loop of interpreter */ for (;;) { Instruction i; StkId ra; vmfetch(); vmdispatch (GET_OPCODE(i)) { vmcase(OP_MOVE) { setobjs2s(L, ra, RB(i)); vmbreak; } //............................ } }

CallInfo

函数执行时               ,lua_state通过CallInfo 数据结构了解函数的状态信息,并通过CallInfo组base_ci的上下生长来维护调用堆栈        。 typedef struct CallInfo { StkId func; /* function index in the stack */ StkId top; /* top for this function */ struct CallInfo *previous, *next; /* dynamic call link */ union { struct { /* only for Lua functions */ StkId base; /* base for this function */ const Instruction *savedpc; } l; struct { /* only for C functions */ lua_KFunction k; /* continuation in case of yields */ ptrdiff_t old_errfunc; lua_KContext ctx; /* context info. in case of yields */ } c; } u; ptrdiff_t extra; short nresults; /* expected number of results from this function */ unsigned short callstatus; } CallInfo;

lua调用C++

lua调用C++                     ,是上述C++调用lua时即c的语法解释运行lua代码生成的字节码的一种情况                      ,即取出lua状态机全局表中的CClosure中的函数指针运行                     。

来看下向lua状态机注册C++函数lua_register #define lua_pushcfunction(L,f) lua_pushcclosure(L, (f), 0) #define lua_register(L,n,f) (lua_pushcfunction(L, (f)), lua_setglobal(L, (n))) LUA_API void lua_pushcclosure (lua_State *L, lua_CFunction fn, int n) { lua_lock(L); if (n == 0) { setfvalue(s2v(L->top), fn); api_incr_top(L); } else { CClosure *cl; api_checknelems(L, n); api_check(L, n <= MAXUPVAL, "upvalue index too large"); cl = luaF_newCclosure(L, n); cl->f = fn; L->top -= n; while (n--) { setobj2n(L, &cl->upvalue[n], s2v(L->top + n)); /* does not need barrier because closure is white */ } setclCvalue(L, s2v(L->top), cl); api_incr_top(L); luaC_checkGC(L); } lua_unlock(L); }

可以看到这里最终创建了一个CCloseure,包裹住lua_CFunction类型的函数指针并推入栈顶和放入全局表中              。

typedef int (*lua_CFunction) (lua_State *L); typedef struct CClosure { ClosureHeader; lua_CFunction f; TValue upvalue[1]; /* list of upvalues */ } CClosure;

可以看到CClosure包含了一个lua_CFunction类型的函数指针和upvalue的链表

解释运行调用语义

循环解释字节码语义的关于调用的部分

void luaV_execute (lua_State *L, CallInfo *ci) { //... vmcase(OP_CALL) { int b = GETARG_B(i); int nresults = GETARG_C(i) - 1; if (b != 0) /* fixed number of arguments? */ L->top = ra + b; /* top signals number of arguments */ /* else previous instruction set top */ ProtectNT(luaD_call(L, ra, nresults)); vmbreak; } //... }

可以看到调用语义的解释调用了luaD_call

void luaD_call (lua_State *L, StkId func, int nresults) { lua_CFunction f; retry: switch (ttypetag(s2v(func))) { case LUA_VCCL: /* C closure */ f = clCvalue(s2v(func))->f; goto Cfunc; case LUA_VLCF: /* light C function */ f = fvalue(s2v(func)); Cfunc: { int n; /* number of returns */ CallInfo *ci = next_ci(L); checkstackp(L, LUA_MINSTACK, func); /* ensure minimum stack size */ ci->nresults = nresults; ci->callstatus = CIST_C; ci->top = L->top + LUA_MINSTACK; ci->func = func; L->ci = ci; lua_assert(ci->top <= L->stack_last); if (L->hookmask & LUA_MASKCALL) { int narg = cast_int(L->top - func) - 1; luaD_hook(L, LUA_HOOKCALL, -1, 1, narg); } lua_unlock(L); n = (*f)(L); /* do the actual call */ lua_lock(L); api_checknelems(L, n); luaD_poscall(L, ci, n); break; } //...

可以看到这里取到了上述Closure中的函数指针并进行调用        。

创心域SEO版权声明:以上内容作者已申请原创保护,未经允许不得转载,侵权必究!授权事宜、对本内容有异议或投诉,敬请联系网站管理员,我们将尽快回复您,谢谢合作!

展开全文READ MORE
博客界面设计(博客系统(页面设计))