C与Lua交互
——在C和Lua之间相互传递数据,并以此操作SQlite3数据库
使用Lua来操作Sqlite3数据库的过程,相比直接在C/C++下操作,简单得多。在嵌入式开发过程中,使用Sqlite3来管理系统中的大量数据,是一种非常成熟的做法;将Lua 和Sqlite3结合起来的开源技术Lua Sqlite3,就显得非常适合快速开发。
这篇文章就是根据在项目开发过程中,提炼出来的实际经验总结。主要涉及到的技术知识背景有:
1、Lua
2、Sqlite3
3、Lua Sqlite3
前面两点,网络资源非常丰富。第3点,网上的资源相比较少,主要就是官网的文档。
本篇博客主要内容有:
1、C/C++如何给Lua 函数传递参数?
2、Lua如何给C/C++返回多个值?
3、C/C++如何通过Lua 提供的接口访问Sqlite3 数据库?
下面详细讲解:
一、C/C++如何给Lua传递参数(传递多个参数)
1、首先我们写一个lua脚本,脚本中提供了一个lua函数。保存为test.lua:
function set_data(...)
local args = {...}
for i=1,#args do
print(args[i])
end
end
2、使用C++写一个测试程序,代码如下:
#include "lua.hpp"
#include <iostream>
using namespace::std;
lua_State* g_L = NULL;
bool initlua()
{
g_L = luaL_newstate();//创建Lua栈
lua_checkstack(g_L,60);//修改Lua栈大小为60,防止在C和Lua之间传递大数据时,崩溃
luaL_openlibs(g_L);//运行Lua虚拟机
int ret;
if(ret=luaL_loadfile(g_L,"test.lua"))//装载lua脚本
{
cout<<"load test.lua fail."<<ret<<endl;
return false;
}
else
{
cout<<"load test.lua ok"<<endl;
if(ret = lua_pcall(g_L,0,0,0))//运行lua脚本
{
cout<<"run test.lua fail..."<<lua_tostring(g_L, -1)<<endl;
return false;
}
else
{
cout<<"run test.lua ok"<<endl;
return true;
}
}
}
void call_iface()
{
lua_getglobal(g_L, "set_data"); // 获取lua函数 set_data
int array[10] = {11,22,33,44,55,66,77,88,99,100};
for(int n=0;n<10;n++)
{
lua_pushinteger(g_L,array[n]); //将数组的数据入栈
}
if(lua_pcall(g_L, 10, 0, 0) != 0)//调用lua函数 set_data
printf("error running function 'set_data': %s", lua_tostring(g_L, -1));
lua_pop(g_L, 1);
}
int main(int argc, char argv[])
{
if(initlua())//装载test.lua脚本
{
call_iface();//调用test.lua脚本中提供的函数
}
return 1;
}
编译上面的CPP文件时,须链接lua的库 -llua -ldl
运行结果如下:
# ./lua-c-example
load test.lua ok
run test.lua ok
11
22
33
44
55
66
77
88
99
100
二、Lua函数如何给C/C++返回多个值
1、lua脚本内容如下,保存为test.lua:
local array = {1,2,4,5,6,7,8,9,10}
function get_data()
print("get_data")
return array
end
2、C++调用Lua函数,获取多个返回值的测试程序代码:
#include "lua.hpp"
#include <iostream>
using namespace::std;
lua_State* g_L = NULL;
bool initlua()
{
g_L = luaL_newstate();
lua_checkstack(g_L,60);
luaL_openlibs(g_L);
int ret;
if(ret=luaL_loadfile(g_L,"test.lua"))
{
cout<<"load test.lua fail."<<ret<<endl;
return false;
}
else
{
cout<<"load test.lua ok"<<endl;
if(ret = lua_pcall(g_L,0,0,0))
{
cout<<"run test.lua fail..."<<lua_tostring(g_L, -1)<<endl;
return false;
}
else
{
cout<<"run test.lua ok"<<endl;
return true;
}
}
}
void call_iface()
{
lua_getglobal(g_L, "get_data"); // 获取lua函数 get_data
if(lua_pcall(g_L, 0, 1, 0) != 0)
printf("error running function 'get_data': %s", lua_tostring(g_L, -1));
int index = lua_gettop(g_L);//获取Lua栈顶的内容的索引值
lua_pushnil(g_L);//nil 入栈作为初始 key
if(lua_isnil(g_L, index))//栈顶内容为空
{
return ;
}
while(lua_next(g_L, index))//循环遍历栈中内容
{
cout<<lua_tonumber(g_L, -1)<<" ";//取出栈顶内容
lua_pop(g_L, 1);//弹出栈顶内容,然后下一个数据处在栈顶
}
cout<<endl;
}
int main(int argc, char argv[])
{
if(initlua())
{
call_iface();
}
return 1;
}
运行结果如下:
# ./lua-c-example
load test.lua ok
run test.lua ok
get_data
1 2 4 5 6 7 8 9 10
三、C/C++通过Lua 提供的接口访问Sqlite3 数据库
结合一、二的讲解,应该很容易从C/C++端,通过lua提供的函数接口,访问sqlite3数据库。
1、Lua创建数据库和表的脚本如下:
local sqlite3 = require("lsqlite3") --加载 Lua Sqlite3 库
local mydb = sqlite3.open("test.db") --打开/或者创建test.db数据库
mydb:exec[[
CREATE TABLE main (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
year INTEGER,
month INTEGER,
day INTEGER,
hour INTEGER,
minite INTEGER,
second INTEGER);]]
2、数据插入操作,使用lua sqlite3的statement语句,以提高插入大量数据时的效率:
main_stmt = mydb:prepare[[
INSERT INTO main VALUES (
?, ?, ?, ?, ?, ?, ?);
]] --7 values 申明一个插入操作的statement
如下Lua函数为提供给C的数据库插入接口函数:
function insert_main(...)
local args = {...}
main_stmt:bind_values(nil,args[1],args[2],args[3],args[4],args[5],args[6])
main_stmt:step()
main_stmt:reset()
return 9
end --提供一个供C/C++调用的Lua函数 insert_main。此函数为数据插入接口。
3、数据查询操作,使用lua sqlite3的statement语句,以提高频繁查询时的效率:
select_stmt_main = mydb:prepare("SELECT * FROM main;")
如下Lua函数为提供给C的数据库查询接口函数:
function select_main()
local stepret = select_stmt_main:step()
if stepret == sqlite3.ROW then
local ret = select_stmt_main:get_values() --返回的是一个lua array
return ret
else
select_stmt_main:reset() --重置statement,以便下一次从表头开始查询
return nil
end
end
以上操作Sqlite3数据库的Lua脚本保存到一个文件db.lua中。结合前面的一、二提供的测试代码,很容易就达到了我们的目的——C/C++通过Lua 提供的接口访问Sqlite3 数据库。