lua学习01:c调用lua、lua调用c、lua的协程、lua的常见API、lua读取配置文件总结

文章目录

    • 1.c通过虚拟机和虚拟栈调用lua
      • test-vm.c
      • test-vm.lua
      • 打印效果
    • 2.lua通过虚拟机和虚拟栈调用c
      • lua-tbl.c
      • test-tbl.lua
    • 3.lua的协程
      • test-co.lua
      • 展示效果
    • 4.常用的lua api解释(C语言函数)
      • 1)lua_State *luaL_newstate (void);
      • 2)lua_State* L=luaL_newstate();
      • 3)lua_State *lua_newstate (lua_Alloc f, void *ud);
      • 4)void luaL_openlibs (lua_State *L);
      • 5)lua_CFunction C调用lua使用的函数,可以用于赋值
      • 5)void lua_register (lua_State *L, const char *name, lua_CFunction f);把 C 函数 f 设到全局变量 name 中。 它通过一个宏定义:
      • 7)lua_gettop 获得lua栈的元素个数
      • 8)int lua_getglobal (lua_State *L, const char *name); 把全局变量 name 里的值压栈,返回该值的类型。
      • 9)void *lua_touserdata (lua_State *L, int index);返回给定索引处内存块的void*类型的指针
      • 10)void lua_pushlightuserdata (lua_State *L, void *p);把一个轻量用户数据压栈。
      • 11)
    • 5.userdata的使用
    • 6.lua读取配置文件

1.c通过虚拟机和虚拟栈调用lua

lua编译指令:

gcc -o test test-vm.c /usr/local/lib/liblua.a   -Ilua/src -Llua/src -llua -ldl -lm 
./test test-vm.lua

test-vm.c

// gcc -o test test-vm.c -Ilua/src -Llua/src -llua

#include 
#include 
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"

static void
call_func_0(lua_State *L, const char* funcname)
{
    lua_getglobal(L, funcname);	//1,c调用lua函数,虚拟栈通过c语言传入init,
                               	//栈是从下往上依次填写的,先函数入栈,再参数入栈
                               	//栈上索引为1的元素
    lua_pushinteger(L, 1);		//2,在虚拟栈里面给lua方法传入参数
    					  		//栈上索引为2的元素
    lua_call(L, 1, 0); 	  		//3.将1,2索引的元素 ,pop弹栈
    							//void lua_call (lua_State *L, int nargs, int nresults);
   	 							//压入栈的参数只有1个,所以第二个参数是1
    							//nresults为返回值的个数,由于是不需要返回值,所以为0
    							//L为传入的lua虚拟机

    printf("c\n");
    							// 空的栈
}

int main(int argc, char** argv)
{
    								//1.创建虚拟栈
    lua_State *L = luaL_newstate();// 1.执行字节码 2.存储lua的数据 3.gc
    
    								//2.加载lua库
    luaL_openlibs(L);				// lua库
    
    								//3.加载要执行的lua文件
    if (argc > 1) {
        if ( LUA_OK != luaL_dofile(L, argv[1]) ) {
            const char* err = lua_tostring(L, -1);
            fprintf(stderr, "err:\t%s\n", err);
            return 1;
        }
        call_func_0(L, "Init");
        call_func_0(L, "Loop");
        call_func_0(L, "Release");
        lua_close(L);                   
    }

    return 0;
}

test-vm.lua

-- package.cpath = "luaclib/?.so"

-- local so = require "lua-uv.c"

--这里用的都是全局的方法
function Init(args)
    print("call [init] function", args)
end

function Loop()
    print("call [loop] function")
    for k, v in ipairs({1,2,3,4,5}) do
        --so.echo(v)
        print(k.." : "..v)
    end
end

function Release()
    print("call [release] function")
end

打印效果

[root@legend1 test-lua]# gcc -o test test-vm.c /usr/local/lib/liblua.a   -Ilua/src -Llua/src -llua -ldl -lm 
[root@legend1 test-lua]# ./test test-vm.lua 
call [init] function    1
c
call [loop] function
1 : 1
2 : 2
3 : 3
4 : 4
5 : 5
c
call [release] function
c

2.lua通过虚拟机和虚拟栈调用c

  • 虚拟栈

