lua与c++交互
来源:互联网 发布:mac docker vs pd 编辑:程序博客网 时间:2024/06/04 18:41
lua和C++交互详细总结
前言:
原理:Lua和C语言通信的主要方法是一个无所不在的虚拟栈。几乎所有的API调用都会操作这个栈上的值。所有的数据交换,无论是Lua到C语言或者是C语言到Lua都是通过这个栈来完成。
可以把Lua看成C的一个库,lua解释器(即Lua库里的那个应用程序)使得lua可以编写程序,它依靠Lua库来实现主要功能。它处理与用户的交互(真正运行Lua代码就是一种交互)
一、lua堆栈
1、lua堆栈的用处?
简单来说,lua与c/c++语言通信的主要方法是依靠这个无处不在的虚拟栈(先进后出)
虚拟栈的形状:
记忆诀窍:正数表示第几个放进去的,负数表示是倒数第几个放进去的
2、lua堆栈的结构?
Lua堆栈就是一个struct
这里要说明的是, 你压入的类型有数值, 字符串, 表和闭包[在c中看来是不同类型的值], 但是最后都是统一用TValue这种数据结构来保存的
关于不同类型如何在这个堆栈中存储(即不同的类型在堆栈中储存方式)总结如下:
<1. lua中, number, boolean, nil, light userdata四种类型的值是直接存在栈上元素里的, 和垃圾回收无关.
<2. lua中, string, table, closure, userdata, thread存在栈上元素里的只是指针, 他们都会在生命周期结束后被垃圾回收.
二、堆栈的操作
因为Lua与C/C++是通过栈来通信,Lua提供了C API对栈进行操作(栈的操作只能在c代码里进行)。
需要注意的是:堆栈操作是基于栈顶的,就是说它只会去操作栈顶的值。
<1.要获取lua中的一个值时,只要调用一个api函数,lua就会将指定的值压入栈中。
<2.要将一个值传给lua时,需要先将这个值压入栈,然后调用api,lua就会将获取该值并将其从栈中弹出。
#include <iostream> #include <string.h> using namespace std; extern "C" { #include "lua.h" #include "lauxlib.h" #include "lualib.h" } void main() { //1.创建一个state lua_State *L = luaL_newstate(); //2.入栈操作 lua_pushstring(L, "I am so cool~"); lua_pushnumber(L,20); //3.取值操作 if( lua_isstring(L,1)){ //判断是否可以转为string cout<<lua_tostring(L,1)<<endl; //转为string并返回 } if( lua_isnumber(L,2)){ cout<<lua_tonumber(L,2)<<endl; } //4.关闭state lua_close(L); return ; }
上边的代码只是c语言这边对栈进行的操作(c语言入栈,c语言取值,没有涉及到与lua的交互,只是体现了单方面操作虚拟栈)
下边总结一些常用的栈操作的api函数
lua_pushcclosure(L, func, 0) //创建并压入一个闭包lua_createtable(L, 0, 0) //新建并压入一个表lua_pushnumber(L, 30) //压入一个数字30lua_pushstring(L, "zfx") //压入一个字符串...其他一些栈操作
int lua_gettop(lua_State *L); //返回栈顶索引(即栈长度)void lua_settop(lua_State* L, int idx);void lua_pushvalue(lua_State* L,int idx) //将idx索引上的值的副本压入栈顶void lua_remove(lua_State* L, int idx) //移除idx索引上的值void lua_insert(lua_State* L, int idx) //弹出栈顶元素,并插入索引idx的位置void lua_replace(lua_State * L,int idx) //弹出栈顶元素,并替换索引idx位置的值lua_settop将栈顶设置为一个指定的位置,即修改栈中元素的数量。如果值比原栈顶高,则高的部分nil补足,如果值比原栈低,则原栈高出的部分舍弃。所以可以用lua_settop(0)来清空栈。
三、C++调用Lua
我们经常可以使用Lua文件来作配置文件。类似ini,xml等文件配置信息。现在我们来使用C++来读取Lua文件中的变量,table,函数。(麒麟的用法)
lua和c通信时有这样的约定: 所有的lua中的值由lua来管理, c++中产生的值lua不知道, 类似表达了这样一种意思: "如果你(c/c++)想要什么, 你告诉我(lua), 我来产生, 然后放到栈上, 你只能通过api来操作这个值, 我只管我的世界",
这个很重要, 因为:
"如果你想要什么, 你告诉我, 我来产生"就可以保证, 凡是lua中的变量, lua要负责这些变量的生命周期和垃圾回收, 所以, 必须由lua来创建这些值(在创建时就加入了生命周期管理要用到的簿记信息)
"然后放到栈上, 你只能通过api来操作这个值", lua api给c提供了一套完备的操作界面, 这个就相当于约定的通信协议, 如果lua客户使用这个操作界面, 那么lua本身不会出现任何"意料之外"的错误.
"我只管我的世界"这句话体现了lua和c/c++作为两个不同系统的分界, c/c++中的值, lua是不知道的, lua只负责它的世界
-----------------------------------------------示例分界线(begin)---------------------------------------------------------------------lua文件(hell0.lua):
str = "I am so cool" -- 一个字符串tbl = {name = "shun", id = 20114442} -- 一个table function add(a,b) -- 一个函数 return a + b end
然后就是c++文件(text.cpp)读取它:
#include <iostream> #include <string.h> using namespace std; extern "C" { #include "lua.h" #include "lauxlib.h" #include "lualib.h" } void main() { //1.创建Lua堆栈 lua_State *L = luaL_newstate(); if (L == NULL) { return ; } //2.加载Lua文件 int bRet = luaL_loadfile(L,"hello.lua"); if(bRet) { cout<<"load file error"<<endl; return ; } //3.运行Lua文件 bRet = lua_pcall(L,0,0,0); if(bRet) { cout<<"pcall error"<<endl; return ; } ////以上都是栈的准备工作,下边是交互过程: //4.读取变量 lua_getglobal(L,"str"); //获取lua中全局名字为str的变量(实际上是一个字符串),压入栈中 string str = lua_tostring(L,-1); //把堆栈中-1(现在就是str)转换为字符串,取值 cout<<"str = "<<str.c_str()<<endl; //str = I am so cool~ //5.读取table lua_getglobal(L,"tbl"); //获取table,压入栈中 lua_getfield(L,-1,"name"); //栈顶(现在是table)取"name"的值,压入栈中 str = lua_tostring(L,-1); //栈顶(现在是name的字符串) cout<<"tbl:name = "<<str.c_str()<<endl; //tbl:name = shun //6.读取函数 lua_getglobal(L, "add"); // 获取函数,压入栈中 lua_pushnumber(L, 10); // 压入第一个参数 lua_pushnumber(L, 20); // 压入第二个参数 int iRet= lua_pcall(L, 2, 1, 0);// 调用函数,调用完成以后,会将返回值压入栈中,2表示参数个数,1表示返回结果个数。 if (iRet) // 调用出错 { const char *pErrorMsg = lua_tostring(L, -1); cout << pErrorMsg << endl; lua_close(L); return ; } if (lua_isnumber(L, -1)) //取值输出 { double fValue = lua_tonumber(L, -1); cout << "Result is " << fValue << endl; } //至此,栈中的情况是: //=================== 栈顶 =================== // 索引 类型 值 // 4 int: 30 // 3 string: shun // 2 table: tbl // 1 string: I am so cool~ //=================== 栈底 =================== //7.关闭state lua_close(L); return ; }
上边需要注意的是函数的调用过程
-----------------------------------------------示例分界线(end)---------------------------------------------------------------------
四、Lua调用C++
可以用四个方法实现:
1.直接将模块写入lua源码中(不介意)
在Lua中调用C/C++,我们可以将函数写lua.c中,然后重新编译Lua文件。(具体做法可以参照http://www.cnblogs.com/sevenyuan/p/4511808.html)
2.使用dll动态链接的方式
我们新建一个dll(zfx.dll)工程 --> 然后编写c++模块(并且编译) --> 新建一个Lua文件(关键是lua文件的写法)如下:
require "mLualib" -- dll方式中最关键的一步local ave,sum = ss.average(1,2,3,4,5)//参数对应堆栈中的数据 print(ave,sum) -- 3 15 ss.sayHello() -- hello world!
方式是:
<1.我们编写几个函数在C++中
<2.然后函数封装在C++的一个数组中,类型必须是luaL_Reg
<3.C++模块中的luaopen_mLualib函数导出并在lua中注册者两个函数
重点是:那么为什么要这样子写呢?实际上当我们在Lua中:
require "mLualib"
这样子写的时候,Lua会这么干:
local path = "mLualib.dll" local f = package.loadlib(path,"luaopen_mLualib") -- 返回luaopen_mLualib函数 f() -- 执行
所以当我们在编写一个这样的模块的时候,编写luaopen_xxx导出函数的时候,xxx最好是和项目名一样(因为项目名和dll一样)。
需要注意的是:函数参数里的lua_State是私有的,每一个函数都有自己的栈。当一个C/C++函数把返回值压入Lua栈以后,该栈会自动被清空。
(具体做法可以参照http://www.cnblogs.com/sevenyuan/p/4511808.html)
3.使用静态依赖的方式
<1.新建一个空的win32控制台工程,记得在vc++目录中,把Lua中的头文件和lib文件的目录包含进来,然后 连接器->附加依赖性->将lua51.lib和Lua5.1.lib也包含进来。
<2.在目录下新建一个avg.lua如下:
avg, sum = average(10, 20, 30, 40, 50) -- average函数lua中没有,等待调用c模块的print("The average is ", avg) print("The sum is ", sum)<3.新建test.cpp如下:
#include <stdio.h> extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" } /* 指向Lua解释器的指针 */ lua_State* L; static int average(lua_State *L) { /* 得到参数个数 */ int n = lua_gettop(L); double sum = 0; int i; /* 循环求参数之和 */ for (i = 1; i <= n; i++) { /* 求和 */ sum += lua_tonumber(L, i); } /* 压入平均值 */ lua_pushnumber(L, sum / n); /* 压入和 */ lua_pushnumber(L, sum); /* 返回返回值的个数 */ return 2; } int main ( int argc, char *argv[] ) { /* 初始化Lua */ L = lua_open(); /* 载入Lua基本库 */ luaL_openlibs(L); /* 注册函数 */ lua_register(L, "average", average); //第二个参数是lua中函数使用的名字,第三个参数是c模块函数名 /* 运行脚本 */ luaL_dofile(L, "avg.lua"); /* 清除Lua */ lua_close(L); /* 暂停 */ printf( "Press enter to exit…" ); getchar(); return 0; }执行一下,我们可以得到结果:
The average is 30The sum is 150Press enter to exit...执行顺序是:c++中写一个模块函数 --> 将函数注册到Lua解释器中 --> C++去执行LUA文件 --> lua调用刚刚注册的函数
注册函数机制记忆法:因为lua和c交互,c是主lua是客,所以c调用lua可以直接调用,而lua调用c需要注册先。
五、总结
lua和C++是通过一个虚拟栈来交互的。
C++调用Lua实际上是:由c++先把数据放入栈中,由Lua去栈中取数据,然后返回数据对应的值到栈顶,再由栈顶返回C++.
Lua调用C++也是一样:先编写自己的C模块,然后注册函数到Lua解释器中,然后由Lua去调用这个模块的函数。
待续...
- 【Lua】Lua与C交互
- Lua 与C交互
- Lua 与 C 交互
- Lua 与C交互
- Lua 与C交互
- Lua 与C交互
- Lua 与 C 交互
- lua与c交互
- C与lua交互
- Lua与C交互
- lua与c交互
- lua与C++ / Lua 与C交互
- Lua 与 C 交互(1)
- lua与c交互 一
- c与lua交互 二
- lua 与 C/C++交互
- lua 与c的交互
- Lua与C的交互
- Keras 更新指令
- linux下的 tar.gz文件和.rpm文件有什么最大的区别?
- Android 程序运行到5.0系统闪退和三星s7(6.0系统)闪退
- shell-exec
- iOS10适配系列教程(二):Security 安全和版本判断
- lua与c++交互
- mysql基础---字段管理(三)
- angular-cli安装及build项目
- jquery双日历插件daterangepicker.js使用方法及设置值
- Windows下PyQt4的安装
- Java如何调用父类的父类的中的方法
- Spring 入门(一) ——Spring设计之路的开始
- Node.js建立一个超简单的HTTP服务器
- 抓取源码 读出视频