成都58手机微信网站建设名录做PPT素材用到的网站
web/
2025/10/8 9:54:58/
文章来源:
成都58手机微信网站建设名录,做PPT素材用到的网站,短视频入口seo,wordpress 总浏览数量C和Lua交互总结 Chapter1. C和Lua交互总结一、Lua与C的交互机制——Lua堆栈二、堆栈的操作三、C 调用 Lua1#xff09;C获取Lua值2#xff09;C调用Lua函数示例#xff1a; 四、Lua 调用 C包装C函数 最后总结一下 Chapter1. C和Lua交互总结
原文链接#xff1a;https://bl… C和Lua交互总结 Chapter1. C和Lua交互总结一、Lua与C的交互机制——Lua堆栈二、堆栈的操作三、C 调用 Lua1C获取Lua值2C调用Lua函数示例 四、Lua 调用 C包装C函数 最后总结一下 Chapter1. C和Lua交互总结
原文链接https://blog.csdn.net/qq826364410/article/details/88624824/
一、Lua与C的交互机制——Lua堆栈
Lua和C 的交互机制的基础在于Lua提供了一个虚拟栈C 和Lua之间的所有类型的数据交换都通过这个栈完成。无论何时C想从Lua中调用一个值被请求的值将会被压入栈无论何时C想要传递一个值给Lua首先将整个值压栈然后就可以在Lua中调用。
Lua中对虚拟栈提供正向索引和反向索引两种索引方式区别是正数索引1永远表示栈底负数索引-1永远表示栈顶。重要后面所有的交互都是基于Lua的虚拟栈来通信。假设当前Lua的栈中有5个元素如下图所示
二、堆栈的操作
因为lua与c/c是通过栈来通信lua提供了C API对栈进行操作。
我们先来看一个最简单的例子
#include iostream
#include string.h
using namespace std;extern C
{
#include lua.h
#include lauxlib.h
#include lualib.h
}int main()
{//1.创建一个state // luaL_newstate返回一个指向堆栈的指针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);getchar();return 0;
}在执行这个例子之前我们需要引入Lua.lib静态库也就是上文中extern C中执行的include。
extern “C” 主要作用就是为了能够正确实现C 代码调用其他C语言代码。加上extern “C”后会指示编译器这部分代码按c语言的进行编译而不是C 的。由于C 支持函数重载因此编译器编译函数的过程中会将函数的参数类型也加到编译后的代码中而不仅仅是函数名比如_Log_int_int而C语言并不支持函数重载因此编译C语言代码的函数时不会带上函数的参数类型一般只包括函数名比如_Log。
void Log(int a, int b){}Lua源代码的下载地址 http://www.lua.org/ftp/但没有提供相应的静态库只有.c和.h文件需要我们自己生成静态库。
生成Lua静态库方法 https://blog.csdn.net/qq826364410/article/details/88563408
也可以直接下载我生成好的静态库https://download.csdn.net/download/qq826364410/11029611
好了我们有了Lua静态库就可以开心的玩耍了。
引入Lua静态库
首先新建一个Visual C的空项目右键点击工程属性选择VC目录把lua工程中的.h头文件所在的目录加到包含目录中把Lua静态库文件所在的目录加到库目录中最后点击链接器-输入-附加依赖项-加上生成的Lua静态库比如Lua.lib记得用分号;与其他库隔开。 OK大功告成 Lua虚拟栈在源码中是如何实现的
Lua栈是在创建lua_State时建立的TValue stack[max_stack_len] 欲知内情可以查 Lua源码lstate.c的stack_init函数 Lua栈可以存储数字字符串表闭包等它们最终都用TValue这种数据结构来保存 。 TValue结构对应于lua中的所有数据类型, 是一个{值, 类型} 结构, 这就lua中动态类型的实现, 它把值和类型绑在一起, 用tt记录value的类型, value是一个联合结构, 由Value定义, 可以看到这个联合有四个域, 先说明简单的 p – 可以存一个指针, 实际上是lua中的light userdata结构 n – 所有的数值存在这里, 不管是int , 还是float b – Boolean值存在这里, 注意, lua_pushinteger不是存在这里, 而是存在n中, b只存布尔 gc – 其他诸如table, thread, closure, string需要内存管理垃圾回收的类型都存在这里 gc是一个指针, 它可以指向的类型由联合体GCObject定义, 从图中可以看出, 有string, userdata, closure, table, proto, upvalue, thread 可以的得出如下结论: 1. lua中, number, boolean, nil, light userdata四种类型的值是直接存在栈上元素里的, 和垃圾回收无关。 2. lua中, string, table, closure, userdata, thread存在栈上元素里的只是指针, 他们都会在生命周期结束后被垃圾回收。
三、C 调用 Lua
C 可以获取Lua中的值可以调用Lua函数还可以修改Lua文件
1C获取Lua值
使用lua_getglocal来获取值然后将其压栈 使用lua_toXXX将栈中元素取出转成相应的C类型的值 如果Lua值为table类型的话通过lua_getfield和lua_setfield获取和修改表中元素的值
2C调用Lua函数
使用lua_getglobal来获取函数然后将其压入栈 如果这个函数有参数的话就需要依次将函数的参数也压入栈 这些准备工作都准备就绪以后就调用lua_pcall开始调用函数了调用完成以后会将返回值压入栈中
示例
新建一个简单的lua放到工程的同级目录下 hello.lua
str I am x-man.
tbl {name DC, id 20114442}
function add(a,b)return a b
end然后我们写一个Lua1.cpp来访问lua中的数据
#include iostream
#include string.h
using namespace std;extern C
{
#include lua.h
#include lauxlib.h
#include lualib.h
}
int 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.读取全局变量// 1.把 str 压栈 2.由lua去寻找全局变量str的值并将str的值返回栈顶替换str// 如果存在相同命名的其他变量、table或函数就会报错读取位置发生访问冲突lua_getglobal(L, str);// -1取出栈顶元素转化为stringstring str lua_tostring(L, -1);cout str str.c_str() endl;//5.读取table把table压栈lua_getglobal(L, tbl);//-------------------------------// 1.把name压入栈中2.由lua去寻找table中name键的值并将键值返回栈顶替换name// 相当于lua_pushstring(L, name) lua_gettable(L, -2)执行结果是一样的lua_getfield(L, -1, name);// 把name压入栈中//lua_pushstring(L, name);// 弹出栈上的name并从表中找到name的键值把结果放在栈上相同的位置//lua_gettable(L, -2);//---------------------------------str lua_tostring(L, -1);// 因为table在栈顶的下面所以取-2把id压栈由lua找到table中id键的值并返回栈顶替换idlua_getfield(L, -2, id);// id的值已经在栈顶取-1int id lua_tonumber(L, -1);cout tbl:name str.c_str() endl;cout tbl:id id endl;// 读取函数// 1.将函数add放入栈中2.由lua去寻找函数add并将函数add返回栈顶替换add。lua_getglobal(L, add); // 获取函数压入栈中lua_pushnumber(L, 10); // 压入第一个参数lua_pushnumber(L, 20); // 压入第二个参数// 栈过程参数出栈-保存参数-参数出栈-保存参数-函数出栈-调用函数-返回结果入栈// 调用函数调用完成以后会将返回值压入栈中2表示参数个数1表示返回结果个数。int iRet lua_pcall(L, 2, 1, 0);if (iRet) {// 调用出错const char *pErrorMsg lua_tostring(L, -1);cout pErrorMsg endl;lua_close(L);return;}if (lua_isnumber(L, -1)) //取值输出{int fValue lua_tonumber(L, -1);cout Result is fValue endl;}// 栈的索引方式可以是正数也可以是负数区别是1永远表示栈底元素-1永远表示栈顶元素。//至此栈中的情况是// 栈顶 // 索引 类型 值// 5或-1 int 30 // 4或-2 int 20114442// 3或-3 string shun // 2或-4 table tbl// 1或-5 string I am so cool~// 栈底 lua_pushstring(L, Master);// 会将Master值出栈保存值找到到table的name键如果键存在存储到name键中lua_setfield(L, 2, name);// 读取lua_getfield(L, 2, name);str lua_tostring(L, -1);cout tbl:name str.c_str() endl;// 创建新的tablelua_newtable(L);lua_pushstring(L, A New Girlfriend);lua_setfield(L, -2, name);// 读取lua_getfield(L, -1, name);str lua_tostring(L, -1);cout newtbl:name str.c_str() endl;//7.关闭state// 销毁指定 Lua 状态机中的所有对象 并且释放状态机中使用的所有动态内存。// 如果有垃圾收集相关的元方法的话会调用它们lua_close(L);getchar();return 0;
}代码中已经有很详细的注释了这里总结一下
1. 读取lua的全局变量
lua_getglobal(L, str); 内部实现1.把全局变量 str 里的值压栈 2.由lua去寻找全局变量str的值并将str的值返回栈顶替换str 注意如果存在相同命名的其他变量、table或函数就会报错读取位置发生访问冲突
2. 读取table中的键值
lua_getglobal(L, tbl);
lua_getfield(L, -1, name); lua_getglobal方法跟上面的实现是一样的。
lua_getfield方法
内部实现1.把name压入栈中2.由lua去寻找table中name键的值如果键存在将键值返回栈顶替换name
注意这里的参数-1就是表示把table中的键值返回到栈顶。
调用lua中的函数
lua_getglobal(L, add); // 获取函数压入栈中
lua_pushnumber(L, 10); // 压入第一个参数
lua_pushnumber(L, 20); // 压入第二个参数
// 栈过程参数出栈-保存参数-参数出栈-保存参数-函数出栈-调用函数-返回结果入栈
// 调用函数调用完成以后会将返回值压入栈中2表示参数个数1表示返回结果个数
// iRet为0表示调用成功
int iRet lua_pcall(L, 2, 1, 0);四、Lua 调用 C
Lua可以调用由C定义、实现具体的函数 步骤
将C的函数包装成Lua环境认可的Lua_CFunction格式将包装好的函数注册到Lua环境中像使用普通Lua函数那样使用注册函数
包装C函数
为了从Lua脚本中调用C函数需要将被调用的C函数从普通的C函数包装成Lua_CFunction格式并需要在函数中将返回值压入栈中并返回返回值个数。
int (*lua_CFunction) (lua_State *L);例如有一个C函数
int add(int a,int b)
{return ab;
}包装为
int add(lua_state *L)
{int a lua_tonumber(-1);int b lua_tonumber(-2);int sum ab;// 将返回值压入栈中lua_pushnumber(L,sum);// 返回返回值个数return 1;
}示例 新建一个简单的lua放到工程的同级目录下 avg.lua
avg, sum average(10, 20, 30, 40, 50)
print(The average is , avg)
print(The sum is , sum)print(age, age)
for k,v in pairs(newTable) do print(k ,k, v ,v)
end
print(name, newTable.name)然后创建一个Lua2.cpp
#include stdio.hextern C {
#include lua.h
#include lualib.h
#include lauxlib.h
}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 *//* 指向Lua解释器的指针 */lua_State* L luaL_newstate();/* 载入Lua基本库 */luaL_openlibs(L);/* 注册函数 */lua_register(L, average, average);// 设置lua中的全局变量lua_pushinteger(L, 18); //入栈lua_setglobal(L, age); //1.先将18值出栈保存值2.在lua中把值存储到全局变量age中// 设置lua中tablelua_newtable(L); //创建一张空表并将其压栈lua_pushstring(L, lili);// 入栈// 1.先将lili值出栈保存值2.找table的name键如果键存在存储到name键中lua_setfield(L, -2, name);//栈顶是lili新创建的table在lili下所以是-2// 将table赋值到lua中并弹出tablelua_setglobal(L, newTable);/* 运行脚本 */luaL_dofile(L, avg.lua);/* 清除Lua */lua_close(L);/* 暂停 */printf(Press enter to exit…);getchar();return 0;
}1. 读取C的变量
// 设置lua中的全局变量
lua_pushinteger(L, 18); //入栈
lua_setglobal(L, age); lua_setglobal(L, “age”) 内部实现 1.先将值出栈保存值2.在lua中把值存储到全局变量age中
2. 调用在C中创建的新表的元素
// 设置lua中table
lua_newtable(L); //创建一张空表并将其压栈
lua_pushstring(L, lili);// 入栈
// 1.先将lili值出栈保存值2.找table的name键如果键存在存储到name键中
lua_setfield(L, -2, name);//栈顶是lili新创建的table在lili下所以是-2
// 将table赋值到lua中并弹出table
lua_setglobal(L, newTable);lua_setglobal(L, “newTable”) 内部实现 1.先将table出栈保存table2.在lua中存储到newTable表中
在lua中print(“name”, newTable.name)使用newTable.name调用在C中创建的新表的元素。
3. 调用C中的函数
将C的函数包装成Lua环境认可的Lua_CFunction格式 将包装好的函数注册到Lua环境中 像使用普通Lua函数那样使用注册函数 包装C函数
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;
}将包装好的函数注册到Lua环境中
/* 注册函数 */
lua_register(L, average, average);在lua中正常调用
avg, sum average(10, 20, 30, 40, 50)4.把C的函数封装成模块 把C的函数封装成模块
①将C的函数包装成Lua环境认可的Lua_CFunction格式调用luaL_newlib放入到一个lua表中压入栈里。
②将自定义模块注册到Lua环境中。
③在lua中加上自定义模块名调用C函数。
avg.lua这里的lua文件在调用C的函数时需要加上自定义的模块名。 比如我们定义模块名为mylib。
avg, sum mylib.average(10, 20, 30, 40, 50)
print(The average is , avg)
print(The sum is , sum)Lua1.cpp
#include stdio.hextern C {
#include lua.h
#include lualib.h
#include lauxlib.h
}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;
}// 1. 列出需要封装的C函数
// luaL_Reg为注册函数的数组类型
static const luaL_Reg mylibs_funcs[] {{ average, average },{ NULL, NULL }
};// 2. 将所有函数放到一个table中并压入栈中
int lua_openmylib(lua_State* L) {//创建一个新的表将所有函数放到一个table中//将这个table压到stack里luaL_newlib(L, mylibs_funcs);return 1;
}// 3. 将自定义模块加到注册列表里
static const luaL_Reg lua_reg_libs[] {{ base, luaopen_base },{ mylib, lua_openmylib }, //这里为自定义的模块名字mylib{ NULL, NULL }
};int main(int argc, char *argv[])
{/* 初始化Lua *//* 指向Lua解释器的指针 */lua_State* L luaL_newstate();/* 载入Lua基本库 */luaL_openlibs(L);//4. 注册让lua使用的模块const luaL_Reg* lua_reg lua_reg_libs;for (; lua_reg-func; lua_reg) {// 加载模块// 首先查找 package.loaded 表 检测 modname 是否被加载过。 // 如果被加载过require 返回 package.loaded[modname] 中保存的值。// 如果 modname 不在 package.loaded 中 则调用函数 openf 并传入字符串 modname。// 将其返回值置入 package.loaded[modname]。// 如果最后一个参数为真 同时也将模块设到全局变量 modname 里。在栈上留下该模块的副本。luaL_requiref(L, lua_reg-name, lua_reg-func, 1);// 从栈中弹出 1 个元素lua_pop(L, 1);}/* 运行脚本 */luaL_dofile(L, avg.lua);/* 清除Lua */lua_close(L);/* 暂停 */printf(Press enter to exit…);getchar();return 0;
}5. Lua调用C类注册生成的Lua模块 由于篇幅的限制请移步https://blog.csdn.net/qq826364410/article/details/88652441
6. 在Lua中以面向对象的方式使用C注册的类 由于篇幅的限制请移步https://blog.csdn.net/qq826364410/article/details/88639408
Lua和C交互全局数组交互 https://blog.csdn.net/qq826364410/article/details/88713839
补充 这里补充其他一些栈操作
int lua_gettop (lua_State *L); //返回栈顶索引即栈长度
// lua_settop将栈顶设置为一个指定的位置即修改栈中元素的数量。
// 如果值比原栈顶高则高的部分nil补足如果值比原栈低则原栈高出的部分舍弃。
// 所以可以用lua_settop(0)来清空栈。
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位置的值
// 确保堆栈上至少有 n 个额外空位。 如果不能把堆栈扩展到相应的尺寸
// 函数返回假。 失败的原因包括将把栈扩展到比固定最大尺寸还大 至少是几
// 千个元素或分配内存失败。 这个函数永远不会缩小堆栈 如果堆栈已经
// 比需要的大了那么就保持原样
int lua_checkstack (lua_State *L, int n); 下面就分两个主要部分进行介绍C和栈操作以及Lua和栈操作
2.C和栈之间操作相关函数
①c - stack 将C数据压到栈里函数:lua_pushxxx
LUA_API void (lua_pushnil) (lua_State *L);
LUA_API void (lua_pushnumber) (lua_State *L, lua_Number n);
LUA_API void (lua_pushinteger) (lua_State *L, lua_Integer n);
LUA_API const char *(lua_pushlstring) (lua_State *L, const char *s, size_t len);
LUA_API const char *(lua_pushstring) (lua_State *L, const char *s);
LUA_API const char *(lua_pushvfstring) (lua_State *L, const char *fmt,va_list argp);
LUA_API const char *(lua_pushfstring) (lua_State *L, const char *fmt, ...);
LUA_API void (lua_pushcclosure) (lua_State *L, lua_CFunction fn, int n);
LUA_API void (lua_pushboolean) (lua_State *L, int b);
LUA_API void (lua_pushlightuserdata) (lua_State *L, void *p);
LUA_API int (lua_pushthread) (lua_State *L);②stack - c 判断栈里类型相关函数 lua_isxxx(lua_State *L, int idx)
LUA_API int (lua_isnumber) (lua_State *L, int idx);
LUA_API int (lua_isstring) (lua_State *L, int idx);
LUA_API int (lua_iscfunction) (lua_State *L, int idx);
LUA_API int (lua_isinteger) (lua_State *L, int idx);
LUA_API int (lua_isuserdata) (lua_State *L, int idx);
LUA_API int (lua_type) (lua_State *L, int idx);③stack - c 获取栈里数据相关函数lua_toxxx (lua_State *L, int idx)
LUA_API lua_Number (lua_tonumberx) (lua_State *L, int idx, int *isnum);
LUA_API lua_Integer (lua_tointegerx) (lua_State *L, int idx, int *isnum);
LUA_API int (lua_toboolean) (lua_State *L, int idx);
LUA_API const char *(lua_tolstring) (lua_State *L, int idx, size_t *len);
LUA_API size_t (lua_rawlen) (lua_State *L, int idx);
LUA_API lua_CFunction (lua_tocfunction) (lua_State *L, int idx);
LUA_API void *(lua_touserdata) (lua_State *L, int idx);
LUA_API lua_State *(lua_tothread) (lua_State *L, int idx);
LUA_API const void *(lua_topointer) (lua_State *L, int idx);3.Lua和栈之间的操作相关函数
①从Lua中得到数据放到栈里进行操作lua_getxxx
LUA_API int (lua_getglobal) (lua_State *L, const char *name);
LUA_API int (lua_gettable) (lua_State *L, int idx);
LUA_API int (lua_getfield) (lua_State *L, int idx, const char *k);
LUA_API int (lua_geti) (lua_State *L, int idx, lua_Integer n);
LUA_API int (lua_rawget) (lua_State *L, int idx);
LUA_API int (lua_rawgeti) (lua_State *L, int idx, lua_Integer n);
LUA_API int (lua_rawgetp) (lua_State *L, int idx, const void *p);LUA_API void (lua_createtable) (lua_State *L, int narr, int nrec);
LUA_API void *(lua_newuserdata) (lua_State *L, size_t sz);
LUA_API int (lua_getmetatable) (lua_State *L, int objindex);
LUA_API int (lua_getuservalue) (lua_State *L, int idx);②从栈里将数据写入到Lua中:lua_setxxx
LUA_API void (lua_setglobal) (lua_State *L, const char *name);
LUA_API void (lua_settable) (lua_State *L, int idx);
LUA_API void (lua_setfield) (lua_State *L, int idx, const char *k);
LUA_API void (lua_seti) (lua_State *L, int idx, lua_Integer n);
LUA_API void (lua_rawset) (lua_State *L, int idx);
LUA_API void (lua_rawseti) (lua_State *L, int idx, lua_Integer n);
LUA_API void (lua_rawsetp) (lua_State *L, int idx, const void *p);
LUA_API int (lua_setmetatable) (lua_State *L, int objindex);
LUA_API void (lua_setuservalue) (lua_State *L, int idx);最后总结一下 Lua和C是通过一个虚拟栈来交互通信的。 C调用Lua 由C先把函数名、变量名、table中键放入栈中然后把函数名、变量名、table中键出栈并返回对应的值到栈顶再由栈顶返回C。 Lua调C
**Lua调用C的函数**先把普通的C函数包装成Lua_CFunction格式然后注册函数到Lua解释器中然后由Lua去调用这个模块的函数。
**Lua以面向对象的方式调用C的类**新建一个元表metatable并设置元表里key为__index的值的为metatable本身然后将成员操作方法添加到元表metatable里在创建对象函数中把元表赋值给对象指针这样通过:操作符就可以找到对应的方法了。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/88989.shtml
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!