Lua与C++交互:函数
来源:互联网 发布:360软件管家字体模糊 编辑:程序博客网 时间:2024/05/17 07:00
Lua虚拟栈
Lua和C++交互,必须通过Lua虚拟栈,所以首先要理解Lua虚拟栈。
栈的特点是先进后出,在Lua中,Lua堆栈是一个struct,它的索引可以是正数也可以是负数,区别是:正数索引1永远表示栈底,负数索引-1永远表示栈顶,lua的栈是在lua_State的时候创建的。
lua中, number, boolean, nil, light userdata四种类型的值是直接存在栈上元素里的, 和垃圾回收无关.
lua中, string, table, closure, userdata, thread存在栈上元素里的只是指针, 他们都会在生命周期结束后被垃圾回收.
Lua栈的操作,Lua与C/C++是通过栈来通信,Lua提供了C API对栈进行操作
操作需要操作Lua栈,需要导入lua.h,lauxlib.h,lualib.h,luaconf.h四个头文件
#include <stdio.h>extern "C"{ #include <lua.h> #include <lualib.h> #include <lauxlib.h>}void main(){ lua_State *L = luaL_newstate(); //1.基本的库都加载进来 /* 官网文档提到不要直接调用 luaopen函数 //打开基础库 luaopen_base(L); //打开表 luaopen_table(L); //打开io luaopen_io(L); //打开字符串库 luaopen_string(L); //打开数学库 luaopen_math(L); */ lua_cpcall(L, luaopen_base, NULL); lua_cpcall(L, luaopen_table, NULL); lua_cpcall(L, luaopen_io, NULL); lua_cpcall(L, luaopen_string, NULL); lua_cpcall(L, luaopen_math, NULL); //2.入栈操作 lua_pushstring(L, "Hello world"); lua_pushnumber(L,20); //3.取值操作 if( lua_isstring(L,1)){ //判断是否可以转为string printf("%s",lua_tostring(L,1)); } if( lua_isnumber(L,2)){ printf("%s",lua_tonumber(L,1)); } //关闭State lua_close(L); }
C++调用Lua
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只负责它的世界
Lua代码:Test.lua
str = "Hello Lua"tab = {name = "ZhangSan",age = 22}function Add(a,b) print(a + b); return a + b;end
现在通过Test.cpp执行它
#include <stdio.h>extern "C"{ #include <lua.h> #include <lualib.h> #include <lauxlib.h>}void main(){ lua_State *L = luaL_newstate(); { //直接执行Lua代码 luaL_dostring(L, "print('Hello Lua')"); //执行Lua文件代码 luaL_dofile(L, "Test.lua"); //读取变量 //查找到变量,并压入栈 lua_getglobal(L,"str"); string str = lua_tostring(L,-1); //读取table lua_getglobal(L,"tab"); //把 index[k] 的值压栈,从-1的位置获取name的值,并将它压栈 lua_getfield(L,-1,"name"); str = lua_tostring(L,-1); //修改table // 将需要设置的值设置到栈中 lua_pushstring(L, "我是一个大帅锅~"); // 将这个值设置到table中(此时tbl在栈的位置为2) lua_setfield(L, 2, "name"); //创建一个新table // 创建一个新的table,并压入栈 lua_newtable(L); // 往table中设置值 lua_pushstring(L, "Give me a girl friend !"); //将值压入栈 //将值设置到table中,并将Give me a girl friend 出栈 lua_setfield(L, -2, "str"); //读取函数 lua_getglobal(L, "Add"); //第一个参数压栈 lua_pushinteger(L, 6); //第二个参数压栈 lua_pushinteger(L, 5); //调用Add函数,同时会对Add函数及两个参数进行出栈,并压入返回值 lua_call(L, 2, 1);//2:参数个数,1:返回值个数 int result = lua_tointeger(L, -1);//从栈中取返回值,也就是获取栈顶 lua_pop(L, 4);//清栈 } //关闭State lua_close(L); }
重点:堆栈操作是基于栈顶的,就是说它只会去操作栈顶的值
Lua调用C++
在lua中注册自己的函数,函数必须遵循规范(可在lua.h中查看)如下:
typedef int (*lua_CFunction) (lua_State *L);
换句话说,所有的函数必须接收一个lua_State作为参数,同时返回一个整数值。因为这个函数使用Lua栈作为参数,所以它可以从栈里面读取任意数量和任意类型的参数。而这个函数的返回值则表示函数返回时有多少返回值被压入Lua栈。(因为Lua的函数是可以返回多个值的)
1.直接将模块写入Lua源码中,单个函数注册
我们在Cpp文件加入如下函数:
int GetTwoVar(lua_State * L){ // 向函数栈中压入2个值 lua_pushnumber(L, 10); lua_pushstring(L, "hello world"); int a = lua_tonumber(L, 1); int b = lua_tointeger(L, 2); printf("A = %d,B = %d;", a, b); return 2;}
在main函数里加入:
lua_register(L, "GetTwoVar", GetTwoVar);/* lua_register :做了两步,第一步将GetTwoVar压栈, 第二步:将栈元素链接表中元素,做成一对键值表 相等于: lua_pushcfunction(L, getTwoVar); //将函数放入栈中 lua_setglobal(L, "getTwoVar"); //设置lua全局变量getTwoVar*/luaL_dostring(L,"print(GetTwoVar(111,999))");
一般我们不建议去修改别人的代码,更倾向于自己编写独立的C/C++模块,供Lua调用,下面来讲讲如何实现。
2.使用使用静态依赖的方式
- 新建一个空的win32控制台工程,记得在vc++目录中,把lua中的头文件和lib文件的目录包含进来,然后->链接器->附加依赖项->将lua51.lib和lua5.1.lib也包含进来。
- 在目录下新建一个avg.lua如下:
avg, sum = average(10, 20, 30, 40, 50) 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); /* 运行脚本 */ luaL_dofile(L, "avg.lua"); /* 清除Lua */ lua_close(L); /* 暂停 */ printf( "Press enter to exit…" ); getchar(); return 0; }
3.使用dll动态链接的方式:批量注册,不加载到全局库里,推荐使用
我们先新建一个dll工程,工程名为mLualib。(因此最后导出的dll也为mLualib.dll)
然后编写我们的c++模块,以函数为例,我们先新建一个.h文件和.cpp文件
h文件如下:
#pragma once extern "C" { #include "lua.h" #include "lualib.h" #include "lauxlib.h" } #ifdef LUA_EXPORTS #define LUA_API __declspec(dllexport) #else #define LUA_API __declspec(dllimport) #endif extern "C" LUA_API int luaopen_mLualib(lua_State *L);//定义导出函数
.cpp文件如下:
#include <stdio.h> #include "mLualib.h" static int averageFunc(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; //返回两个结果 } static int sayHelloFunc(lua_State* L) { printf("hello world!"); return 0; } static const struct luaL_Reg myLib[] = { {"average", averageFunc}, {"sayHello", sayHelloFunc}, {NULL, NULL} //数组中最后一对必须是{NULL, NULL},用来表示结束 }; int luaopen_mLualib(lua_State *L) { luaL_register(L, "ss", myLib); return 1; // 把myLib表压入了栈中,所以就需要返回1 }
然后编译它,然后新建一个lua文件,在lua中我们这样子来调用:(调用之前记得把dll文件复制到lua文件目录下)
require "mLualib" local ave,sum = ss.average(1,2,3,4,5)//参数对应堆栈中的数据 print(ave,sum) -- 3 15 ss.sayHello() -- hello world!
至此都发生了什么呢?梳理一下:
1.我们编写了averageFunc求平均值和sayHelloFunc函数,
2.然后把函数封装myLib数组里面,类型必须是luaL_Reg
3.由luaopen_mLualib函数导出并在lua中注册这两个函数。
4.加载到全局库,这样会使全局库变乱,不推荐使用
#include <stdio.h>#include<string.h>extern "C"{ #include <lua.h> #include <lualib.h> #include <lauxlib.h>}#pragma comnent(lib,"lua5.1.lib");int GetTwoVar(lua_State * L){ // 向函数栈中压入2个值 lua_pushnumber(L, 10); lua_pushstring(L, "hello world"); int a = lua_tonumber(L, 1); int b = lua_tointeger(L, 2); printf("A = %d,B = %d;", a, b); return 2;}luaL_Reg funcs[] ={ {"GetTwoVar1",GetTwoVar}, { "GetTwoVar2",GetTwoVar }, { "GetTwoVar3",GetTwoVar }, { "GetTwoVar4",GetTwoVar }, { "GetTwoVar5",GetTwoVar }, {0,0},};void main(){ lua_State* L = luaL_newstate(); //加载基础库 lua_openlib(L); //Lua调用C++函数 //函数要遵循规范(可在lua.h中查看)如下: //typedef int (*lua_CFunction) (lua_State *L); //所有的函数必须接收一个lua_State作为参数, // 同时返回一个整数值。因为这个函数使用Lua栈作为参数, // 所以它可以从栈里面读取任意数量和任意类型的参数。 // 而这个函数的返回值则表示函数返回时有多少返回值被压入Lua栈。 // (因为Lua的函数是可以返回多个值的) //Lua调用C++函数,批量注册到Lua //:加载到全集库 //1:状态机,2:库名称,3:函数的地址数组,4:是否加到系统项上 luaL_openlib(L, "myLid", funcs, 0); luaL_dostring(L, "myLid.GetTwoVar1(999,0)"); luaL_dostring(L, "myLid.GetTwoVar2(888,0)"); luaL_dostring(L, "myLid.GetTwoVar3(777,0)"); lua_close(L);}
需要注意的是:函数参数里的lua_State是私有的,每一个函数都有自己的栈。当一个C/C++函数把返回值压入Lua栈以后,该栈会自动被清空。
- Lua与C/C++交互函数问题
- 【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交互
- C/C++与Lua之间进行数据函数交互
- Lua与C++交互:函数
- Lua 与 C 交互(1)
- lua与c交互 一
- c与lua交互 二
- 利用JavaScript模拟给四个小球上色
- 关于面试“有戏”和“没戏”的信号
- Android 数据库demo
- 2017金马五校赛 K.购买装备(二分)
- SurfaceView攻略~~~从零开始实现打雷下雨天气特效~①
- Lua与C++交互:函数
- 拥抱黑暗,心向光明
- NP完全问题的证明-算法概论课后习题8.15
- Spring源码之Resource加载源码解析(二)
- 细说ThreadLocal
- 并查集详解
- nodejs response
- 继承和原型链
- 人生的智慧