文章目录


ubuntu 18.04 lua安装命令

curl -R -O http://www.lua.org/ftp/lua-5.4.3.tar.gz
tar zxf lua-5.4.3.tar.gz
cd lua-5.4.3
make all test
sudo make install

C++调用lua

一、Lua堆栈

要理解Lua和C++交互,首先要理解Lua堆栈。

简单来说,Lua和C/C++语言通信的主要方法是一个无处不在的虚拟栈。栈的特点是先进后出。

在Lua中,Lua堆栈就是一个struct

堆栈索引的方式可是是正数也可以是负数,区别是:正数索引1永远表示栈底,负数索引-1永远表示栈顶。

二、堆栈的操作

因为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
lua_State *luaHandle = luaL_newstate();
//2.入栈操作
lua_pushstring(luaHandle, "I am so cool~");
lua_pushnumber(luaHandle,20);
//3.取值操作
if( lua_isstring(luaHandle,1)){ //判断是否可以转为string
cout<<lua_tostring(luaHandle,1)<<endl; //转为string并返回
}
if( lua_isnumber(luaHandle,2)){
cout<<lua_tonumber(luaHandle,2)<<endl;
}
//4.关闭state
lua_close(luaHandle);
return 0;
}
g++ -o lua lua.cpp -llua

运行

$ ./lua 
I am so cool~
20

其他一些栈操作:

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位置的值

ua_settop将栈顶设置为一个指定的位置,即修改栈中元素的数量。如果值比原栈顶高,则高的部分nil补足,如果值比原栈低,则原栈高出的部分舍弃。所以可以用lua_settop(0)来清空栈

C++调用lua的脚本

创建一个lua脚本 ​​test.lua​

str = "I am so cool"  
tbl = {name = "shun", id = 20114442}
function add(a,b)
return a + b
end

编写.cpp文件调用lua代码

/**
* @file test.cpp
* @author deroy (you@domain.com)
* @brief C/C++调用lua代码
* @version 0.1
* @date 2021-11-24
*
* @copyright Copyright (c) 2021
*
*/
#include <iostream>
#include <string.h>
using namespace std;
extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
int main()
{
//1.创建一个state
lua_State *luaHandle = luaL_newstate();

//2.加载Lua文件
int bRet = luaL_loadfile(luaHandle,"test.lua");
if(bRet)
{
cout<<"load file error"<<endl;
return 0;
}
//3.运行Lua文件
bRet = lua_pcall(luaHandle,0,0,0);
if(bRet)
{
cout<<"pcall error"<<endl;
return 0;
}
//4.读取变量
lua_getglobal(luaHandle,"str");
string str = lua_tostring(luaHandle,-1);
cout<<"str = "<<str.c_str()<<endl; //str = I am so cool~

//5.读取table
lua_getglobal(luaHandle,"tbl");
lua_getfield(luaHandle,-1,"name");
str = lua_tostring(luaHandle,-1);
cout<<"tbl:name = "<<str.c_str()<<endl; //tbl:name = shun
//6.读取函数
lua_getglobal(luaHandle, "add"); // 获取函数,压入栈中
lua_pushnumber(luaHandle, 10); // 压入第一个参数
lua_pushnumber(luaHandle, 20); // 压入第二个参数
int iRet= lua_pcall(luaHandle, 2, 1, 0);// 调用函数,调用完成以后,会将返回值压入栈中,2表示参数个数,1表示返回结果个数。
if (iRet) // 调用出错
{
const char *pErrorMsg = lua_tostring(luaHandle, -1);
cout << pErrorMsg << endl;
lua_close(luaHandle);
return 0;
}
if (lua_isnumber(luaHandle, -1)) //取值输出
{
double fValue = lua_tonumber(luaHandle, -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(luaHandle);
return 0;
}

Lua调用C/C++

lua作为脚本语言是无法直接调用静态库的(除非吧静态库编译到Lua解析器里面)

本文介绍的是Lua调用C/C++动态库

对于那些可被Lua调用的C函数而言,其接口必须遵循Lua要求的形式,即

​typedef int (*lua_CFunction)(lua_State* L)​

编写lua.c文件

#include <stdio.h>
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
static int bs_pclose (lua_State *L) {
#if 0
luaL_Stream *p = tolstream(L);
#else
luaL_Stream *p = (luaL_Stream *)luaL_checkudata(L, 1, LUA_FILEHANDLE);
#endif
return luaL_execresult(L, pclose(p->f));
}
static int bs_popen (lua_State *L) {
const char *filename = luaL_checkstring(L, 1);
const char *mode = luaL_optstring(L, 2, "r");
#if 0
luaL_Stream *p = newprefile(L);
#else
luaL_Stream *p = (luaL_Stream *)lua_newuserdata(L, sizeof(luaL_Stream));
luaL_setmetatable(L, LUA_FILEHANDLE);
p->closef = &bs_pclose;
#endif
p->f = popen(filename, mode);
return (p->f == NULL) ? luaL_fileresult(L, 0, filename) : 1;
}
static const struct luaL_Reg reg_libs[] =
{
{"pclose", bs_pclose},
{"popen", bs_popen},
{NULL, NULL}
};
int luaopen_libBsLuaExt(lua_State *L)
{
lua_newtable(L);
luaL_setfuncs(L, reg_libs, 0);
//luaL_register(L, "clibs", reg_libs);
return 1;
}

编译生成动态库

gcc lua.c -fPIC -shared -o libBsLuaExt.so

在动态库所在文件夹下编辑lua脚本

#!/usr/local/bin/lua
local bs = require("libBsLuaExt")
-- require("logPrint")
--[[
写入固定行数日志
--]]
function file_exists(path)
local file = io.open(path, "rb")
if file then
file:close()
end
return file ~= nil
end
function file_lines(path)
local lineCnt = 0;
--print(path)
if(file_exists(path) == true) then
local sysCmd = bs.popen(string.format("wc -l %s | awk '{print $1}'", path));
lineCnt = tonumber(sysCmd:read("*a"));
sysCmd:close();
end
--print(lineCnt)
return tonumber(lineCnt);
end
function file_write(path, data)
local file = io.open(path, "ab")
if file then
file:write(data)
file:write("\n")
file:close()
end
--print(lineCnt)
return 0;
end
function logFileWrite(maxLine, logFile, logData)
local logDir = string.match(logFile, "(.+)/[^/]*%.%w+$");
local t = {};
--print("maxLine:",maxLine);
--print("logFile:",logFile);
--print("logData:",logData);
local ret = os.execute(string.format("mkdir -p %s", logDir));
if(ret ~= true) then
return string.format("failed to create dir '%s'", logDir), -1
end
if(file_lines(logFile) >= tonumber(maxLine)) then
local cmdDelLine = bs.popen(string.format("sed -i '1d' %s", logFile));
cmdDelLine:close()
end
--local cmdAppend = bs.popen(string.format("echo '%s' >> %s", logData, logFile));
--cmdAppend:close()
file_write(logFile, logData);
return t,"OK",0
end
logFileWrite(10, "/home/ubuntu/learnbase/lua/info.txt", "2222")
if(file_exists("/home/ubuntu/learnbase/lua/info.txt") == true) then
print("存在")
else
print("不存在")
end

直接运行lua脚本

$ lua lua.lua 
存在