我的编程空间,编程开发者的网络收藏夹
学习永远不晚

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

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

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相互调用”这篇文章的所有内容,感谢各位的阅读!相信大家阅读完这篇文章都有很大的收获,小编每天都会为大家更新不同的知识,如果还想学习更多的知识,请关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

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

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

下载Word文档

猜你喜欢

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

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

怎么实现C#与Lua相互调用

这篇文章主要介绍“怎么实现C#与Lua相互调用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“怎么实现C#与Lua相互调用”文章能帮助大家解决问题。一、编译Lua动态链接库1. 编译Windows下使
2023-06-30

C++实现与Lua相互调用的示例详解

这篇文章主要为大家详细介绍了C++实现与Lua相互调用的方法,文中的示例代码讲解详细,具有一定的学习价值,感兴趣的小伙伴可以了解一下
2023-05-13

C和C++如何实现互相调用详解

在学习c++中用到一些古老的c语言库时,在工作中我们经常要使用C和C++混合编程,下面这篇文章主要给大家介绍了关于C和C++如何实现互相调用的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
2023-01-10

胶水语言Python与C/C++的相互调用的实现

准备工作: python:https://www.python.org/downloads/ Dev-C++:https://sourceforge.net/projects/orwelldevcpp/ gcc和g++:http://min
2022-06-02

Kotlin与Java如何相互调用

这篇文章主要介绍了Kotlin与Java如何相互调用,具有一定借鉴价值,感兴趣的朋友可以参考下,希望大家阅读完这篇文章之后大有收获,下面让小编带着大家一起了解一下。一、Kotlin 调用 Java1. kotlin 关键字转义java 中的
2023-06-25

C++与Lua实现交互的原理是什么

本篇文章给大家分享的是有关C++与Lua实现交互的原理是什么,小编觉得挺实用的,因此分享给大家学习,希望大家阅读完这篇文章后可以有所收获,话不多说,跟着小编一起来看看吧。具体步骤:1,找到cocos自带的绑定工具脚本文件genbinding
2023-06-06

详解python如何调用C/C++底层库与互相传值

前言开发环境:Centos 7 + Python 3.5.1 + Qt Creator(只是使用Qt Creator编译而已,并没有使用QT的任何库) Python调用C/C++库,我现在能做到的有两种方式 1.extern “C” 导出(
2022-06-04

lua如何调用c++函数

在 Lua 中调用 C++ 函数通常需要通过 Lua C API 来实现。以下是一些基本步骤:在 C++ 代码中编写一个函数,该函数可以被 Lua 调用。例如:extern "C" {#include }int myFun
lua如何调用c++函数
2024-04-08

golang与c语言是怎么相互调用

这篇文章主要为大家展示了“golang与c语言是怎么相互调用”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“golang与c语言是怎么相互调用”这篇文章吧。go语言调用c语言以下为一个简短的例子:
2023-06-21

C#如何实现前台与后台方法互调

本篇文章为大家展示了C#如何实现前台与后台方法互调,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。前台与后台方法互调是很多读者关心的功能。下面提供的代码,可以实现C#前台与后台方法互调的目的。< %@
2023-06-17

Naive与WebView在Android中怎么实现互相调用

本篇文章为大家展示了Naive与WebView在Android中怎么实现互相调用,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。Android Naive与WebView的互相调用详解Android
2023-05-31

Nginx+lua如何实现调用.so文件

这篇文章主要为大家展示了“Nginx+lua如何实现调用.so文件”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Nginx+lua如何实现调用.so文件”这篇文章吧。最近在和智能硬件部门一起,做
2023-06-04

AndroidView与Compose互相调用实例探究

这篇文章主要介绍了AndroidView与Compose互相调用,Compose具有超强的兼容性,兼容现有的所有代码,Compose能够与现有View体系并存,可实现渐进式替换
2023-01-30

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录