C++与lua联合编程
- 一、环境配置
- 二、lua基本语法
- 1.第一个lua和C++程序
- 2.基本数据类型和变量
- 2.1 Nil
- 2.2 Booleans
- 2.3 Numbers
- 2.4 String(最常用)
- 3. 字符串处理
- 3.1 错误处理
- 3.2 字符串长度:string.len
- 3.3 字符串子串 :string.sub
- 3.4 字符串查找: string.find
- 3.5字符串替换: string.gsub
- 4.控制结构语句
- 4.1 if条件语句
- 4.2 while 循环语句
- 4.3 repeat循环语句
- 4.4 for循环语句
- 5.表和函数
- 5.1表的大小
- 5.2 插入元素
- 5.3 删除元素
- 5.4 二维数组
- 5.5 函数
- 普通参数
- 可变参数
- 返回值
- 使用变量定义函数
- 三、Lua调用C++
- 1.原理
- 2.传递参数类型
- 2.1 传递普通参数
- 2.2 传递数组参数
- 2.3 传递表
- 获取表中内容
- 2.4 参数类型检查
- 3.获取返回值类型
- 3.1 返回普通类型
- 3.2 返回表
- 四、C++调用Lua
- 1.C++给Lua传递变量并访问Lua的全局变量
- 2.C++给Lua传递表并访问Lua的表
- 3.C++调用Lua的函数
- 4.错误显示和堆栈清理
- 5.C++调用Lua的函数并传递参数
- 6.C++调用Lua函数并获取返回的表
- 五、Lua和MFC结合开发
- 1.通过Lua配置窗口大小
- 2.通过Lua弹出提示窗口
- 3.通过Lua载入文件并在MFC中显示
一、环境配置
首先下载lua源码。
将main函数改名后编译。
目前我的工作目录是工程文件所在目录,src存放了lua的源码。
修改dll输出路径。
指定导出宏生成.lib文件
生成 .lib 文件需要导出宏,这是为了确保在编译和链接过程中正确处理导入和导出的符号。具体来说,导出宏(export macro)用于标记哪些函数、变量或类应当从 DLL 中导出,使其可以在 DLL 外部被访问。
修改lib的输出路径。
二、lua基本语法
1.第一个lua和C++程序
2.基本数据类型和变量
- 全局变量
类型+变量名
- 本地变量(尽量用本地变量,保证及时的垃圾回收)
Lua基本数据类型:
2.1 Nil
在 Lua 中,nil
是一种特殊的数据类型,用于表示“无值”或“空值”。它的主要用途有以下几个方面:
- 表示变量没有值:
nil
用于区分变量是否有值。例如,如果一个变量被赋值为nil
,这意味着该变量目前没有任何值。 - 删除表中的元素:在 Lua 中,将表(table)中的元素赋值为
nil
,可以删除该元素。 - 全局变量和垃圾回收:将全局变量设置为
nil
可以将其标记为可回收,从而允许垃圾回收器释放相关的内存。
local a = nil
print(type(a)) -- 输出:nil
这段代码展示了如何将一个局部变量 a
设为 nil
,然后通过 type
函数检查其类型。type(a)
会返回 nil
,表示变量 a
目前没有值。
2.2 Booleans
在 Lua 中,boolean
类型有两个值:true
和 false
。它们通常用于条件判断和控制流(如 if
语句、循环等)。
在 Lua 中,只有 false
和 nil
会被视为假(false),其他所有值,包括数值 0
,都被视为真(true)。这与一些其他编程语言(如 C/C++)中的行为不同,在那些语言中,0
通常被视为假。
2.3 Numbers
-
Lua 中没有整数,都是用浮点数运算:
在 Lua 5.3 之前,Lua 语言只使用浮点数来表示所有的数字,这意味着即使你写的是一个整数,内部也是用浮点数来存储和运算的。这种浮点数对应于 C 语言中的
double
类型。 -
新版 Lua 中有基于 64 位的整型:
从 Lua 5.3 开始,Lua 引入了整数类型(
integer
),这是一个 64 位的整数。这一变化使得 Lua 可以更高效地处理整数运算,同时也保留了对浮点数的支持。现在,Lua 支持两种数字类型:整数和浮点数。 -
tonumber
转换格式:tonumber
是 Lua 内置的一个函数,用于将字符串或其他类型的值转换为数字。这个函数在需要将其他数据类型转换为数字时非常有用。
2.4 String(最常用)
1.tostring 转换格式:tostring
函数可以将其他数据类型转换为字符串。
2.多行字符串赋值:使用 [[
和 ]]
可以定义多行字符串,这在需要处理大段文本时非常有用。
3.转义字符:Lua 中的字符串可以使用与 C 语言相同的转义字符。例如,\n
表示换行,\"
表示双引号。
4.字符串拼接:Lua 使用 ..
运算符进行字符串拼接。
3. 字符串处理
在Lua中,字符串处理是一个非常常见的任务,Lua提供了一些内置函数来处理字符串。
3.1 错误处理
3.2 字符串长度:string.len
string.len
函数用于获取字符串的长度。
加载string库
在 Lua 5.2 及以后的版本中,luaL_openlibs
函数会自动打开所有标准库。
3.3 字符串子串 :string.sub
string.sub
函数用于从字符串中提取子串。参数包括字符串、起始位置和结束位置。
3.4 字符串查找: string.find
string.find
函数用于在字符串中查找子串,返回子串的起始和结束位置。它支持正则表达式。
3.5字符串替换: string.gsub
string.gsub
函数用于在字符串中替换子串。它支持正则表达式。
4.控制结构语句
4.1 if条件语句
条件语句结构:
if 条件 then-- then-part
elseif 其他条件 then-- elseif-part
else-- else-part
end
在条件语句中,可以使用逻辑操作符来构建更复杂的条件表达式。常见的逻辑操作符包括:
<
:小于>
:大于<=
:小于等于>=
:大于等于~=
:不等于==
:等于
4.2 while 循环语句
while
循环语句用于在条件为真时重复执行某段代码。当条件变为假时,循环结束。while
循环的基本结构如下:
while 条件 do-- 循环体
end
在循环中,可以使用 break
语句提前退出循环。
在 Lua 中,not
操作符用于取反一个条件表达式。在使用 not
操作符时,注意加上括号以确保逻辑的正确性。
4.3 repeat循环语句
repeat...until
循环类似于 while
循环,但它至少会执行一次循环体,然后再检查条件是否为真。如果条件为假,循环体会继续执行,直到条件为真时退出循环。
repeat-- 循环体
until 条件
4.4 for循环语句
Lua 提供了两种类型的 for
循环:数值循环和泛型循环。
数值循环用于遍历一段数值范围,具有固定的步长。
for var = from, to, step do-- 循环体
end
泛型循环用于遍历集合,如表(数组)或字典。Lua 提供了 pairs
和 ipairs
函数来支持泛型循环。
pairs
遍历表的所有键值对,适用于字典。
ipairs
以整数顺序遍历表的元素,适用于数组。
5.表和函数
5.1表的大小
table.getn(t)
函数用于获取表 t
的大小,但在 Lua 5.1 之后的版本中,建议使用 #
操作符。
在 Lua 中,#
操作符(长度运算符)只能用于顺序整数键的表(即数组),而不能用于键为字符串或非连续整数的表(即字典)。
5.2 插入元素
table.insert(t, pos, value)
函数用于在表 t
中插入一个元素。如果没有指定 pos
,则默认在表的末尾插入。
字典的元素插入和删除
5.3 删除元素
table.remove(t, [pos])
函数用于从表 t
中删除一个元素。如果没有指定 pos
,则默认删除表的最后一个元素。函数返回被删除的元素。
5.4 二维数组
5.5 函数
function func_name(arguments-list)-- statements-list
end
Lua 函数可以接受普通参数和可变参数。
普通参数
可变参数
返回值
使用变量定义函数
三、Lua调用C++
1.原理
Lua 调用 C++ 函数的基本原理是将 C++ 函数注册到 Lua 虚拟机中,使其可以在 Lua 脚本中像普通 Lua 函数一样调用。这涉及以下几个步骤:
- 定义C++函数:首先,我们定义一个C++函数,例如
add
或greet
。这些函数的参数类型都是lua_State*
,并且返回值类型是int
。 - 获取参数:使用
lua_tonumber
或lua_tostring
等函数从Lua栈中获取参数。 - 执行逻辑:在C++函数中执行所需的逻辑操作。
- 返回结果:将结果压入Lua栈,并返回结果数量。
- 注册函数:使用
lua_register
将C++函数注册为Lua全局函数。 - 调用函数:在Lua脚本中,直接调用注册的C++函数
返回值数量:函数最后返回一个整数,表示压入Lua栈的返回值数量。
2.传递参数类型
Lua 和 C++ 之间可以传递多种类型的参数,包括数字、字符串、布尔值和用户自定义类型(如指针或结构体)。
lua_gettop(lua_State* L)
:返回堆栈中元素的数量,即传递给函数的参数数量。lua_isnumber(lua_State* L, int index)
:检查堆栈中的值是否是数字。lua_tonumber(lua_State* L, int index)
:将堆栈中的值转换为数字。lua_isstring(lua_State* L, int index)
:检查堆栈中的值是否是字符串。lua_tostring(lua_State* L, int index)
:将堆栈中的值转换为字符串。lua_pushnumber(lua_State* L, lua_Number n)
:将一个数字压入堆栈。lua_pushstring(lua_State* L, const char* s)
:将一个字符串压入堆栈。
在 Lua 中,参数的位置是从 1 开始索引的。第一个参数的位置是 1,第二个参数的位置是 2,以此类推。通过在 C++ 函数中使用 lua_tonumber
或 lua_tostring
等函数,并传入相应的索引,可以获取相应的参数值。
2.1 传递普通参数
2.2 传递数组参数
lua_gettable
使用栈顶的索引(即 i
)从表中获取对应的值,并将该值压入栈顶。例如,如果 i
为 1
,获取数组中第一个元素,并将该元素压入栈顶。
在Lua中,所有的操作都是通过堆栈完成的。当你希望从表中获取某个元素时,首先需要将该元素的索引(下标)压入堆栈。
lua_pushnumber(L, i); // 将索引 i 压入栈顶
这行代码将当前的索引 i
压入堆栈。lua_pushnumber
函数将一个数字压入堆栈。
在将索引压入堆栈之后,使用 lua_gettable
函数来获取表中的值。lua_gettable
会使用栈顶的值作为索引,从给定位置的表中获取相应的值,并将该值压入栈顶。
lua_gettable(L, 1); // 从索引为 1 的表中获取值(根据栈顶的索引 i)
在处理完当前元素后,需要将栈顶的值弹出,以便在下一次迭代时堆栈状态保持一致。
lua_pop(L, 1); // 弹出栈顶的值
在 Lua 中,lua_pop
函数的参数是弹出的元素数量,而不是位置。
因此,lua_pop(L, 1)
的含义是弹出栈顶的一个元素,并不是从栈底位置 1
弹出元素。
2.3 传递表
lua_next
是 Lua C API 中用来遍历 Lua 表(table)的关键函数。以下是对 lua_next
的详细解释:
- 首先从栈顶弹出一个 key: 在每次调用
lua_next
前,需要将一个 key 压入栈中。首次调用时,传递nil
以获得第一个键值对。 - 从栈指定位置的 table 里取下一对 key-value:
lua_next
会根据栈顶的 key 找到下一个 key-value 对,将 key 和 value 压入栈中。如果没有更多的元素,返回 0 并且不在栈中压入任何值。 - 如果成功,返回非 0 值:
lua_next
成功找到下一个键值对时,返回 1;否则,返回 0。
获取表中内容
如果你需要获取表中的某个具体字段,可以使用 lua_getfield
函数:
void lua_getfield(lua_State *L, int index, const char *k);
参数说明
-
lua_State *L:
- 这是指向 Lua 状态的指针,表示当前的 Lua 环境。
-
int index:
- 表示栈中表的位置。这个索引可以是绝对索引(正数)或相对索引(负数)。
- 正数表示从栈底开始的索引。
- 负数表示从栈顶开始的索引,例如
-1
表示栈顶。
- 通常,如果你传递给 C 函数的表位于栈的第一个位置,则
index
为1
。
- 表示栈中表的位置。这个索引可以是绝对索引(正数)或相对索引(负数)。
-
const char *k:
- 表示字段的名称,即你要获取的表的键。
- 这是一个 C 字符串,表示键的名称。
2.4 参数类型检查
确保传递给C++函数的参数类型正确,可以使用 luaL_checktype
和 lua_type
进行类型检查:
lua_type
是一个基本的 Lua C API 函数,用于获取栈中某个索引处的值的类型。
函数原型
int lua_type(lua_State *L, int index);
参数:
- lua_State *L:指向 Lua 状态的指针。
- int index:栈中元素的索引,可以是正数(从栈底开始)或负数(从栈顶开始)。
返回值:
返回值是一个整数,对应于 Lua 类型常量,如 LUA_TNIL
, LUA_TNUMBER
, LUA_TBOOLEAN
, LUA_TSTRING
, LUA_TTABLE
等。
luaL_checktype
是 Lua 辅助库(luaL)的函数,用于检查栈中某个索引处的值的类型,并在类型不匹配时抛出错误。
void luaL_checktype(lua_State *L, int arg, int t);
参数:
- lua_State *L:指向 Lua 状态的指针。
- int arg:栈中元素的索引,可以是正数(从栈底开始)或负数(从栈顶开始)。
- int t:预期的类型常量,如
LUA_TNUMBER
,LUA_TSTRING
,LUA_TTABLE
等。
如果栈中指定索引处的元素类型不匹配,则 luaL_checktype
会抛出一个错误并终止当前的 C 函数执行。这对于确保函数参数类型正确非常有用。
3.获取返回值类型
C++ 函数可以返回各种类型的值给 Lua 脚本,包括数字、字符串、布尔值和结构体对象。Lua 脚本可以通过 Lua API 获取这些返回值并使用它们。
3.1 返回普通类型
3.2 返回表
四、C++调用Lua
1.C++给Lua传递变量并访问Lua的全局变量
- 访问Lua的全局变量
- 给Lua传递变量
可以使用 lua_pushstring
和 lua_setglobal
函数将 C++ 中的值设置为 Lua 的全局变量。
2.C++给Lua传递表并访问Lua的表
- C++ 调用 Lua 全局变量(表)
为了从 C++ 访问 Lua 的全局变量(包括表),可以使用 lua_getglobal
和 lua_getfield
函数。
- C++给Lua传递表
为了从 C++ 向 Lua 传递表,可以使用 lua_newtable
创建一个新的 Lua 表,并使用 lua_settable
将键值对设置到表中,然后使用 lua_setglobal
将表设置为 Lua 的全局变量。
3.C++调用Lua的函数
4.错误显示和堆栈清理
lua_pcall
是 Lua 提供的用于在保护模式下调用 Lua 函数的 API。保护模式的意思是,如果函数调用过程中发生错误,Lua 不会崩溃,而是返回一个错误码,并且错误信息会被压入栈顶。这样可以让 C++ 程序处理错误而不至于崩溃。
int lua_pcall(lua_State *L, int nargs, int nresults, int msgh);
参数说明:
lua_State *L
:指向 Lua 状态的指针。int nargs
:函数参数的个数。int nresults
:函数返回值的个数。int msgh
:错误处理函数在栈中的索引。设为 0,表示不使用错误处理函数。
返回值:
0
:函数调用成功。非 0
:函数调用失败,返回错误码,并且错误信息被压入栈顶。
5.C++调用Lua的函数并传递参数
6.C++调用Lua函数并获取返回的表
五、Lua和MFC结合开发
1.通过Lua配置窗口大小
2.通过Lua弹出提示窗口
3.通过Lua载入文件并在MFC中显示
- 获取打开文件的路径
- 实现Load按钮,弹窗显示path编辑框中的文件名
- 解析文件中的内容并显示在show编辑框中。