91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

C++如何實現與Lua相互調用

發布時間:2023-03-31 11:30:20 來源:億速云 閱讀:104 作者:iii 欄目:開發技術

今天小編給大家分享一下C++如何實現與Lua相互調用的相關知識點,內容詳細,邏輯清晰,相信大部分人都還太了解這方面的知識,所以分享這篇文章給大家參考一下,希望大家閱讀完這篇文章后有所收獲,下面我們一起來了解一下吧。

概述

從本質上來看,其實說是不存在所謂的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++如何實現與Lua相互調用

考慮上述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 function's closure */
  k = cl->p->k;  /* local reference to function's constant table */
  base = ci->u.l.base;  /* local copy of function's 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中的函數指針并進行調用。

以上就是“C++如何實現與Lua相互調用”這篇文章的所有內容,感謝各位的閱讀!相信大家閱讀完這篇文章都有很大的收獲,小編每天都會為大家更新不同的知識,如果還想學習更多的知識,請關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

昭平县| 禹州市| 永宁县| 公安县| 富平县| 正安县| 溧阳市| 北票市| 宜川县| 静安区| 昭觉县| 津市市| 镇宁| 鸡西市| 客服| 濮阳县| 镇远县| 昔阳县| 舒城县| 青川县| 施甸县| 乌苏市| 乌鲁木齐县| 泽普县| 浪卡子县| 米易县| 韶山市| 罗田县| 宁国市| 翁牛特旗| 阿鲁科尔沁旗| 亳州市| 永年县| 商丘市| 泰州市| 长武县| 广饶县| 安塞县| 龙井市| 汉沽区| 诸暨市|