1.C调用lua例子
#include <iostream>
#include <lua.hpp>
int main()
{
//用于创建一个新的lua虚拟机
lua_State* L = luaL_newstate();
luaL_openlibs(L);//打开标准库
/*
if (luaL_dofile(L, "test.lua") != LUA_OK)
{
std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;
}
luaL_dofile = luaL_loadfile + lua_pcall(L, 0, 0, 0);
*/
/*
编译和解析lua代码 但是并不会运行lua代码
*/
if (luaL_loadfile(L, "test.lua") != LUA_OK)
{
std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;
return -1;
}
/*
全局变量和函数此时才会注册到lua虚拟机里面
如果不调用lua_pcall 将获取不到函数
*/
lua_pcall(L, 0, 0, 0);
lua_getglobal(L, "str");
lua_pushstring(L, "ZZH ");
lua_pushnumber(L, 25);
// 调用函数 (2个参数,1个返回值, 错误处理函数的index是0 表示没有)
if (lua_pcall(L, 2, 1, 0) != 0) {
std::cerr << "Error: " << lua_tostring(L, -1) << std::endl;
return 1;
}
// 获取返回值 在栈顶
const char *str = lua_tostring(L, -1);
lua_pop(L, 1); // 弹出返回值
std::cout << "Result: " << str << std::endl;
//关闭lua虚拟机
lua_close(L);
return 0;
}
2.lua虚拟栈
lua是一个脚本,经常用来和宿主程序交互,他们之间通过lua虚拟机沟通
lua的堆栈是用数字索引的,1代表栈底, -1代表栈顶
我们可以看到在上面C调用lua的时候,先获取到lua中的全局函数str
lua_getglobal这个函数将获取到的参数压入栈顶,然后依次调用lua_pushxxxx函数将两个参数也压入栈顶。
lua_pcall这个函数是用来调用lua函数的,lua_pcall的第一个参数是lua虚拟机,第二个参数是函数参数个数,第三个参数是期望的返回值个数,第四个参数是错误处理函数在栈中的位置。
执行完lua_pcall之后,lua的函数栈自己清理,然后将结果放到栈顶,我们获取到结果之后,必须调用lua_pop将返回值弹出,否则会内存泄漏
3.往栈中压入元素
lua的栈中可以压入任意的lua类型
4.从栈中获取一个值,index是栈中的位置
5.判断栈中元素类型
6.通用栈操作
1.lua_gettop返回栈中元素的个数,也就是栈顶索引
2.lua_settop将栈顶设置为一个值,即修改栈中元素数量,多了补nil
3.lua_pushvalue 将栈中指定索引位置处的元素拷贝一份到栈顶
4.lua_rotate 将栈中指定index位置处的元素,向上/向下移动
5.lua_replace弹出一个值,并且栈顶设置为索引上的值
6.lua_copy将一个索引处的值复制到另外一个索引上
7.对table的操作函数
8.lua调用C函数
C函数要想被lua调用,那么函数原型必须是
int (*Cfun)(lua_State*L);
返回值代表了参数个数
{
return 1; //代表往栈中压入了1个返回值
}
举个例子
int Cadd(lua_State* L)
{
//lua压入的第一个参数
int a = lua_tonumber(L, 1);
//lua压入的第二个参数
int b = lua_tonumber(L, 2);
int c = a + b;
std::cout << "a = " << a << " b = " << b << std::endl;
lua_pushnumber(L, c);
//返回一个参数
return 1;
}
int main()
{
//用于创建一个新的lua虚拟机
lua_State* L = luaL_newstate();
luaL_openlibs(L);//打开标准库
//将C函数放入到lua的虚拟栈上
lua_pushcfunction(L, Cadd);
//将Cadd这个C函数注册到global
lua_setglobal(L, "Cadd");
if (luaL_dofile(L, "test.lua") != LUA_OK)
{
std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;
}
//关闭lua虚拟机
lua_close(L);
return 0;
}
--lua代码
function CallAdd()
print("lua call Cfun ret = " .. Cadd(10, 20))
end
CallAdd()
结果
9.lua_Reg批量导入C函数
int Cadd(lua_State* L)
{
int a = luaL_checkinteger(L, 1);
int b = luaL_checkinteger(L, 2);
std::cout << "Cadd a=" << a << " b = " << b << std::endl;
lua_pushinteger(L, a + b);
return 1;
}
int Csub(lua_State* L)
{
int a = luaL_checkinteger(L, 1);
int b = luaL_checkinteger(L, 2);
std::cout << "Csub a=" << a << " b = " << b << std::endl;
lua_pushinteger(L, a - b);
return 1;
}
luaL_Reg g_Cfuns[] = {
{"Cadd", Cadd},
{"Csub", Csub},
{"NULL", nullptr}
};
int main()
{
//用于创建一个新的lua虚拟机
lua_State* L = luaL_newstate();
luaL_openlibs(L);//打开标准库
luaL_newlib(L, g_Cfuns);
lua_setglobal(L, "g_Cfuns"); // 将 table 绑定到全局变量 "g_Cfuns"
if (luaL_dofile(L, "test.lua") != LUA_OK)
{
std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;
}
//关闭lua虚拟机
lua_close(L);
return 0;
}
function CallAdd()
print("lua call Cfun ret = " .. g_Cfuns.Cadd(10, 20))
print("lua call Cfun ret = " .. g_Cfuns.Csub(20, 10))
end
CallAdd()
拿一段skynet中的代码来演示另外一种用法
luaL_Reg l[] = {
{ "start", lstart }, // 绑定 C 函数 lstart
{ "stop", lstop }, // 绑定 C 函数 lstop
{ "resume", luaB_coresume },// 绑定 C 函数 luaB_coresume
{ "wrap", luaB_cowrap }, // 绑定 C 函数 luaB_cowrap
{ NULL, NULL }, // 结束标记
};
luaL_newlibtable(L, l); // 1. 创建一个与 l 数组大小相同的 table(但此时 table 为空)
lua_newtable(L); // 2. 创建一个新的 table(用于存储线程 start 时间)
lua_newtable(L); // 3. 创建一个新的 table(用于存储线程 total 运行时间)
lua_newtable(L); // 4. 创建一个弱表(用于存储线程数据)
lua_pushliteral(L, "kv"); // 5. 压入字符串 "kv",表示 key-value 方式的弱表模式
lua_setfield(L, -2, "__mode"); // 6. 设置 `__mode` 字段,使该 table 变成弱引用表
lua_pushvalue(L, -1); // 7. 复制弱表,使两个线程时间表共享相同的弱表
lua_setmetatable(L, -3); // 8. 设置 `thread->start time` 表的 metatable(弱引用)
lua_setmetatable(L, -3); // 9. 设置 `thread->total time` 表的 metatable(弱引用)
luaL_setfuncs(L, l, 2); // 10. 绑定 l 中的 C 函数,并将栈顶的 2 个 table 作为 upvalue 传入
10.数组操作 lua_geti
第一个参数是lua虚拟机,第二个参数是数组在栈中的索引,第三个参数是在表中的下标
int visitArr(lua_State* L)
{
luaL_checktype(L, 1, LUA_TTABLE); //检验是否是table
lua_len(L, 1);
int len = lua_tointeger(L, -1);
lua_pop(L, 1);
for (int i = 1; i <= len; i++)
{
lua_pushvalue(L, 1);
lua_geti(L, 1, i);
std::cout << " i = " << i << " num=" << lua_tointeger(L, -1) << std::endl;
lua_pop(L, 1);
}
return 0;
}
luaL_Reg g_Cfuns[] = {
{"visitArr", visitArr},
{"NULL", nullptr}
};
int main()
{
//用于创建一个新的lua虚拟机
lua_State* L = luaL_newstate();
luaL_openlibs(L);//打开标准库
luaL_newlib(L, g_Cfuns);
lua_setglobal(L, "g_Cfuns"); // 将 table 绑定到全局变量 "g_Cfuns"
if (luaL_dofile(L, "test.lua") != LUA_OK)
{
std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;
}
//关闭lua虚拟机
lua_close(L);
return 0;
}
function testArr()
g_Cfuns.visitArr({7,5,3,4})
end
testArr()
11.字符串的操作
当C函数接收到一个lua字符串为参数时,必须遵守两个原则:1.使用字符串期间不能将其从栈中弹出 2.不应该修改字符串
const char* str = "Hello World";
lua_getglobal(L, "testStr");
lua_pushlstring(L, str + 0, strlen(str));
function testStr(str)
print("lua str = " .. str)
end
一旦调用了lua_pushlstring,那么在lua栈上就会产生一个字符串的副本,lua会管理这部分内存,C上面的字符串释放与否,都没关系
lua_pushfstring
12.在C函数中保存状态
注册表registry,也就等于是提供了一个全局变量吧,但是lua全局变量会被lua代码访问修改 不安全
注册表是一个只能被C代码访问的全局表, 总是位于LUA_REGISTRYINDEX中,伪索引就像是栈中的索引,但是关联的值不在栈中。
获取注册表中键为key的值,可以使用
lua_getfield(L, LUA_REGISTRYINDEX, "key");
设置注册表中键为key的值
lua_pushlightuserdata(L, 一个指针);
lua_setfield(L, LUA_REGISTRYINDEX, "key");
注册表是一个普通的lua表,但是不能用数字作为键
upvalue上值实现了一种类似于C语言静态变量的机制,每一次在lua中创建新的C函数时候,可以将任意数量的上值和这个函数关联起来,每个上值保存一个lua值,后面调用该函数的时候,通过伪索引自由访问这些值
#include <lua.h>
#include <lauxlib.h>
// 计数器增加
int lua_counter_add(lua_State *L) {
int count = lua_tointeger(L, lua_upvalueindex(1));
count += luaL_checkinteger(L, 1); // 加上 Lua 传入的值
lua_pushinteger(L, count);
lua_replace(L, lua_upvalueindex(1)); // 更新 upvalue
return 1;
}
// 计数器读取
int lua_counter_get(lua_State *L) {
lua_pushinteger(L, lua_tointeger(L, lua_upvalueindex(1))); // 返回 upvalue
return 1;
}
// 初始化模块
int luaopen_counter(lua_State *L) {
lua_pushinteger(L, 0); // 计数器初始值 0
lua_pushcclosure(L, lua_counter_add, 1); // 绑定 upvalue
lua_setglobal(L, "add_count"); // 绑定到全局变量 add_count
lua_pushinteger(L, 0); // 计数器初始值 0
lua_pushcclosure(L, lua_counter_get, 1); // 绑定 upvalue
lua_setglobal(L, "get_count"); // 绑定到全局变量 get_count
return 0;
}
add_count(5)
add_count(3)
print(get_count()) -- 8
add_count(2)
print(get_count()) -- 10
luaL_setfuncs 这个也可以为函数集合设置他们的upvalue
13.require
14.C中的 用户自定义类型
一个简单的例子
struct Pos
{
int x;
int y;
};
int setX(lua_State* L)
{
Pos * p = (Pos*)lua_touserdata(L, 1);
int v = lua_tointeger(L, 2);
p->x = v;
return 0;
}
int setY(lua_State* L)
{
Pos* p = (Pos*)lua_touserdata(L, 1);
int v = lua_tointeger(L, 2);
p->y = v;
return 0;
}
int getX(lua_State* L)
{
Pos* p = (Pos*)lua_touserdata(L, 1);
lua_pushinteger(L, p->x);
return 1;
}
int getY(lua_State* L)
{
Pos* p = (Pos*)lua_touserdata(L, 1);
lua_pushinteger(L, p->y);
return 1;
}
int createNewPos(lua_State* L)
{
Pos * p = (Pos*)lua_newuserdata(L, sizeof(Pos));
int x = lua_tointeger(L, 1);
int y = lua_tointeger(L, 2);
p->y = y;
p->x = x;
return 1;
}
luaL_Reg g_Cfuns[] = {
{"createNewPos", createNewPos},
{"setX", setX},
{"setY", setY},
{"getX", getX},
{"getY", getY},
{"NULL", nullptr}
};
int main()
{
//用于创建一个新的lua虚拟机
lua_State* L = luaL_newstate();
luaL_openlibs(L);//打开标准库
luaL_newlib(L, g_Cfuns);
lua_setglobal(L, "g_Cfuns"); // 将 table 绑定到全局变量 "g_Cfuns"
if (luaL_dofile(L, "test.lua") != LUA_OK)
{
std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;
}
//关闭lua虚拟机
lua_close(L);
return 0;
}
local pos = g_Cfuns.createNewPos(10, 20)
print(g_Cfuns.getX(pos))
但是这个用法不规范,lua不能直接调用他的方法 也不能安全访问字段,一般需要绑定元表
#include <lua.hpp>
#include <iostream>
struct Pos {
int x;
int y;
};
int setX(lua_State* L) {
Pos* p = (Pos*)luaL_checkudata(L, 1, "PosMeta"); // 安全获取 userdata
p->x = luaL_checkinteger(L, 2);
return 0;
}
int setY(lua_State* L) {
Pos* p = (Pos*)luaL_checkudata(L, 1, "PosMeta");
p->y = luaL_checkinteger(L, 2);
return 0;
}
int getX(lua_State* L) {
Pos* p = (Pos*)luaL_checkudata(L, 1, "PosMeta");
lua_pushinteger(L, p->x);
return 1;
}
int getY(lua_State* L) {
Pos* p = (Pos*)luaL_checkudata(L, 1, "PosMeta");
lua_pushinteger(L, p->y);
return 1;
}
int createNewPos(lua_State* L) {
int x = luaL_checkinteger(L, 1);
int y = luaL_checkinteger(L, 2);
Pos* p = (Pos*)lua_newuserdata(L, sizeof(Pos));
p->x = x;
p->y = y;
luaL_getmetatable(L, "PosMeta");
lua_setmetatable(L, -2); // 绑定元表
return 1;
}
luaL_Reg pos_methods[] = {
{"setX", setX},
{"setY", setY},
{"getX", getX},
{"getY", getY},
{nullptr, nullptr}
};
extern "C" int luaopen_mylib(lua_State* L) {
luaL_newmetatable(L, "PosMeta");
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index"); // 让对象可以用 `pos:getX()` 访问方法
luaL_setfuncs(L, pos_methods, 0); // PosMeta 绑定方法
lua_newtable(L);
lua_pushcfunction(L, createNewPos);
lua_setfield(L, -2, "createNewPos");
return 1;
}
int main() {
lua_State* L = luaL_newstate();
luaL_openlibs(L);
luaopen_mylib(L); // 注册模块
lua_setglobal(L, "PosLib");
if (luaL_dofile(L, "test.lua") != LUA_OK) {
std::cerr << "Lua error: " << lua_tostring(L, -1) << std::endl;
}
lua_close(L);
return 0;
}
------lua
local pos = PosLib.createNewPos(10, 20)
print(pos:getX()) -- 10
print(pos:getY()) -- 20
pos:setX(30)
pos:setY(40)
print(pos:getX()) -- 30
print(pos:getY()) -- 40