广告
返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C++如何实现与Lua相互调用
  • 842
分享到

C++如何实现与Lua相互调用

2023-07-05 20:07:25 842人浏览 独家记忆
摘要

今天小编给大家分享一下c++如何实现与lua相互调用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。概述从本质上来看,其实说是

今天小编给大家分享一下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);    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;    lu_byte is_vararg;  lu_byte maxstacksize;    int sizeupvalues;    int sizek;    int sizecode;  int sizelineinfo;  int sizep;    int sizelocvars;  int linedefined;    int lastlinedefined;    TValue *k;    Instruction *code;  //codes  struct Proto **p;    int *lineinfo;    LocVar *locvars;    Upvaldesc *upvalues;    struct LClosure *cache;    TString  *source;    GCObject *gclist;} Proto;

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

FuncState

typedef struct FuncState {  Proto *f;    struct FuncState *prev;    struct LexState *ls;    struct BlockCnt *bl;    int pc;    int lasttarget;     int jpc;    int nk;    int np;    int firstlocal;    short nlocvars;    lu_byte nactvar;    lu_byte nups;    lu_byte freereg;  } 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;    enterlevel(ls);  switch (ls->t.token) {    case ';': {        luaX_next(ls);        break;    }    case TK_IF: {        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))      luaV_execute(L);    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;   newframe:    lua_assert(ci == L->ci);  cl = clLvalue(ci->func);    k = cl->p->k;    base = ci->u.l.base;      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;    StkIdtop;    struct CallInfo *previous, *next;    uNIOn {    struct {        StkId base;        const Instruction *savedpc;    } l;    struct {        lua_KFunction k;        ptrdiff_t old_errfunc;      lua_KContext ctx;      } c;  } u;  ptrdiff_t extra;  short nresults;    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));          }    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];  } 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)            L->top = ra + b;                  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:        f = clCvalue(s2v(func))->f;      Goto Cfunc;    case LUA_VLCF:        f = fvalue(s2v(func));     Cfunc: {      int n;        CallInfo *ci = next_ci(L);      checkstackp(L, LUA_MINSTACK, func);        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);        lua_lock(L);      api_checknelems(L, n);      luaD_poscall(L, ci, n);      break;    }//...

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

以上就是“C++如何实现与Lua相互调用”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网其他教程频道。

--结束END--

本文标题: C++如何实现与Lua相互调用