a)栈上的都是lua类型的值
b)lua调用c的函数都得到一个新的栈,独立于之前的栈(lua调用c不需要维护栈的平衡)
c)c调用lua的时候(需要维护栈的平衡,有返回值要压栈),每一个协程都有一个栈,没有显示创建那么就是主协程上一个栈

  • 看虚拟栈的角度

①明确是c调用lua,还是lua调用c
②c调用lua,c有多个协程,每个协程一个虚拟栈
③lua调用c,每次调用都有一个虚拟栈

lua-tbl.c

#include 

#include 

#include 

#include 


//lua调用c,c语言函数的参数只有一个,就是虚拟机
static int
lecho (lua_State *L) {
    const char* str = lua_tostring(L, -1);
    fprintf(stdout, "%s\n", str);           //将栈顶数据打印到屏幕上,然后return
    return 0;
}

//写一个导出给lua使用的数组
static const luaL_Reg l[] = {// 导出给lua使用数组
	{"echo", lecho},         //lecho是函数指针
                             //echo是lua中使用的函数名
	{NULL, NULL},
};

//要符合规范lua才能使用,函数得以luaopen_开头,tbl_c到lua里面,_会变成.号
int
luaopen_tbl_c(lua_State *L) { // local tbl = require "tbl.c"
    // 创建一张新的表,并预分配足够保存下数组 l 内容的空间
	// luaL_newlibtable(L, l);
	// luaL_setfuncs(L, l, 0);
    luaL_newlib(L, l);
	return 1;
}

test-tbl.lua

package.cpath = "luaclib/?.so" --c库的路径

local so = require "tbl.c"

so.echo("hello world") -- so.echo调用了c函数,生成了新的虚拟栈
-- 上面lua调用c完毕,栈已经被清空了,此时上面的虚拟栈被回收了
so.echo("hello world1")-- 新的虚拟栈
so.echo("hello world2")-- 新的虚拟栈

-- 在lua层中调用c函数 不需要考虑 虚拟栈的平衡

--[[
    1. c调用lua  c有多个协程 每个协程一个虚拟栈
    2. lua调用c  每次调用都有一个虚拟栈

]]

3.lua的协程

test-co.lua

-- 主协程

--做的事:主协程往里面传一个参数,传完参数就+1
--创建之后不会运行
local co = coroutine.create(function (arg1)
    -- 这里面就是创建的协程
    print(coroutine.running(), arg1)
    local ret1 = arg1+1                 --将参数+1
    local arg2 = coroutine.yield(ret1)  --yield表示让出执行权(并将参数传回主协程)
    local ret2 = arg2+1
    return ret2
end)

local co1 = coroutine.running()
local arg1 = 1
local ok, ret1, ret2
ok, ret1 = coroutine.resume(co, arg1)   --先往协程里面传第一个参数,ret1接住传回的参数
                                        --co是协程的名字
                                        --arg1是传入的参数
                                        --ok是看之前的操作是否发生错误,执行成功为true
print(co1, ok, ret1)
ok, ret2 = coroutine.resume(co, ret1)   --再去调用这个函数,在yield后面传递数据,ret2借助返回值
print(co1, ok, ret2)

--lua中协程是怎么调度的:
-- lua中是一个协程调度另一个协程(主协程去调用创建的协程)
-- lua 不能做csp模型?  go csp 协程: 同时有多个协程在运行
-- 因为lua的协程 同时只有一个在运行,一个协程在运行,另一个协程就在挂起

展示效果

在这里插入图片描述

4.常用的lua api解释(C语言函数)

  • lua索引信息:

CAPI使用索引(index)来引用栈中的元素。第一个被压入栈的元素索引为1,第二个被压入的元素索引为2,依此类推。我们还可以以栈顶为参照,使用负数索引来访问栈中的元素

1)lua_State *luaL_newstate (void);

作用:创建虚拟栈,也就是创建一个新的 Lua 状态机。 它以一个基于标准 C 的 realloc 函数实现的内存分配器 调用 lua_newstate 。 并把可打印一些出错信息到标准错误输出的 panic 函数(参见 §4.6) 设置好,用于处理致命错误。

2)lua_State* L=luaL_newstate();

作用:L是一个指向该虚拟机堆栈的指针

3)lua_State *lua_newstate (lua_Alloc f, void *ud);

