Lua简介

Lua 是一门扩展式程序设计语言,被设计成支持通用过程式编程,并有相关数据描述设施。 同时对面向对象编程、函数式编程和数据驱动式编程也提供了良好的支持。 它作为一个强大、轻量的嵌入式脚本语言,可供任何需要的程序使用。 Lua 由 clean C(标准 C 和 C++ 间共通的子集) 实现成一个库。

作为一门扩展式语言,Lua 没有 “main” 程序的概念: 它只能 嵌入 一个宿主程序中工作, 该宿主程序被称为 被嵌入程序 或者简称 宿主 。 宿主程序可以调用函数执行一小段 Lua 代码,可以读写 Lua 变量,可以注册 C 函数让 Lua 代码调用。 依靠 C 函数,Lua 可以共享相同的语法框架来定制编程语言,从而适用不同的领域。 Lua 的官方发布版包含一个叫做 lua 的宿主程序示例, 它是一个利用 Lua 库实现的完整独立的 Lua 解释器,可用于交互式应用或批处理。

Lua 是一种轻量小巧的脚本语言,用标准C语言编写并以源代码形式开放, 其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。

Lua 特性

  • 轻量级: 它用标准C语言编写并以源代码形式开放,编译后仅仅一百余K,可以很方便的嵌入别的程序里。
  • 可扩展: Lua提供了非常易于使用的扩展接口和机制:由宿主语言(通常是C或C++)提供这些功能,Lua可以使用它们,就像是本来就内置的功能一样。
  • 其它特性:
    • 支持面向过程(procedure-oriented)编程和函数式编程(functional programming);
    • 自动内存管理;只提供了一种通用类型的表(table),用它可以实现数组,哈希表,集合,对象;
    • 语言内置模式匹配;闭包(closure);函数也可以看做一个值;提供多线程(协同进程,并非操作系统所支持的线程)支持;
    • 通过闭包和table可以很方便地支持面向对象编程所需要的一些关键机制,比如数据抽象,虚函数,继承和重载等。

Lua安装

如果没有时间或意愿自己编译Lua,可单击如下图位置下载生成的二进制文件进行操作,解压即可完成安装。

image-20230219215421992

本文档进行源码编译演示。

VS编译lua源码

lua静态库编译

  1. 新建vs静态库工程,将下载的Lua源码引入项目中(除lua.c和luac.c)
  2. 将项目改为C编译,不使用预编译头

image-20230219220354828

image-20230219220517123

编译生成静态库。

lua动态库编译

将配置类型更改为动态库

image-20230219220746612

编译生成动态库。

lua解释器编译

将配置类型更改为应用程序

image-20230219220858239

将lua.c引入源文件中;

编译生成lua解释器。

lua编译器编译

将lua.c从源文件中移除,把luac.c引入源文件,目标文件名改为$(ProjectName)c

image-20230219221641881

编译生成lua编译器器。

生成路径下有这些文件

image-20230219222323532

在该路径下运行cmd输入lua5.4.4.exe -i显示如下即为成功,如果需要,可将其配至环境变量下。

image-20230219222506808

Lua项目源码

百度网盘下载 提取码:oq8j

Lua入门

Lua参考手册

目前5.4只有官网提供的英文手册

中文手册可以参考云风译版 5.3版本

数据类型

Lua 是动态类型语言,变量不要类型定义,只需要为变量赋值。 值可以存储在变量中,作为参数传递或结果返回。

Lua 中有 8 个基本类型分别为:nil、boolean、number、string、userdata、function、thread 和 table。

数据类型 描述
nil 这个最简单,只有值nil属于该类,表示一个无效值(在条件表达式中相当于false)。
boolean 包含两个值:false和true。
number 表示双精度类型的实浮点数
string 字符串由一对双引号或单引号来表示
function 由 C 或 Lua 编写的函数
userdata 表示任意存储在变量中的C数据结构
thread 表示执行的独立线路,用于执行协同程序
table Lua 中的表(table)其实是一个”关联数组”(associative arrays),数组的索引可以是数字、字符串或表类型。在 Lua 里,table 的创建是通过”构造表达式”来完成,最简单构造表达式是{},用来创建一个空表。

基本语法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
--输出语句
print("hello lua")

--定义局部变量
do
local num = 100
print(num)
end

--定义无参函数
function sayHello()
print("hello")
end
sayHello()

--定义有参函数
function max(a,b)
if a>b then
return a
else
return b
end
end
print(max(3,5))

--遍历
for var = 1, 100 do
print(var)
end

1
2
3
4
5
6
7
8
9
10
11
12
13
--定义空表
tab = {}
--赋值
tab.word = 'hello'
tab.num = 100
tab['name'] = 'zhangsan'

print(tab.name)
print(tab['word'])
--遍历表
for k,v in pairs(tab) do
print(k,v)
end

数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
--以表来实现数组
arr = {1,2,3,4,"hello"}

for key, var in pairs(arr) do
print(key,var)
end
--lua索引是从1开始
arr = {}
for var = 1, 100 do
table.insert(arr, 1, var)
end

for key,var in pairs(arr) do
print(key,var)
end

面向对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function People(name)
local self = {}
local function init()
self.name = name
end
self.sayhello = function()
print("hello "..self.name)
end

init()
return self
end

local p = People('zhangsan')
p.sayhello()

--继承
function Man(name)
local self = People(name)

--重写
self.sayhello = function()
print("the man say hello to "..self.name)
end

