lua学习01:c调用lua、lua调用c、lua的协程、lua的常见API、lua读取配置文件总结
文章目录1.c通过虚拟机和虚拟栈调用luatest-vm.ctest-vm.lua打印效果2.lua通过虚拟机和虚拟栈调用clua-tbl.ctest-tbl.lua3.lua的协程test-co.lua展示效果4.userdata的使用1.c通过虚拟机和虚拟栈调用lualua编译指令:gcc -o test test-vm.c /usr/local/lib/liblua.a-Ilua/src -
文章目录
- 1.c通过虚拟机和虚拟栈调用lua
- 2.lua通过虚拟机和虚拟栈调用c
- 3.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 <stdio.h>
#include <stdlib.h>
#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 <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <stdio.h>
//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 <stdio.h>
#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 <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <assert.h>
#include <string>
#include <vector>
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}
- 实现效果
更多推荐
所有评论(0)