作用:创建一个运行在新的独立的状态机中的线程。 如果无法创建线程或状态机(由于内存有限)则返回 NULL。 参数 f 是一个分配器函数; Lua 将通过这个函数做状态机内所有的内存分配操作。 第二个参数 ud ,这个指针将在每次调用分配器时被转入。

4)void luaL_openlibs (lua_State *L);

作用:打开指定状态机中的所有 Lua 标准库。

5)lua_CFunction C调用lua使用的函数,可以用于赋值

typedef int (lua_CFuntion) (lua_State L)
C调用lua使用的函数,可以用于赋值

为了正确的和 Lua 通讯, C 函数必须使用下列协议。 这个协议定义了参数以及返回值传递方法: C 函数通过 Lua 中的栈来接受参数, 参数以正序入栈(第一个参数首先入栈)。 因此,当函数开始的时候, lua_gettop(L) 可以返回函数收到的参数个数。 第一个参数(如果有的话)在索引 1 的地方, 而最后一个参数在索引 lua_gettop(L) 处。 当需要向 Lua 返回值的时候, C 函数只需要把它们以正序压到堆栈上(第一个返回值最先压入), 然后返回这些返回值的个数。 在这些返回值之下的,堆栈上的东西都会被 Lua 丢掉。 和 Lua 函数一样,从 Lua 中调用 C 函数也可以有很多返回值。

下面这个例子中的函数将接收若干数字参数,并返回它们的平均数与和:

 static int foo (lua_State *L) {
   int n = lua_gettop(L);    /* 参数的个数 */
   lua_Number sum = 0.0;
   int i;
   for (i = 1; i <= n; i++) {
     if (!lua_isnumber(L, i)) {
       lua_pushliteral(L, "incorrect argument");
       lua_error(L);
     }
     sum += lua_tonumber(L, i);
   }
   lua_pushnumber(L, sum/n);        /* 第一个返回值 */
   lua_pushnumber(L, sum);         /* 第二个返回值 */
   return 2;                   /* 返回值的个数 */
 }

5)void lua_register (lua_State *L, const char *name, lua_CFunction f);把 C 函数 f 设到全局变量 name 中。 它通过一个宏定义:

 #define lua_register(L,n,f) \
        (lua_pushcfunction(L, f), lua_setglobal(L, n))

7)lua_gettop 获得lua栈的元素个数

int lua_gettop (lua_State *L);
返回栈顶元素的索引。 因为索引是从 1 开始编号的, 所以这个结果等于栈上的元素个数; 特别指出,0 表示栈为空。

8)int lua_getglobal (lua_State *L, const char *name); 把全局变量 name 里的值压栈,返回该值的类型。

这个name比如说red、green、blue的值可能设置在table里面,压栈后,可用lua_gettable获得值,

#include 
#define MAX_COLOR       255 
extern "C"
{
#include "lua-5.2.2/src/lauxlib.h"
#include "lua-5.2.2/src/lualib.h"
#include "lua-5.2.2/src/lstate.h"
}

int getfield(const char* key, lua_State* L)
{
    int result = 0;
    lua_pushstring(L, key);
    lua_gettable(L, -2);//以栈顶的值作为key来访问-2位置上的table。并将其值放入栈顶
    if (!lua_isnumber(L, -1))//将栈顶元素转换成double类型
    {
    }
    result = (int)lua_tonumber(L, -1) * MAX_COLOR;//lua_tonumber只是转换栈顶元素是不出栈的
    lua_pop(L, 1);//从栈中弹出1个元素
    return result;
}

int main(int argc, char *argv[])
{
    const char* buf = "background = {r=30, g=10, b=0} ";//这个是个预先加载的字符串
    lua_State* L = luaL_newstate();
    luaL_dostring(L, buf);
    lua_getglobal(L, "background");//获取全局变量的background的值,并将其放入栈顶
    if (!lua_istable(L, -1))//判断是否是table
    {
        
    }
    int red = getfield("r", L);
    int green = getfield("g", L);
    int black = getfield("b", L);

    getchar();

    return 0;
}

9)void *lua_touserdata (lua_State L, int index);返回给定索引处内存块的void类型的指针