self.sayhi = function()
print("the man say hello to "..self.name)
end

return self
end

local p = Man('lisi')
p.sayhello()

C++ 调用 Lua

C++ 可以获取Lua中的值,可以调用Lua函数,还可以修改Lua文件

1)C++获取Lua值
使用lua_getglobal来获取值,然后将其压栈
使用lua_toXXX将栈中元素取出转成相应的C++类型的值
如果Lua值为table类型的话,通过lua_getfield和lua_setfield获取和修改表中元素的值

2)C++调用Lua函数
使用lua_getglobal来获取函数,然后将其压入栈;
如果这个函数有参数的话,就需要依次将函数的参数也压入栈;
这些准备工作都准备就绪以后,就调用lua_pcall开始调用函数了,调用完成以后,会将返回值压入栈中;

hello.lua

1
2
3
4
5
str = "I am so cool"  
tbl = {name = "shun", id = 20114442}
function add(a,b)
return a * b
end

示例代码

引入头文件

1
2
3
4
5
6
extern "C"
{
#include "Lua5.4.4/include/lua.h"
#include "Lua5.4.4/include/lauxlib.h"
#include "Lua5.4.4/include/lualib.h"
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//1.创建Lua状态机,返回一个指向堆栈的指针
lua_State *L = luaL_newstate();
if (L == NULL)
{
return 0;
}

//2.加载lua文件
int bRet = luaL_loadfile(L, "hello.lua");
if (bRet)
{
cout << "load file error" << endl;
return 0;
}

//3.运行lua文件
bRet = lua_pcall(L, 0, 0, 0);
if (bRet)
{
cout << "pcall error" << endl;
return 0;
}

//4.读取全局变量,
// 1.把 str 压栈 2.由lua去寻找全局变量str的值,并将str的值返回栈顶(替换str)
// 如果存在相同命名的其他变量、table或函数,就会报错(读取位置发生访问冲突)
lua_getglobal(L, "str");
// -1取出栈顶元素,转化为string
string str = lua_tostring(L, -1);
cout << "str = " << str.c_str() << endl;
string str1 = lua_tostring(L, -1);
cout << "str = " << str1.c_str() << endl;
//5.读取table,把table压栈
lua_getglobal(L, "tbl");
//-------------------------------
// 1.把name压入栈中,2.由lua去寻找table中name键的值,并将键值返回栈顶(替换name)
// 相当于lua_pushstring(L, "name") + lua_gettable(L, -2)执行结果是一样的
lua_getfield(L, -1, "name");
// 把name压入栈中
//lua_pushstring(L, "name");
// 弹出栈上的name,并从表中找到name的键值,把结果放在栈上相同的位置
//lua_gettable(L, -2);
//---------------------------------
str = lua_tostring(L, -1);
// 因为table在栈顶的下面,所以取-2,把id压栈,由lua找到table中id键的值,并返回栈顶(替换id)
lua_getfield(L, -2, "id");
// id的值已经在栈顶,取-1
int id = lua_tonumber(L, -1);
cout << "tbl:name = " << str.c_str() << endl;
cout << "tbl:id = " << id << endl;

// 读取函数,
// 1.将函数add放入栈中,2.由lua去寻找函数add,并将函数add返回栈顶(替换add)。
lua_getglobal(L, "add"); // 获取函数,压入栈中
lua_pushnumber(L, 10); // 压入第一个参数
lua_pushnumber(L, 20); // 压入第二个参数
// 栈过程:参数出栈->保存参数->参数出栈->保存参数->函数出栈->调用函数->返回结果入栈
// 调用函数,调用完成以后,会将返回值压入栈中,2表示参数个数,1表示返回结果个数。
int iRet = lua_pcall(L, 2, 1, 0);
if (iRet)
{
// 调用出错
const char *pErrorMsg = lua_tostring(L, -1);
cout << pErrorMsg << endl;
lua_close(L);
return 0;
}
if (lua_isnumber(L, -1)) //取值输出
{
int fValue = lua_tonumber(L, -1);
cout << "Result is " << fValue << endl;
}

// 栈的索引方式可以是正数也可以是负数,区别是:1永远表示栈底元素,-1永远表示栈顶元素。
//至此,栈中的情况是:
//=================== 栈顶 ===================
// 索引 类型 值
// 5或-1 int 30
// 4或-2 int 20114442
// 3或-3 string shun
// 2或-4 table tbl
// 1或-5 string I am so cool~
//=================== 栈底 ===================

lua_pushstring(L, "Master");
// 会将"Master"值出栈,保存值,找到到table的name键,如果键存在,存储到name键中
lua_setfield(L, 2, "name");
// 读取
lua_getfield(L, 2, "name");
str = lua_tostring(L, -1);
cout << "tbl:name = " << str.c_str() << endl;

// 创建新的table
lua_newtable(L);
lua_pushstring(L, "A New friend1");
str = lua_tostring(L, -1);
//lua_pushstring(L, "A New friend2");
lua_setfield(L, -2, "name");
// 读取
lua_getfield(L, -1, "name");
str = lua_tostring(L, -1);
cout << "newtbl:name = " << str.c_str() << endl;

//7.关闭state
// 销毁指定 Lua 状态机中的所有对象, 并且释放状态机中使用的所有动态内存。
// (如果有垃圾收集相关的元方法的话,会调用它们)
lua_close(L);

//getchar();
return 0;

源码下载

百度网盘

提取码:lgqi