本篇文章为之后的xlua打铺垫
环境配置
没绷住,鼓捣了半天才弄好
主要配置方法见下链接
按照上面的还不够,跑的时候还是报错了
进行下面修改后跑通了
[2]
#include时要加上extern "C",告知编译器链接时不要按C++方法查找符号,这一步很重要。
extern "C" { #include "lua.h" #include "lauxlib.h" #include "lualib.h" }
Lua和c交互的原理---虚拟栈
栈
Lua 和 C之间通信的主要组件是无处不在的虚拟栈(stack), 几乎所有的API调用都是 在操作这个栈中的值, Lua与C之间所有的数据交换都是通过这个栈完成的。 此外,还可以 利用栈保存中间结果。
栈中的每个元素都能保存Lua中任意类型的值。 当我们想要从Lua中获取一个值 (例如一个全局变量的值)时,只需调用Lua, Lua就会将指定的值压入栈中。 当想要将一个 值传给Lua时, 首先要将这个值压入栈,然后调用Lua将其从栈中弹出即可
如图栈顶索引为-1,栈底索引为1
压入元素C API
获取元素C API
检查类型
小实验
void stackTest()
{
lua_State* L = luaL_newstate();
lua_pushstring(L, "testForCCallLua");
lua_pushnumber(L, 123);
//读栈取值
if (lua_isstring(L, -2))//或if(lua_isstring(L,1))
{
std::cout << lua_tostring(L, -2) << std::endl;
}
if (lua_isnumber(L, -1))
{
std::cout << lua_tonumber(L, -1) << std::endl;
}
//关闭state
lua_close(L);
}
int main()
{
stackTest();
system("pause");
return 0;
}
c调用Lua
Lua代码
-- test.lua
name = "ccc"
id = 33
user = { uid = 33, uname = "forward256"}
function add (a,b)
return a+b
end
获取值
- 使用 lua_getglobal 来获取值并将其压栈。
- 使用 lua_toXXX 将栈中元素取出(此时元素并不会出栈)转成相应的 C/C++ 类型的值。
void stackDump(lua_State* L)
{
cout << "\nbeging dump lua stack" << endl;
//栈顶下标
int top = lua_gettop(L);
//从栈底向上遍历
for (int i = 1; i <= top; ++i)
{
int t = lua_type(L, i);
switch (t) {
case LUA_TSTRING: {
printf("'%s' ", lua_tostring(L, i));
}
break;
case LUA_TBOOLEAN: {
printf(lua_toboolean(L, i) ? "true " : "false ");
}
break;
case LUA_TNUMBER: {
printf("%g ", lua_tonumber(L, i));
}
break;
default: {
printf("%s ", lua_typename(L, t));
}
break;
}
cout << "\nend dump lua stack" << endl;
}
}
void testGetLuaValue()
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);//打开lua给我们提供的标准库
// 读 lua 文件
int fret = luaL_dofile(L, "test.lua");
if (fret)
{
std::cout << "read lua file error!" << std::endl;
}
//获取的时候会压入栈,top name
lua_getglobal(L, "name");
cout << "name=" << lua_tostring(L, -1) << endl;
//top -> id name <-bottom
lua_getglobal(L, "id");
cout << "id=" << lua_tonumber(L,-1) << endl;
stackDump(L);
lua_close(L);
}
获取和设置表
- 使用 lua_getglobal 来获取值并将其压栈。
- 使用 lua_getfield(L,索引,key) 获取表的值 会压栈
设置表
1.先压一个值
2.再使用lua_setfield(L,索引, key) 会使得之前的值出栈
void testOperaTableLua()
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);//打开lua给我们提供的标准库
// 读 lua 文件
int fret = luaL_dofile(L, "test.lua");
if (fret)
{
std::cout << "read lua file error!" << std::endl;
}
lua_getglobal(L,"user");
if (!lua_istable(L, -1))
{
std::cout << "error:it is not a table" << std::endl;
}
// 取表中元素
lua_getfield(L, -1, "uid");
cout << "uid=" << lua_tonumber(L, -1) << endl;
lua_getfield(L, -2, "uname");
cout << "uname=" << lua_tostring(L, -1) << endl;
cout << endl;
// 修改表中元素
lua_pushstring(L, "007");
stackDump(L);
lua_setfield(L, -4, "uname");
stackDump(L);
lua_getfield(L, -3, "uname");
cout << "************" << endl;
std::cout << " newName = " << lua_tostring(L, -1) << std::endl;
stackDump(L);
lua_close(L);
}
调用Lua 函数
- 使用 lua_getglobal 来获取函数并将其压栈。
- 如果这个函数有参数的话,就需要依次将函数的参数也压入栈。
- 调用 lua_pcall 开始调用函数,调用完成以后,会将返回值压入栈中。
- 取返回值,调用完毕。
void testFuncLua()
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);//打开lua给我们提供的标准库
// 读 lua 文件
int fret = luaL_dofile(L, "test.lua");
if (fret)
{
std::cout << "read lua file error!" << std::endl;
}
lua_getglobal(L, "add");
stackDump(L);
lua_pushnumber(L, 15);
lua_pushnumber(L, 5);
stackDump(L);
// 2-参数个数,1-返回值个数,调用函数,函数执行完,会将返回值压入栈中
lua_pcall(L, 2, 1, 0);
stackDump(L);
cout << endl;
std::cout << "5 + 15 = " << lua_tonumber(L, -1) << std::endl;
lua_close(L);
}
Lua调用c语言
Lua 可以调用 C/C++ 的函数,步骤为:
- 将 C 的函数包装成 Lua 环境认可的函数。
- 将包装好的函数注册到 Lua 环境中。
- 像使用普通 Lua 函数那样使用注册函数。
包装C/C++函数
需要将函数包装成 Lua_CFunction 格式,并需要在函数中将返回值压入栈中,并返回返回值个数
int (Lua_CFunction*)(lua_state*)
{
// c code // 实现逻辑功能
// lua_push code // 需要将返回值压入堆栈
return n; // n为具体的返回值个数,告诉解释器,函数向堆栈压入几个返回值
}
// 示例
/*
int add(int a,int b)
{
return a+b;
}
*/
int add(lua_State* L)
{
int a = lua_tonumber(L,-1);
int b = lua_tonumber(L,-2);
int sum = a + b;
lua_pushnumber(L, sum);
return 1;
}
把函数注册
// lua_register 是一个宏,对应两个函数 lua_pushfunction(L,f) 和 lua_setglobal(L,n),将函数存放在一个全局 table 中。
lua_register(L,"Add2Number",add);//将 c/c++ 函数 add 注册到全局 table[Add2Number] 中
使用
参考的文章案例没有用上面的,我这里用上面的示例
void testLuaCallC()
{
// 创建一个 state
lua_State* L = luaL_newstate();
luaL_openlibs(L);
// 为 Lua 注册名为第一个参数的函数,实际上是调用 c/c++ 中第三个参数名的函数
lua_register(L, "Add2Number", add);
// 读 lua 文件并运行 Lua code
int fret = luaL_dofile(L, "test3.lua");
if (fret)
{
std::cout << "read lua file error!" << std::endl;
}
// 关闭state
lua_close(L);
}
Lua代码 ,test2.lua
res=Add2Number(15,5)
print(res)
完整的CPP代码
#include <stdio.h>
#include <stdlib.h>
//#include "lua.h"
//#include "lauxlib.h"
#include<iostream>
using namespace std;
extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
void stackDump(lua_State* L)
{
cout << "\nbeging dump lua stack" << endl;
//栈顶下标
int top = lua_gettop(L);
//从栈底向上遍历
for (int i = 1; i <= top; ++i)
{
int t = lua_type(L, i);
switch (t) {
case LUA_TSTRING: {
printf("'%s' ", lua_tostring(L, i));
}
break;
case LUA_TBOOLEAN: {
printf(lua_toboolean(L, i) ? "true " : "false ");
}
break;
case LUA_TNUMBER: {
printf("%g ", lua_tonumber(L, i));
}
break;
default: {
printf("%s ", lua_typename(L, t));
}
break;
}
cout << endl;
}
cout << "end dump lua stack" << endl;
}
void stackTest()
{
lua_State* L = luaL_newstate();
lua_pushstring(L, "testForCCallLua");
lua_pushnumber(L, 123);
//读栈取值
if (lua_isstring(L, -2))//或if(lua_isstring(L,1))
{
std::cout << lua_tostring(L, -2) << std::endl;
}
if (lua_isnumber(L, -1))
{
std::cout << lua_tonumber(L, -1) << std::endl;
}
//关闭state
lua_close(L);
}
void testGetLuaValue()
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);//打开lua给我们提供的标准库
// 读 lua 文件
int fret = luaL_dofile(L, "test.lua");
if (fret)
{
std::cout << "read lua file error!" << std::endl;
}
//获取的时候会压入栈,top name
lua_getglobal(L, "name");
cout << "name=" << lua_tostring(L, -1) << endl;
//top -> id name <-bottom
lua_getglobal(L, "id");
cout << "id=" << lua_tonumber(L,-1) << endl;
stackDump(L);
lua_close(L);
}
void testOperaTableLua()
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);//打开lua给我们提供的标准库
// 读 lua 文件
int fret = luaL_dofile(L, "test.lua");
if (fret)
{
std::cout << "read lua file error!" << std::endl;
}
lua_getglobal(L,"user");
if (!lua_istable(L, -1))
{
std::cout << "error:it is not a table" << std::endl;
}
// 取表中元素
lua_getfield(L, -1, "uid");
cout << "uid=" << lua_tonumber(L, -1) << endl;
lua_getfield(L, -2, "uname");
cout << "uname=" << lua_tostring(L, -1) << endl;
cout << endl;
// 修改表中元素
lua_pushstring(L, "007");
stackDump(L);
lua_setfield(L, -4, "uname");
stackDump(L);
lua_getfield(L, -3, "uname");
cout << "************" << endl;
std::cout << " newName = " << lua_tostring(L, -1) << std::endl;
stackDump(L);
lua_close(L);
}
void testCCallLua()
{
lua_State* L = luaL_newstate();
luaL_openlibs(L);//打开lua给我们提供的标准库
// 读 lua 文件
int fret = luaL_dofile(L, "test.lua");
if (fret)
{
std::cout << "read lua file error!" << std::endl;
}
lua_getglobal(L, "add");
stackDump(L);
lua_pushnumber(L, 15);
lua_pushnumber(L, 5);
stackDump(L);
// 2-参数个数,1-返回值个数,调用函数,函数执行完,会将返回值压入栈中
lua_pcall(L, 2, 1, 0);
stackDump(L);
cout << endl;
std::cout << "5 + 15 = " << lua_tonumber(L, -1) << std::endl;
lua_close(L);
}
int add(lua_State* L)
{
int a = lua_tonumber(L,-1);
int b = lua_tonumber(L,-2);
int sum = a + b;
lua_pushnumber(L, sum);
return 1;
}
void testLuaCallC()
{
// 创建一个 state
lua_State* L = luaL_newstate();
luaL_openlibs(L);
// 为 Lua 注册名为第一个参数的函数,实际上是调用 c/c++ 中第三个参数名的函数
lua_register(L, "Add2Number", add);
// 读 lua 文件并运行 Lua code
int fret = luaL_dofile(L, "test3.lua");
if (fret)
{
std::cout << "read lua file error!" << std::endl;
}
// 关闭state
lua_close(L);
}
int main()
{
/*stackTest();
testGetLuaValue();*/
//testOperaTableLua();
//testCCallLua();
testLuaCallC();
system("pause");
return 0;
}
引用
配置环境
lua源码编译及与C/C++交互调用细节剖析 - 知乎 (zhihu.com)[2]
主要内容来自
截图来自
Lua程序设计(第4版) (豆瓣) (douban.com)[4]
后记
笔者能力有限,见谅。侵权请联系删除。