如果给定索引处的值是一个完全用户数据, 函数返回其内存块的地址。 如果值是一个轻量用户数据, 那么就返回它表示的指针。 否则,返回 NULL 。

10)void lua_pushlightuserdata (lua_State *L, void *p);把一个轻量用户数据压栈。

用户数据是保留在 Lua 中的 C 值。 轻量用户数据 表示一个指针 void*。 它是一个像数字一样的值: 你不需要专门创建它,它也没有独立的元表,而且也不会被收集(因为从来不需要创建)。 只要表示的 C 地址相同,两个轻量用户数据就相等。

11)

5.userdata的使用

6.lua读取配置文件

  • 代码

xml文件层次分明,写起来比较复杂。lua脚本作为一门轻量小巧的脚本语言,常用在游戏开发中或者web应用中。

#include 
#include 
#include 
#include 
#include 
#include 
 
extern "C"
{
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
 
class luaConfile
{
public:
	luaConfile();
	~luaConfile();
    /*
    * \brief 加载lua配置文件
    * \param file lua配置文件路径
    */
	bool loadFile(const char* file);
    /*
    * \brief 解析获取全局数据的字符串
    * \param key全局数据的键 例:serverIP="127.0.0.1" key就是"serverIP" 得value="127.0.0.1"
    * \param value 返回的字符串
    * 
    * return 是否读取成功
    */
	bool getStringVal(const char* key, std::string& value);
    /*
    * \brief 解析获取全局数据的int型数值
    * \param key全局数据的键 例:serverPort=9010 key就是"serverPort" 得value=9010
    * \param value 返回的int值
    * 
    * return 是否读取成功
    */
	bool getIntegerVal(const char* key, int& value);
    /*
    * \brief 解析获取全局数据的double型数值
    * \param key全局数据的键 例:param=10.35 key就是"param" 得value=10.35
    * \param value 返回的double值
    * 
    * return 是否读取成功
    */
	bool getDoubleVal(const char* key, double& value);
    /*
    * \brief 解析获取全局数据的bool型数值
    * \param key全局数据的键 例:isopen=true key就是"isopen" 得value=true
    * \param value 返回的bool值
    * 
    * return 是否读取成功
    */
	bool getBooleanVal(const char* key, bool& value);
    /*
    * \brief 解析获取table中的字符串
    * \param table table的名字
    * \param table中数据的键 例:mysqldb={ host="127.0.0.1" port=8080} table为"mysqldb"     
    * key 为"host" 获取value="127.0.0.1"
    * \param value 返回的字符串
    * 
    * return 是否读取成功
    */
	bool getStringValFromTable(const char* table, const char* key, std::string& value);
	bool getIntegerValFromTable(const char* table, const char* key, int& value);
	bool getDoubleValFromTable(const char* table, const char* key, double& value);
	bool getBooleanValFromTable(const char* table, const char* key, bool& value);
    /*
    * \brief 解析获取table中的int数组
    * \param table table的名字 例:blackList={123,567,101} table为"blackList"     
    *  获取arrayInt = {123,567,101}
    * \param arrayInt 返回的int数组
    * 
    * return 是否读取成功
    */
	bool readIntArrayFromTable(const char* table, std::vector<int>& arrayInt);
private:
	lua_State *L;
};
 
luaConfile::luaConfile()
{
	//创建lua状态
	L = luaL_newstate();
	assert(NULL != L);
 
}
luaConfile::~luaConfile()
{
	//关闭lua状态
	lua_close(L);
}
 
bool luaConfile::loadFile(const char* file)
{
	//加载并运行lua文件 等价于 luaL_loadfile(L, filen) || lua_pcall(L, 0, LUA_MULTRET, 0)
	int recode = luaL_dofile(L, file);
	if (recode)
	{
		printf("luaL_dofile error!\n");
		return false;
	}
	return true;
}
 
bool luaConfile::getStringVal(const char* key, std::string& value)
{
	//把全局变量 key里的值压入堆栈
	lua_getglobal(L, key);
	if (!lua_isstring(L, -1)) {//检查索引位置的值是否是一个字符串或是一个数字
		return false;
	}
	value = lua_tostring(L, -1);// 查询栈顶元素转化为C字符串
	return true;
}
 
bool luaConfile::getIntegerVal(const char* key, int& value)
{
	lua_getglobal(L, key);
	if (!lua_isnumber(L, -1)) {//检查索引位置的值是否是个数字类型
		return false;
	}
	value = lua_tointeger(L, -1);
	return true;
}
 
bool luaConfile::getDoubleVal(const char* key, double& value)
{
	lua_getglobal(L, key);
	if (!lua_isnumber(L, -1)) {
		return false;
	}
	value = lua_tonumber(L, -1);
	return true;
}
bool luaConfile::getBooleanVal(const char* key, bool& value)
{
	lua_getglobal(L, key);
	if (!lua_isboolean(L, -1)) {
		return false;
	}
	value = lua_toboolean(L, -1);
	return true;
}
 
bool luaConfile::getStringValFromTable(const char* table, const char* key, std::string& value)
{
	lua_getglobal(L, table);
	lua_pushstring(L, key);//把指针key指向的以零结尾的字符串压栈
	lua_gettable(L, -2);//值会放在栈顶,同时刚才压入的元素名字被弹出
	if (!lua_isstring(L, -1)) {
		return false;
	}
	value = lua_tostring(L, -1);
	lua_pop(L, 2);//从堆栈中弹出 n 个元素
	return true;
}
 
bool luaConfile::getIntegerValFromTable(const char* table, const char* key, int& value)
{
	lua_getglobal(L, table);
	lua_pushstring(L, key);
	lua_gettable(L, -2);
	if (!lua_isnumber(L, -1)) {//lua 5.3 支持用lua_isinteger这样才准确
		return false;
	}
	value = lua_tointeger(L, -1);
	lua_pop(L, 2);
	return true;
}
 
bool luaConfile::getDoubleValFromTable(const char* table, const char* key, double& value)
{
	lua_getglobal(L, table);
	lua_pushstring(L, key);
	lua_gettable(L, -2);
	if (!lua_isnumber(L, -1)) {
		return false;
	}
	value = lua_tonumber(L, -1);
	lua_pop(L, 2);
	return true;
}
 
bool luaConfile::getBooleanValFromTable(const char* table, const char* key, bool& value)
{
	lua_getglobal(L, table);
	lua_pushstring(L, key);
	lua_gettable(L, -2);
	if (!lua_isboolean(L, -1)) {
		return false;
	}
	value = lua_toboolean(L, -1);
	lua_pop(L, 2);
	return true;
}
 
bool luaConfile::readIntArrayFromTable(const char* table, std::vector<int>& arrayInt)
{
	lua_getglobal(L, table);
	int it = lua_gettop(L);
	lua_pushnil(L);//第一个 key
	while (lua_next(L, it))//用一下 'key' (在索引 -2 处) 和 'value' (在索引 -1 处)用于遍历
	{
		if (!lua_isnumber(L, -1))
			continue;
 
		arrayInt.push_back((int)lua_tonumber(L, -1));
		lua_pop(L, 1);
	}
	lua_pop(L, 1);
	return (arrayInt.size() > 0);
}
 
int main(int argc, char **argv)
{
 
	luaConfile conf;
	conf.loadFile("config.lua");
	int intfPort;
 
	if (conf.getIntegerVal("serverPort", intfPort))
	{
		printf("serverPort:%d\n", intfPort);
	}
 
	std::string dbname;
	if (conf.getStringValFromTable("mysqldb", "name", dbname))
	{
		printf("dbname:%s\n", dbname.c_str());
	}
	int port;
	if (conf.getIntegerValFromTable("mysqldb", "port", port))
	{
		printf("port:%d\n", port);
	}
	std::vector<int> blackList;
	if (conf.readIntArrayFromTable("blackList", blackList))
	{
		for (size_t i = 0; i < blackList.size(); i++)
		{
			printf("blackList:%lu,%d\n", i, blackList[i]);
		}
	}
	return 0;
}
  • lua代码
 
mysqldb = {host = "127.0.0.1",
          port = 3306,
          user = "root",
          passwd = "123456",
          name = "xxxxx" }
serverPort = 9090
serverIP = "127.0.0.1"
blackList = {123,145,101,345,1000}
  • 实现效果

lua学习01:c调用lua、lua调用c、lua的协程、lua的常见API、lua读取配置文件总结_第1张图片

你可能感兴趣的:(lua,lua,c语言)