本文链接: https://www.lsjlt.com/news/354838.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • C++如何实现与Lua相互调用
    今天小编给大家分享一下C++如何实现与Lua相互调用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。概述从本质上来看,其实说是...
    99+
    2023-07-05
  • 怎么实现C#与Lua相互调用
    这篇文章主要介绍“怎么实现C#与Lua相互调用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“怎么实现C#与Lua相互调用”文章能帮助大家解决问题。一、编译Lua动态链接库1. 编译Windows下使...
    99+
    2023-06-30
  • C++实现与Lua相互调用的示例详解
    目录概述示例C++调用lua编译lua流程运行lua调用C++解释运行调用语义概述 从本质上来看,其实说是不存在所谓的C++与lua的相互调用。lua是运行在C上的,简单来说lua的...
    99+
    2023-05-13
    C++ Lua相互调用 C++ Lua 调用 C++ Lua
  • 原生实现C#与Lua相互调用方法(Unity3D可用)
    目录引言一、编译Lua动态链接库1. 编译Windows下使用的DLL文件2. 编译Android下使用的SO文件二、编写C#使用的API1. 动态链接库在Unity中的存放位置。2...
    99+
    2022-11-13
  • Java代码中与Lua相互调用实现详解
    目录一、方案二、性能测试1. ScriptEngine调用方式2. Globals调用方式3. lua调用java三、结论四、其他调用方式?一、方案 Java与Lua相互调用案例比较...
    99+
    2022-11-13
  • python 与c++相互调用实现
    目录一、c++调用Python1.Python脚本2.C++调用python脚本二、接口方法1.规范化语法三、Pthon调用c++1.基于extern2.基于swig一、c++调用P...
    99+
    2022-11-13
  • C语言与C++项目实现相互调用
    目录前言一、C++项目调用C的静态库二、C项目调用C++的静态库三、总结前言 extern “c”的作用可以实现c语言和c++相互调用。 1.当我们写c语言代...
    99+
    2022-11-12
  • C和C++如何实现互相调用详解
    目录前言1、为什么会有差异?2、extern “C”3、C++调用C正确方式4、C调用C++补充:C/C++文件之间函数的引用总结前言 在项目开发过程中,我们...
    99+
    2023-01-10
    c++中调用c c和c++混合编译 c语言能调用c++吗
  • 胶水语言Python与C/C++的相互调用的实现
    准备工作: python:https://www.python.org/downloads/ Dev-C++:https://sourceforge.net/projects/orwelldevcpp/ gcc和g+...
    99+
    2022-06-02
    Python C++调用 Python C语言相互调用
  • C++与Lua实现交互的原理是什么
    本篇文章给大家分享的是有关C++与Lua实现交互的原理是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。具体步骤:1,找到cocos自带的绑定工具脚本文件genbinding...
    99+
    2023-06-06
  • Kotlin与Java如何相互调用
    这篇文章主要介绍了Kotlin与Java如何相互调用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、Kotlin 调用 Java kotlin 关键字转义java 中的方法...
    99+
    2023-06-25
  • 详解python如何调用C/C++底层库与互相传值
    前言 开发环境: Centos 7 + Python 3.5.1 + Qt Creator(只是使用Qt Creator编译而已,并没有使用QT的任何库) Python调用C/C++库,我现在能做到的有...
    99+
    2022-06-04
    底层 详解 python
  • golang与c语言是怎么相互调用
    这篇文章主要为大家展示了“golang与c语言是怎么相互调用”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“golang与c语言是怎么相互调用”这篇文章吧。go语言调用c语言以下为一个简短的例子:...
    99+
    2023-06-21
  • python模块与C和C++动态库相互调用实现过程示例
    目录Python调用C/C++1、Python调用C动态链接库C语言文件:pycall.cgcc编译生成动态库libpycall.soPython调用动态库的文件:pycall.py...
    99+
    2022-11-12
  • AndroidView与Compose互相调用实例探究
    目录1. 前言2. Android传统View调用Compose2.1 新建传统View体系的Android项目2.2 项目添加Compose配置2.2.1 在android代码块添...
    99+
    2023-01-30
    Android View与Compose互相调用 Android View与Compose
  • Naive与WebView在Android中怎么实现互相调用
    本篇文章为大家展示了Naive与WebView在Android中怎么实现互相调用,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。Android  Naive与WebView的互相调用详解An...
    99+
    2023-05-31
    android naive webview
  • C#使用XSLT实现xsl、xml与html相互转换
    目录一、转为html文档1、xsl文件2、转换3、结果二、转为xml文档1、prices.xsl2、转换XsltArgumentList.AddExtensionObject三 、调...
    99+
    2022-11-13
  • Python与C之间的相互调用(Python C API及Python ctypes库)
    问题,需要实现全局快捷键,但是是事实上Qt并没有对全局快捷键提供支持,那么用Qt的话就只能通过Win32Api来完成了,而我,用的是PyQt,还需要用Python来调用win32 API,事实上,都没有什么难的。因为Python如此的流行,...
    99+
    2023-06-05
  • C#如何实现前台与后台方法互调
    本篇文章为大家展示了C#如何实现前台与后台方法互调,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。前台与后台方法互调是很多读者关心的功能。下面提供的代码,可以实现C#前台与后台方法互调的目的。<...
    99+
    2023-06-17
  • C#代码与函数相互调用问题有哪些
    这篇文章主要讲解了“C#代码与函数相互调用问题有哪些”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C#代码与函数相互调用问题有哪些”吧!C#代码与#函数的相互调用问题之一问:如何在#访问C#...
    99+
    2023-06-17
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作