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 数据库。


Logo

快速构建 Web 应用程序

更多推荐