Home lua 调用C++动态库 lua如何调用dll 使用LUA调用DLL 在lua中调用DLL lua 5.2 实现luaL_register 功能 lua调用dll lua 调用 dll 的简单demo Lua调用C的动态库 windows下lua调用C动态库文件(.dll) Qt调用Lua动态链接库 Win32下 Qt与Lua交互使用:配置Qt下Lua运行环境MinGW Win32下 Qt与Lua交互使用(二):在Lua脚本中使用Qt类 Win32下 Qt与Lua交互使用(三):在Lua脚本中connect Qt 对象 Win32下 Qt与Lua交互使用(四):在Lua脚本中自由执行Qt类中的函数 开启Lua之旅 开启Lua之旅 开启Lua之旅
测试时需要将生成的dll与脚本、lua.exe、lua.dll等等放在同一个文件夹中。
调用DLL一般用于扩展功能,但是不利于前台脚本和后台程序变量交换,除非使用文件方式。
Lua 5.2及以上要使用 lua_newlib代替原来的luaL_Register生成动态库dll.
===================================================================
一.生成动态库dll:
1.去官网下载http://www.lua.org/versions.htm,下载lua5.1.5版本,lua5.2以后的有些函数时有改动的
2.下载后解压到一个文件夹中比如lua-5.1.5(本文安装的是lua5.1)
3.VS2015新建一个win32控制台程序,选动态库,空项目,如图

4.新建好工程后,把lua-5.1.5文件夹中的src文件夹拷贝进工程目录,如图所示

5.在解决方案里点击头文件,右键,添加->现有项,把src文件夹中的.h文件全部添加进来如图:

6.同理5步添加源文件,把src里的.c文件除了lua.c和luac.c两个文件不要添加进来,别的文件全部添加进来,如图

7.按Ctrl+Shift+A弹出对话框,分别添加MyDll.h和MyDll.cpp两个文件。分别添加代码
MyDll.h中添加:
#pragma once
extern "C" {
#include "../src/lua.h" //指定正确的路径
#include "../src/lualib.h"
#include "../src/lauxlib.h"
}
#ifdef LUA_EXPORTS
#define LUA_API __declspec(dllexport)
#else
#define LUA_API __declspec(dllimport)
#endif
extern "C" LUA_API int luaopen_MyDll(lua_State *L);//定义导出函数
MyDll.cpp中添加代码:
#include <stdio.h>
#include "MyDll.h"
static int averageFunc(lua_State *L)
{
int n = lua_gettop(L);
double sum = 0;
int i;
/* 循环求参数之和 */
for (i = 1; i <= n; i++)
sum += lua_tonumber(L, i);
lua_pushnumber(L, sum / n); //压入平均值
lua_pushnumber(L, sum); //压入和
return 2; //返回两个结果
}
static int sayHelloFunc(lua_State* L)
{
printf("hello world!");
return 0;
}
static const struct luaL_Reg myLib[] =
{
{ "average", averageFunc },
{ "sayHello", sayHelloFunc },
{ NULL, NULL } //数组中最后一对必须是{NULL, NULL},用来表示结束
};
int luaopen_MyDll(lua_State *L)
{
luaL_register(L, "ss", myLib);
return 1; // 把myLib表压入了栈中,所以就需要返回1
}
8.解决方案配置选择Release
9.按Ctrl+Shift+B键,生成解决方案,
如果是VS2015,应该会报错,大概如下
在解决方案下,选中此报错的文件,右键->属性->
添加_CRT_SECURE_NO_WARNINGS,就不会报错了。所有的文件都如此修改属性,直到正确生成dll文件为止
10.生成的dll如图所示:

11.随便在一个地方建个文件夹,把Mydll.dll拷贝进此文件夹中。在此文件夹中新建一个main.lua文件,输入代码:
require "MyDll"
local ave,sum = ss.average(1,2,3,4,5)--参数对应堆栈中的数据
print(ave,sum) -- 3 15
ss.sayHello() -- hello world!
11.命令行运行输入 lua main.lua,如图

===================================================================
如果读者朋友想详细的了解lua与C的通信原理,请移步这里【https://www.zhangshengrong.com/p/ERNnAzWa5W/】;
先说两句废话:怪不得lua不火,因为它对我们新手太不友好了。比如生成一个为lua调用dll的文件这么简单的小事,昨天我按照网上给出的教程总是出错(而且网上关于这方面的资料少的可怜!),最后还是通过自己的不断尝试修成正果,所以还是在这儿写博便记,下面进入正题。
首先是下载一个lua源码包,这里我选择的是lua5.1.5(本来我用的是lua-5.4,但是遇到了各种坑,编译的,运行的等等),朋友们可以去这里下载【https://mp.csdn.net/console/upDetailed】,解压缩之后,把它随便弄到一个地方。
其次,使用VS创建一个动态库项目,具体步骤,打开VS-》创建新项目-》选择window桌面向导,然后给项目命名(不要瞎起名,因为这决定您生成的dll名字),我这里给项目的命名是【MyLuaDll】,记住这个名字!
然后,点击创建,VS会弹出一个【window桌面向导】对话框,这里一定要把应用程序类型改成【动态链接库(.dll)】,最好把下面的【空项目】也勾选上,之后点击确定,项目就创建完成了,记住这个项目创建的路径。
接着,去到我们解压缩后的lua-5.1.5文件夹下的【src】文件夹下,复制里面所有的文件(注意,除了【lua.c】和【luac.c】,这两个文件不要复制!),然后来到我们刚才创建的【MyLuaDll】项目根目录下,把方才复制的所有文件直接粘贴进来。
再接着,打开VS,鼠标右键点击我们的项目,选择【添加】-》【现有项】,点击后弹出一个对话框,在对话框里找到我们的项目根目录,然后选择刚才粘贴进去的所有文件,如下图所示:

完成上面的步骤之后,我们还需要添加一个自定义的cpp文件,鼠标右键项目然后添加新文件,文件名随便取,我选择的是【abcd.cpp】,把它添加到项目里之后,就可以在它上面书写源代码了,我的源代码内容如下:
#include <iostream>
#include <windows.h>
extern "C"
{
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
extern "C" int func1(lua_State* luaEnv) // 这里可以这样写,也可以写成static
{
std::cout << "welcome to C world,first at all,allow me to sleep for 3 second." << std::endl;
Sleep(3000);
std::cout << "i am dll, now i wake up!" << std::endl;
return 0;
}
static int func2(lua_State* luaEnv)//在lua中,调用这个函数时,需要传入两个number类型值
{
auto op1 = luaL_checknumber(luaEnv, 1);//检查栈顶的元素是不是number类型,然后赋值给op1
auto op2 = luaL_checknumber(luaEnv, 2);
lua_pushnumber(luaEnv, op1+op2); //计算op1+op2的结果值,然后压入lua栈顶(返回给lua)
lua_pushstring(luaEnv, "i am dll of C,my work is done, goodbye!"); //再返回给lua一个字符串
return 2; //这句代码作用是告诉lua"本函数要返回给你几个东西",比如上面返回了一个数值和一个字符串,所以这里应该返回2
}
static luaL_Reg mylibs[] //我们需要弄一个表,而表中的内容就是我们需要导出的这个文件里的函数
=
{
{"aaa", func1}, //第一项可以随便取名,但要记住,因为在lua中调用这个函数时,用到的就是我们这里取的名字
{"bbb", func2},
{NULL , NULL} //最后不要忘了用null收尾
};
extern "C" __declspec(dllexport) //下面函数的作用是定义一个module名,以备在lua中require
int luaopen_MyLuaDll(lua_State* luaEnv) //请注意!这句代码中“luaopen_”后面的“MyLuaDll”,不能乱取名,一定要与您的dll名字一致!
{
/***很重要的一句代码,2参是字符串,内容一定要与上面的luaopen_后面的内容或者说我们的dll名字一致!最后的参数就是我们需要导出的表***/
luaL_register(luaEnv, "MyLuaDll", mylibs);
return 1;
}
代码写好之后,点击项目的生成按钮生成文件,不出意外的话VS会报错,大概如下图:

没关系,我们慢慢来【不用设置安全检查为否啥的,因为我试了,在我机器上没用,还是报错】,那么如何解决上面的错误呢,方法很简单,首先双击错误提示最上面的那句话,然后VS会打开对应的文件,在该文件的最上面添加一句代码【#pragma warning(disable : 4996)】,如下图:

完成之后,重新生成一下,不出意外还会报错(但报错的数量比上次要少几个),没关系,重复上一次的步骤:双击某行报错提示,进入对应文件,在文件代码的上面添加【#pragma warning(disable:4996)】,然后再重新生成一下,再报错,再添加,如此重复,报错会不断减少,一直到最后,总有一刻我们会真的生成了dll文件【应该是在项目的debug目录下】
然后,让我们随便在某个地方创建一个lua文件,我这里是在桌面上创建的【test.lua】,内容如下:
local obj = require("MyLuaDll")
print("welcome to lua!")
obj.aaa();
print("now come back lua")
local n, s = obj.bbb(33,66)
print('now in lua,this is last words of C dll: "'..s..'"')
print("dll return value is:",n)
最后,我们去我们刚才的项目里的debug文件夹下,找出我们生成的【MyLuaDll.dll】文件,复制它,然后回到刚才创建的lua文件的同目录下,把【MyLuaDll.dll】文件拷贝进来,运行lua文件,就可以看到我们dll工作的样子的,我的如下图:

===================================================================
使用Lua调用DLL
之前希望学点lua来自定义ocgcore,然而官方的脚本机器都是C语言写的,要懂点Lua调用C/C++库的知识。所以看了下在线版的Programming in Lua,下了个5.4测试版的源代码自己编译了一下。
生成Lua编译器
貌似官网只说了*nix的几个发行版上的编译,对Windows上面不太友好(好吧是提供了二进制文件的下载方法)。我早些时候装了个MinGW-win64,所以用mingw32-make mingw(反正是类似的命令)就可以了。不知道为什么,命令后面跟install的话,总是报告找不到文件。我都不知道install是make自带的还是什么东西。反正光生成二进制文件就没问题,所以我就把几个文件从\src拷贝出来开始用了。
不过没想到在5.2版本后语言不支持lua_register之类的一批函数,原书中直接调用loadlib也不支持了(我试了一下package.loadlib好像是可以的)。
C++生成dll
先按照书里写了个C++文件打算编译成动态链接库(.dll)文件,无奈VS技术重回低谷,总是报连接错误,最后简单粗暴地把Lua所有源代码文件都放进项目里了(大概正确做法是把编译生成的lua54.dll放进去?)。
代码都是抄的,最后找到了正确的示例,感谢。
// test.cpp
extern "C" {
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
}
#include <math.h>
//这个extern也许可以改成static吧,我也不清楚
extern "C" int l_sin(lua_State* L) {
double d = luaL_checknumber(L, 1);
lua_pushnumber(L, sin(d));
return 1; /* number of results */
}
//之前把下面变量命名为mylib,结果在Lua里总是说找不到module
//调用require "mylib"是错的,也许调用require "test"就行了?
//因为require不行,还用过load、loadfile、package.loadlib一堆函数
static const struct luaL_Reg test[] = {
{"sin", l_sin},
{NULL, NULL} /* sentinel */
};
//这个函数当时也命名为luaopen_mylib的
extern "C" _declspec(dllexport) int luaopen_test(lua_State* L) {
lua_newtable(L);
luaL_setfuncs(L, test, 0);
return 1;
}
然后把生成的动态链接库放到根目录下,就可以耍Lua了。
Lua交互界面
下面用了>.\bin\lua.exe,进入lua的REPL(交互模式),所以没有用local来修饰变量(不知道为什么,交互模式里好像不支持用local生成局部变量,之前用local声明的变量都是nil)。这时候已经可以调用函数了。在正确运行前有好几屏幕的错误示范:P
这个代码片功能!居然不能设置为Lua。还是设成python模式吧。
> test = require "test"
> sin = test.sin
> sin(1.57)
> 0.99999968293183
最后吐槽一下REPL一定要通过Ctrl+C来退出,为什么不给个quit()函数呢?如果有的话麻烦告诉我一声。
===================================================================
整整的花了一个下午的时候,才在lua中调用了动态链接库。比起其他脚本语言,lua的调用方式算是比较繁琐的,但是lua的编程思想非常的统一,即lua和C之间的传值都是通过栈进行的,目前为止我还没有深入的去了解这个“栈”,但是这并不妨碍去使用”栈“。
当我们在一个空的工程中写下几个C/C++的函数,然后把属性类型设置为“动态链接库”以后,编译虽然成功了,DLL也产生了,但是我们得到的DLL是一个没有价值的数据文件,它缺少export函数(通过dumpbin /exports xxx.dll 观察),我们的lua也无法使用它。为了把我们写好的函数export出去,必须在函数名前,返回类型之后加上__declspec(dllexport),比如:
int add(int a, int b)
{
return a + b;
}
//要修改为--->>>
int __declspec(dllexport) add(int a , int b)
{
return a + b;
}
这是你就可以用dumpbin看到我们的add了。不对??是的,也行你看到的不是add,而是@ILT#$%^add(@$%) (我随便写个大概),那么你的工程一定是C++工程。这样的DLL只能通过引导库(lib)来实现,LUA用不了。为了能建立lua可以使用的库,必须在导出函数的最前面加上extern "C" (不能是__stdcall),所以此时的函数变成了:
extern "C" int __declspec(dllexport) add(int a , int b)
{
return a + b;
}
还有一种常用的方法也可以得到extern的效果,那就是建立def文件,例如:
LIBRARY mylib
DESCRIPTION "Just for test"
VERSION 1.0
EXPORTS
add
有了def,你就不需要再每个函数前加上extern “C”了。记得要在属性/连接器/输入/模块定义文件中输入def的文件名称。我们这个动态链接库以及lua.exe必须动态的连接lua.dll (lua5.2.dll),而不能静态的链接,否则你会得到错误:
stack traceback :
[C]:?
[C]:in function 'require'
test.lua:1:in main chunk
[C]:?
有了上面的知识铺垫,我们可以进入下一个环节了——让lua调用DLL。下面代码可以生成一个DLL。
#include <Windows.h>
extern "C"{
#include <lua.h>
#include <lauxlib.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <lualib.h>
#include <math.h>
}
static int mysin (lua_State *L)
{
double d = luaL_checknumber(L, 1);
lua_pushnumber(L, sin(d));
return 1;
}
static int l_printf(lua_State *L)
{
const char * pPattern = luaL_checkstring(L, 1);
const char * str = luaL_checkstring(L, 2);
lua_pushnumber(L, printf(pPattern, str));
return 1;
}
static int l_MessageBox(lua_State *L)
{
const char * sTitle = luaL_checkstring(L, 1);
const char * sText = luaL_optstring(L, 2, "");
MessageBox(NULL, sTitle, sText, 0);
return 1;
}
static const struct luaL_Reg mylib[] =
{
{"mysin", mysin},
{"printf", l_printf},
{"messagebox", l_MessageBox},
{NULL, NULL}
};
extern "C" int __declspec(dllexport) luaopen_mylib(lua_State *L)
{
luaL_newlib(L, mylib);
return 1;
}
上面的代码中,最后export出去的函数是luaopen_mylib,一定要注意:函数名luaopen_mylib表明了最后输出的dll名称必须为mylib.dll,而且大小写要一致,否则lua无论如何都找不到luaopen_mylib函数的。我们可以在lua脚本中这样使用。
mylib = require("mylib")
mylib.printf("Say: %s\n", "hello world")
msgbox = mylib.messagebox
msgbox("hello from luaopen_mylib")
另外,我要介绍一下另一种老方法。在DLL代码中添加:
extern "C" int __declspec(dllexport) MyMessageBox(lua_State *L)
{
const char * sText = luaL_checkstring(L, 1);
const char * sTitle = luaL_checkstring(L, 2);
return MessageBox(NULL, sText, sTitle, 0);;
}
你猜对了,我们要export另一个函数,然后我们可以在lua中这样用到:
pf = package.loadlib("mylib.dll","MyMessageBox")
print(pf)
pf("hello", "again")
这两种方法的效果是一致的,但是第一种方式是最新的,后面一种似乎有点out of date了。
————————————————
版权声明:本文为CSDN博主「语言观止」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/snlscript/article/details/16340653
===================================================================
lua 5.2 去掉了luaL_register功能,不能直接注册函数到一个全局table中了。
网上找了很多文章都是要导出到库,再引用库。
// my_math.c
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
#include <math.h>
#include <stdio.h>
static int my_math_sin (lua_State *L) {
lua_pushnumber(L, sin(luaL_checknumber(L, 1)));
return 1;
}
static int my_math_cos (lua_State *L) {
lua_pushnumber(L, cos(luaL_checknumber(L, 1)));
return 1;
}
static const luaL_Reg mathlib[] = {
{"my_cos", my_math_cos},
{"my_sin", my_math_sin},
{NULL, NULL}
};
/*
** Open my_math library
*/
LUALIB_API int luaopen_my_math (lua_State *L) {
luaL_newlib(L, mathlib);
return 1;
}
LUALIB_API 是导出导入函数或extern,看是否指定 LUA_BUILD_AS_DLL
// callc.lua
my_math = require("my_math") --my_math 是一个dll或so,由下面的.c生成
print(my_math.my_sin(3.14))
print(my_math.my_cos(3.14))
如果要实现luaL_register,可以不导出dll,改用luaL_requiref在c里面注册这个load方法
luaL_requiref(L,"my_math",luaopen_my_math,0);这样就不用导出dll了,直接在exe里就行了
===================================================================
首先先看下lua调用的dll的编写,新建一个新的 Win32 控制台应用程序 ,然后选择dll模板,记得将预编译头去掉(如下图)
// mydall_test.cpp : 定义 DLL 应用程序的导出函数。//
#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <lua.hpp>
#include <lauxlib.h>
#include <lualib.h>
extern "C" int average(lua_State* L) // 导出平均函数接口
{
double sum = 0;
int num = lua_gettop(L);
for (int i = 1; i <= num; i++)
{
sum += lua_tonumber(L, i);
}
lua_pushnumber(L, sum / num);
return 1;
}
static luaL_Reg cMethods[] = {
{"average", average},
{NULL, NULL}
};
extern "C" __declspec(dllexport)
int luaopen_mydll_test(lua_State* L){ // luaopen_xxxx 这里的 xxxx 一定要是你的项目名
luaL_newlib(L, cMethods);
return 1;
}
```
// mydall_test.cpp : 定义 DLL 应用程序的导出函数。//
#include "stdafx.h"
#include <stdio.h>
#include <string.h>
#include <lua.hpp>
#include <lauxlib.h>
#include <lualib.h>
extern "C" int average(lua_State* L) // 导出平均函数接口
{
double sum = 0;
int num = lua_gettop(L);
for (int i = 1; i <= num; i++)
{
sum += lua_tonumber(L, i);
}
lua_pushnumber(L, sum / num);
return 1;
}
static luaL_Reg cMethods[] = {
{"average", average},
{NULL, NULL}
};
extern "C" __declspec(dllexport)
int luaopen_mydll_test(lua_State* L){ // luaopen_xxxx 这里的 xxxx 一定要是你的项目名
luaL_newlib(L, cMethods);
return 1;
}
```
以下是lua文件调用代码,将上面生成的dll文件放在lua找得到的地方(CPATH,PATH 配置),也可以直接把它放在lua文件相同的文件夹里。
local mydll = require "mydll_test" -- mydll_test对应C++文件中的 luaopen_xxxx 文件名
print(mydll.average(1.2,2.5,1.5))
以上代码简简单单,一切都顺利,在生成dll的时候,配置lua的动态链接库时,如果直接使用官方下载的文件,最后执行lua文件会出现以下错误:multiple Lua VMs detected
这是因为官方的lua.dll在生成的时候没有指定 LUA_BUILD_AS_DLL 来生成,解决方法是,直接下载一个lua源文件,

然后自己使用以下的bat代码进行编译:
@echo off
md bin
md lib
md include
cd src
cl /c /nologo /W3 /O2 /Ob1 /Oi /Gs /MD /D_CRT_SECURE_NO_DEPRECATE l*.c
ren lua.obj lua.o
ren luac.obj luac.o
lib /OUT:lua53.lib *.obj
copy lua53.lib ..\lib\lua53.lib
del *.o *.obj *.exp *.lib *.dll *.exe
cl /c /nologo /W3 /O2 /Ob1 /Oi /Gs /MD /D_CRT_SECURE_NO_DEPRECATE /DLUA_BUILD_AS_DLL l*.c
ren lua.obj lua.o
ren luac.obj luac.o
link /DLL /IMPLIB:lua53.lib /OUT:lua53.dll *.obj
link /OUT:lua53.exe lua.o lua53.lib
link /OUT:luac53.exe luac.o *.obj
copy lua53.exp ..\bin\lua53.exp
copy lua53.lib ..\bin\lua53.lib
copy lua53.dll ..\bin\lua53.dll
copy lua53.exe ..\bin\lua53.exe
copy luac53.exe ..\bin\luac53.exe
del *.o *.obj *.exp *.lib *.dll *.exe
copy lauxlib.h ..\include\lauxlib.h
copy lua.h ..\include\lua.h
copy lua.hpp ..\include\lua.hpp
copy luaconf.h ..\include\luaconf.h
copy lualib.h ..\include\lualib.h
cd ..\
将上面的bat代码保存为lua_t.bat,并放在与Makefile 文件同层,并点开

进入到bat 所在文件并执行lua_t.bat,最终会编译成功如下图:

接下来便是设置编译需要的链接库,右键项目点开属性,具体配置如下:

这里最重要的是设置 LUA_BUILD_AS_DLL


最后生成dll:

此时便可以正常执行lua调用dll。
===================================================================
windows下的 lua 与 c 进行交互, 本人新手,只能做到这么多了
c代码
//#include "stdio.h"
#include "windows.h"
#ifdef _cplusplus
extern "C"{
#endif
#include "lua.h"
#include "lauxlib.h"
#include "lualib.h"
extern int isquare(lua_State *L);
extern int alert(lua_State *L);
#ifdef _cplusplus
}
#endif
int luaopen_add(lua_State *L){
lua_register(
L, /* Lua 状态机 */
"square", /*Lua中的函数名 */
isquare /*当前文件中的函数名 */
);
lua_register(L,"alert",alert);
// lua_register(L,"cube",icube);
return 0;
}
int alert(lua_State *L)
{
const char * desc = lua_tostring(L,-1);
MessageBox(NULL,desc,"alert",MB_OK);
return 1;
}
int isquare(lua_State *L){ /* C中的函数名 */
float rtrn = lua_tonumber(L, -1); /* 从Lua虚拟机里取出一个变量,这个变量是number类型的 */
//printf("Top of square(), nbr=%f\n",rtrn);
lua_pushnumber(L,rtrn*rtrn); /* 将返回值压回Lua虚拟机的栈中 */
return 1; /* 这个返回值告诉lua虚拟机,我们往栈里放入了多少个返回值 */
}
编译命令
cl /c add.c /I ../include
include 为 lua 头文件所在目录
link /def:export.def /dll add.obj ../lua53.lib "kernel32.lib" "user32.lib" "gdi32.lib"
导出函数到 dll
export.def
; export.def
LIBRARY ADD
; MY_DLLMAIN 将成为生成的dll的名称
DESCRIPTION 'test'
EXPORTS
isquare @ 1
alert @ 2
; 这个名称即为函数的实际导出名称 @1为函数的导出编号
lua
square = package.loadlib("ADD.dll", "isquare")
alert=package.loadlib("ADD.dll", "alert")
print(alert)
print ( square(2) )
alert("222")
执行结果
虽然有乱码存在问题,但是期望结果基本已经达到
===================================================================
项目背景:
因为lua是单线程的,每个lua执行就相当于是一个线程,因此是没有办法完成调用两个C线程的,不能满足当前项目需求,因此不使用lua脚本的方式进行CDLL的线程调用管理了,而使用python了。
1、 lua基础
https://www.runoob.com/lua/lua-tutorial.html
--两个点表示连接
print("两个点表示连接")
a = "1212" .. "nihao"
print(a)
--测试function及for和if语法
print("测试function及for和if语法")
local function maxs(a)
local maxi = 1
local maxnum = a[maxi]
for i, val in ipairs(a) do
if val > maxnum then
maxi = i
maxnum = val
end
end
return maxi, maxnum
end
print(maxs({8,10,23,12,5,0}))
--table的concat测试
local fruits = {"banana", "orange", "apple"}
print("连接后的字符串", table.concat(fruits))
print("指定连接字符后的字符串", table.concat(fruits, ", "))
print("指定索引来连接table的字符串", table.concat(fruits, ", ", 2, 3))
--测试函数传参可变参数
print("测试函数传参可变参数")
function add(...)
local s = 0
local arg = {...}
for i, v in ipairs{...} do --> {...} 表示一个由所有变长参数构成的数组
s = s+v
end
print("总共传入 " .. select("#",...) .. " 个数")
print("传入" .. #arg .. "数字")
return s
end
print(add(1,2,3,4,5,6,7,0))
--获取参数总数
function foo(...)
for i = 1, select('#', ...) do
local arg = select(i, ...) --> select(n,…) 可以访问第 n 个到数组最后的参数
print("arg", arg)
end
end
print(foo(1,2,3,4))
--运算符.. #
a = "Hello "
b = "World"
print("连接字符串 a 和 b ", a..b )
print("b 字符串长度 ",#b )
--测试模块
《module.lua文件》
module = {}
module.constant = "这是一个变量"
function module.func1()
io.write("这是一个公有函数!\n")
end
local function func2()
print("这是一个私有函数")
end
function module.func3()
func2()
end
return module
《main.lua文件》
local m = require("module") --> 别名
m.func1();
m.func3();
print(m.constant)
--测试元表的__index
print("测试元表的__index")
mytable = setmetatable({key1 = "value1"}, {__index = {key2 = "metatablevalue"}})
print(mytable.key1, mytable.key2)
--测试元表的相加
function table_maxn(t)
local mn = 0
for k, v in pairs(t) do
if mn < k then
mn = k
end
end
return mn
end
mytable = setmetatable({1, 2, 3}, {
__add = function(mytable, newtable)
for i = 1, table_maxn(newtable) do
table.insert(mytable, table_maxn(mytable)+1, newtable[i])
end
return mytable
end
})
secondtable = {4, 5, 6}
mytable = mytable + secondtable
for k, v in ipairs(mytable) do
print(k, v)
end
2、 C调用lua,执行lua脚本
复制lua源码到工程中或者引入lua的lib文件及头文件也可。

#include <windows.h>
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
}
int main()
{
lua_State* l = luaL_newstate();
luaL_openlibs(l);
luaL_dofile(l, "main.lua");
lua_close(l);
system("pause"); //为了保证在windows资源管理器中打开main.exe不闪退。需要windows.h。
return 0;
}
3、lua调用C库

c工程
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
};
#include <thread>
#include <iostream>
using namespace std;
int number = 0;
int number2 = 0;
int flag = 0;
std::thread *t1 = NULL;
std::thread *t2 = NULL;
void ThreadProc1()
{
flag = 1;
while (1)
{
cout << "thread 1 :" << number << endl;
++number;
this_thread::sleep_for(std::chrono::milliseconds(10));
}
return ;
}
void ThreadProc2()
{
while (number2 < 100)
{
cout << "thread 2 :" << number2 << endl;
++number2;
this_thread::sleep_for(std::chrono::milliseconds(10));
}
return ;
}
extern "C" int ShowMsg(lua_State* luaEnv) {
cout << "Hello world from clibs!" << endl;
t1 = new std::thread(ThreadProc1);
return 1; // 返回值个数为0个.
}
extern "C" int ShowMsg2(lua_State* luaEnv) {
cout << "Hello world from clibs222!" << endl;
t2 = new std::thread(ThreadProc2);
return 0; // 返回值个数为0个.
}
// part one: 要导出的函数列表
static luaL_Reg luaLibs[] = {
{ "ShowMsg", ShowMsg },
{ "ShowMsg2", ShowMsg2 },
{ NULL, NULL }
};
// part two: DLL入口函数,Lua调用此DLL的入口函数.
//testDll是modole名, 将来require这个名字并且这个名称要与dll名称一致,并且不能与lua名称一致
extern "C" __declspec(dllexport)
int luaopen_testDll(lua_State* luaEnv) {
const char* const LIBRARY_NAME = "WinFeature"; //这里也写WinFeature
luaL_register(luaEnv, LIBRARY_NAME, luaLibs);//注册要导出的函数列表
return 1;
}
lua文件
--加载test库
WinFeature1 = require("testDll")
--定义协程
co = coroutine.create(
function()
WinFeature1.ShowMsg()
end
)
--激活协程
coroutine.resume(co)
--打印协程状态
print(coroutine.status(co))
4、lua无法调起C库中两个线程,以及lua端卡死问题或者lua端协程结束C也跟着结束

lua函数
--加载test库
WinFeature1 = require("testDll")
function Sleep(n)
local t0 = os.clock()
while os.clock() - t0 <= n do end
end
--定义协程
co = coroutine.create(
function()
WinFeature1.ShowMsg()
while true do
print("coroutine")
Sleep(0.1)
end
end
)
--激活协程
coroutine.resume(co)
--打印协程状态
print(coroutine.status(co))
参考博客:
https://blog.csdn.net/qq_35807952/article/details/79912769 Lua与C++交互初探之Lua调用C++
https://www.runoob.com/lua/lua-tutorial.html Lua 教程
https://www.cnblogs.com/chechen/p/11415104.html VS2015 创建C++动态库及使用
https://blog.51cto.com/u_1433795/1310702 lua 调用C++ DLL函数简单实例
https://www.kancloud.cn/thinkphp/lua-guide/43809 绑定Lua和C/C++的库
检测动态库中导出的函数列表
dumpbin /exports testDll.dll

===================================================================
置顶,使用git bash + mingw32方式解决lua调用C库问题(推荐)
lua-5.3.6为存放源码的文件夹
cd lua-5.3.6/src
mingw32-make.exe mingw
cp lua53.dll ../../
cp lua.exe ../../
cd ../../
gcc -shared -L. -llua53 myLuaCLib.c -o myLuaCLib.dll
./lua.exe test.lua
lua代码如下:
local clib = require "myLuaCLib"
clib.test()
C库代码如下:
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include <stdio.h>
static int l_test(lua_State* L)
{
printf("hello world\r\n");
return 0;
}
static const struct luaL_Reg myLuaCLib[] = {
{ "test", l_test },
{ NULL , NULL },
};
__declspec(dllexport) int luaopen_myLuaCLib(lua_State* L)
{
luaL_newlib(L, myLuaCLib);
return 1;
}

感谢大神指点!!!
一下方法过于冗余,建议用上述方法解决
1.lua调用C库时,找不到指定程序问题.
lua代码如下:
local clib = require "myLuaCLib"
clib.test()
C库代码如下:
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include <stdio.h>
static int l_test(lua_State* L)
{
printf("hello world\r\n");
return 0;
}
static const struct luaL_Reg myLuaCLib[] = {
{ "test", l_test },
{ NULL , NULL },
};
int luaopen_myLuaCLib(lua_State* L)
{
luaL_newlib(L, myLuaCLib);
return 1;
}
bash显示如下:
error loading module 'myLuaCLib' from file '.\myLuaCLib.dll':
找不到指定的程序。
stack traceback:
[C]: in ?
[C]: in function 'require'
test.lua:1: in main chunk
[C]: in ?
问题原因:
主要原因是在生成C库.dll文件时忘记在需要导出的函数前加入__declspec(dllexport),修改后接口函数代码如下:
__declspec(dllexport) int luaopen_myLuaCLib(lua_State* L)
{
luaL_newlib(L, myLuaCLib);
return 1;
}
注:在lua中使用requir(“xxx_c_dll”)函数去调用一个C的动态链接库时,lua函数内部会默认去寻找并调用luaopen_xxx_c_dll函数,所以在此仅提供C库的入口函数就可以了.
2. lua调用C库时,multiple Lua VMs detected问题
./lua53.exe test.lua
lua53.exe: multiple Lua VMs detected
stack traceback:
[C]: in ?
[C]: in function 'require'
test.lua:1: in main chunk
[C]: in ?
如果按照①中所述解决了"指定程序找不到"的问题后出现"multiple Lua VMs detected",说明我们编写的C库已被lua虚拟机识别,但由于lua虚拟机使用的动态链接库文件(注:此处指的是lua虚拟机本身所调用的lua源码的dll文件)和我们编译C库时所使用的lua源码的dll文件不同,故导致上述问题的发生.所以需要重新编译整个工程(包含重新编译生成lua虚拟机,C库),使其指向相同的lua源码的dll文件即可.
1.生成lua源码的.dll和.lib文件
具体步骤:
1.找一份新鲜的lua源码(我这边使用的是lua-5.3.6),利用VS建立DLL动态链接库工程,将源码全部加进解决方案(除了lua.c luac.c lua.hpp makefile)
2.在解决方案然的配置属性,C/C+±>预处理器->预处理器定义编辑加入:LUA_BUILD_AS_DLL
3.点击编译,即可在"Debug/Release"文件夹下生成.dll和.lib文件,若没有生成.lib文件就是第二步,预处理器定义编辑有问题
2.生成lua控制台程序
具体步骤:
1.重新建立一个工程,仅把lua.c加入
2.配置解决方案
C/C++ ->常规->附加包含目录:在此添加lua源码头文件路径
链接器->常规->附加库目录:在此添加lua源码的.lib文件路径
链接器->输入->附加依赖项:在此添加lua源码的.lib文件名
将lua源码的.dll文件加在可执行文件目录中
3.点击编译即可生成lua控制台程序
3.生成C库的.dll和.lib文件
1.重新建立一个工程,仅把C库文件加入
2.配置解决方案
C/C++ ->常规->附加包含目录:在此添加源码头文件路径
链接器->常规->附加库目录:在此添加lua源码的.lib文件路径
链接器->输入->附加依赖项:在此添加lua源码的.lib文件名
将lua源码的.dll文件加在可执行文件目录中
3.点击编译即可生成C库的.dll和.lib文件
注:此处的生成文件代码细节参考–lua调用C库时,找不到指定程序问题.不在此赘述
最后,各个工程生成的必要文件如下所示
最后将上述文件放在同一个目录,使用bash输入一下命令:
./xxx_vm.exe xxx.lua
hello world
限于本人水平,若有纰漏,欢迎指正!
===================================================================
Qt调用Lua动态链接库
上一篇笔记讲解了如何在VS2017下使用C++调用Lua动态链接库,因笔者平时也喜欢使用Qt,故创作本篇记录Qt是如何调用Lua Dll协助我们工作的
新建Qt工程
将Lua5.1源码目录“src”拷贝至Qt工程目录下,如下图所示
在Qt中打开LearnLua.pro文件,将“INCLUDEPATH += ./src”添加到任意位置,至此头文件路径已经配置OK了
在Qt工程main函数中插入如下代码
lua_State *L = lua_open();
luaopen_base(L);
luaopen_table(L);
luaopen_string(L);
if (luaL_loadfile(L, "Print.lua"))
{
//std::string error = "cannot run Lua file: " + filename; //throw Exception(error.c_str()); //LOGDEBUG(error.c_str()); //cout<
新建Print.lua脚本,将其置于exe所在目录的上级目录下
以下代码为Print.lua脚本中的内容
--[[
File name: Print.lua
Description: lua编程Demo1:熟悉lua的基本语法,如变量声明,C语言中if else等语句
在lua中的实现
Function List:
Author: Hanc
Version: V0.0
Other:
1. lua中没有i++这类用法
2. while中不能像C一样使用continue功能
3. 除了nil和false都为真
4. 拼接字符串使用 ..
5. C语言中的“&&”“||”“!”在lua中为 and or not
6. lua中 != 要用 ~=
7. 与C/C++不同,lua中的下标是从1开始的
History:
2019.1.5 创建
]]--
print("hello Lua !!")
--[[
a = 10 声明全局变量
local a = 10 声明局部变量
lua中尽量使用局部变量,保证垃圾回收及时
全局变量赋值为“nil”才会交给垃圾回收
--]]
a = 10
--打印变量的数据类型
print(type(a))
-- 将字符串转换为数字
local c = tonumber("256")
print(c)
--字符串连接使用“..”
local d = "hello baba".."\two shi\n"
--local e = string.len(d)
--print(string.len("we"))
--if else 用法
if(1==1 and 1==2) then
print("1==1")
else
print("1 != 1")
end
-- if elseif 用法
if(1==1 and 1==2) then
print("1==1")
elseif(2==2) then
print("2 == 2")
end
--break退出,未实现continue
while(nil) do
print("while")
end
--lua中的do while语句
local i = 100;
repeat
i = i+1
print("i = "..i)
until i>110
for var =1,5,1 do
print("var = "..var)
end
--pairs遍历所有
--ipairs 按照标准数组去访问
local days = {"Sun","Mon","Tus"}
for i,v in ipairs(days) do
print(i..":"..v)
end
===================================================================
Lua与C++之间可以实现非常强的交互性。Lua中可以使用C++中的函数,C++中也可以使用Lua中的函数。由此可以引发出很多奇思妙想了。
简单来说,Lua动态的特性补充了C++的功能。当然,也看你具体怎么用。
笔者经常使用Qt,故准备将Qt和Lua结合在一起。想象一下在Lua脚本中调用如下代码:
w = QWidget:new() w:show()
然后弹出一QWidget的窗口,是多么感人的一件事情呀。
好,下面开始配置环境吧。
1. 下载Lua 5.1。目前的最新版本是5.2,但是因为使用到的toLua不支持Lua 5.2,故仍然使用5.1版。
在http://www.lua.org/versions.html#5.1可以找到5.1版的Lua,下载下来,解压待用。
2. 下载toLua。笔者找到的是目前最新的toLua 0.95,下载下来后,解压。
将include/toLua++.h,src/lib中*.c/*.h文件,src/bin/toluabind.c,共8个文件复制到Lua解压目录/src目录下,仅复制文件。
3. 编译。在Lua解压目录下新建文件夹bin,新建一build.bat文件,输入以下代码:
@setlocal @if "%1" == "mingw" goto :build_mgw @if "%1" == "vs" goto :build_vs @if "%1" == "dist" goto :install @if "%1" == "" goto :build_mgw @echo usage: %1 [mingw|vs|dist] @goto :EOF :build_mgw @set srcdir=..\src\ @set CFLAGS=-s -O3 -Wall -I. "-DLUA_USER_H=<luacpath.h>" @echo build lua52.dll ... gcc %CFLAGS% -DLUA_BUILD_AS_DLL -c %srcdir%*.c @del lua.o luac.o gcc -s -mdll -o lua52.dll -Wl,--out-implib,liblua52.dll.a *.o @echo build lua.exe ... gcc %CFLAGS% -o lua.exe %srcdir%lua.c -L. -llua52 @echo build luac.exe ... gcc %CFLAGS% -c %srcdir%*.c @del lua.o luac.o ar rcs liblua52.a *.o gcc %CFLAGS% -o luac.exe %srcdir%luac.c -L. -static -llua52 @del *.o 2>nul goto :dist :build_vs @set srcdir=..\src\ @set MYCOMPILE=/nologo /MD /O2 /W3 /c /D_CRT_SECURE_NO_DEPRECATE "/DLUA_USER_H=<luacpath.h>" @set MYLINK=link /nologo @set MYLIB=lib /nologo @set MYMT=mt /nologo @echo build lua52.dll ... %MYCOMPILE% /DLUA_BUILD_AS_DLL %srcdir%*.c @del lua.obj luac.obj %MYLINK% /DLL /out:lua52.dll *.obj if exist lua52.dll.manifest^ %MYMT% -manifest lua52.dll.manifest -outputresource:lua52.dll;2 %MYCOMPILE% /DLUA_BUILD_AS_DLL %srcdir%lua.c %MYLINK% /out:lua.exe lua.obj lua52.lib if exist lua.exe.manifest^ %MYMT% -manifest lua.exe.manifest -outputresource:lua.exe %MYCOMPILE% %srcdir%*.c del lua.obj luac.obj %MYLIB% /out:lua52s.lib *.obj %MYCOMPILE% %srcdir%luac.c %MYLINK% /out:luac.exe luac.obj lua52s.lib if exist luac.exe.manifest^ %MYMT% -manifest luac.exe.manifest -outputresource:luac.exe @del *.exp *.obj *.manifest goto :dist :dist @echo install ... @set dstdir=.\Lua52\ mkdir %dstdir% 2>nul mkdir %dstdir%clibs 2>nul mkdir %dstdir%include 2>nul mkdir %dstdir%lib 2>nul mkdir %dstdir%lua 2>nul copy /y *.dll %dstdir% >nul copy /y *.exe %dstdir% >nul copy /y *.a %dstdir%lib >nul copy /y %srcdir%lua*.h* %dstdir%include >nul copy /y %srcdir%laux*.h %dstdir%include >nul copy /y %dstdir%lua.exe %dstdir%lua52.exe >nul copy /y %dstdir%luac.exe %dstdir%lua52c.exe >nul :EOF echo finish
这是一位Lua爱好者为编译Lua 5.2编写的脚步,在这里可以直接使用。
注意建议使用MinGW编译,VS会导致一些问题。没有安装MinGW的可以百度一下,配置好MinGW的环境,并将bin目录加入环境变量path中。
4. 运行编译脚步。建议先打开CMD窗口,在CMD窗口中进入该目录,执行build.bat。这样报错的话可以看到一些信息。
可能会有Warnning,不过还是可以顺利通过编译的。在生成的Lua52文件夹中,就有我们想要的东西。
确保Lua52/lib目录下,生成了liblua52.a文件。
5. 测试使用。使用MinGW编译,建议使用MinGW版的Qt。
在Qt Creator中新建一个工程。删除到只剩下mian.cpp即可。
复制Lua52/include下所有文件,以及之前复制的toLua++.h,到Qt工程目录下。在Qt Creator中加入这些新文件。
复制Lua52/lib/liblua52.a文件到Qt工程目录下。在Qt工程的*.pro文件中加入
LIBS+=../LuaTest/liblua52.a
笔者的工程名为LuaTest,具体的目录名按照你的工程名以及你的build文件夹地址修改。如果提示找不到文件,就该为绝对路径。
在main.cpp文件中,加入
extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
#include "tolua++.h"
}
将main函数改为:
lua_State *lu = luaL_newstate();
luaL_openlibs(lu);
//执行内存脚本
string str = "print (\"Hello world!\")";
luaL_loadbuffer(lu, str.c_str(), str.length(), "line");
lua_pcall(lu, 0, 0, 0);
顺利运行则配置成功。(在.pro文件中加入CONFIG+=console,在项目-〉运行中勾选Run in terminal,可以打开控制台)
那么如何做到在Lua中新建Qt的窗口呢?下篇博客继续探讨。
如有问题,可以留言。发现错误,欢迎指正。
===================================================================
话接上篇。成功配置好Qt+Lua+toLua后,我们可以实现在Lua脚本中使用各个Qt的类。直接看代码吧。
#include "include/lua.hpp"
#include <QWidget>
#include <QApplication>
#include <QFile>
#include <QDebug>
static int tolua_new_QWidget(lua_State* pState)
{
QWidget* widget = new QWidget();
tolua_pushusertype(pState, widget, "QWidget");
return 1;
}
static int tolua_delete_QWidget(lua_State* pState)
{
QWidget* widget = (QWidget* )tolua_tousertype(pState, 1, 0);
if(NULL != widget)
{
widget->close();
delete widget;
}
return 1;
}
static int tolua_Show_QWidget(lua_State* pState)
{
QWidget* widget = (QWidget* )tolua_tousertype(pState, 1, 0);
if(widget != NULL)
{
widget->show();
}
return 1;
}
static int tolua_Resize_QWidget(lua_State* pState)
{
QWidget* widget = (QWidget* )tolua_tousertype(pState, 1, 0);
double a = tolua_tonumber(pState, 2, 0);
double b = tolua_tonumber(pState, 3, 0);
qDebug() << a;
qDebug() << b;
if(widget)
{
widget->resize((int)a, (int)b);
}
return 1;
}
int main(int argc, char * argv[])
{
Q_INIT_RESOURCE(resources);
QApplication a(argc, argv);
// 初始化Lua对象
lua_State *lu = luaL_newstate();
luaL_openlibs(lu);
// 使用tolua对QWidget类进行“描述”
tolua_open(lu);
tolua_module(lu, NULL, 0);
tolua_beginmodule(lu, NULL);
tolua_usertype(lu, "QWidget");
tolua_cclass(lu, "QWidget", "QWidget", "", tolua_delete_QWidget);
tolua_beginmodule(lu, "QWidget");
tolua_function(lu, "new", tolua_new_QWidget);
tolua_function(lu, "show", tolua_Show_QWidget);
tolua_function(lu, "resize", tolua_Resize_QWidget);
tolua_endmodule(lu);
tolua_endmodule(lu);
// 读取资源文件
QFile file("://test.lua");
file.open(QIODevice::ReadOnly | QIODevice::Text);
// 注意,笔者使用的事UTF-8编码,如果是ANSI编码可以去掉这句
QTextStream in(&file);
in.setCodec("UTF-8");
// 读取,并打印下来查看是否正确
QString str = in.readAll();
qDebug() << str;
// Lua加载并执行脚本
luaL_loadbuffer(lu, str.toLocal8Bit().data(), str.length(), "line");
lua_pcall(lu, 0, 0, 0);
return a.exec();
}
笔者将QWidget的几个函数都写成了static的形式,用tolua进行绑定。这样在Lua脚本中,就可以顺利使用绑定的函数了。下面的Lua脚本的内容:
w = QWidget:new() w:show() w:resize(300,400)
如果成功执行的话,运行结果应该是这样的:

可以看到成功建立了一个新的QWidget对象,并且大小更改了。
这就是在Lua中调用Qt类的一种方法。我们可以将Qt类中绝大多数类绑定进去,就可以在Lua脚本中自有的使用Qt。
对Lua的各个操作可以封装到类里,使代码结构更清晰。
我觉得现在已经可以实现你的很多奇思妙想了,不是吗?
附完整工程文件,可能要修改.pro文件中.lib文件的相对路径:
http://pan.baidu.com/s/1c0rbirm
附《Lua程序设计》电子书一本,希望有兴趣的同学可以共同学习交流:
http://pan.baidu.com/s/1c01k2J2
===================================================================
话接上文。笔者为了方便使用Lua,自己编写了一个Lua的类。主要代码如下:
QLua.h
1 #ifndef QLUA_H
2 #define QLUA_H
3
4 // own
5 #include "include/lua.hpp"
6
7 // qt
8 #include <QObject>
9 #include <QFile>
10 #include <QDebug>
11
12 #include <QWidget>
13 #include <QLineEdit>
14 #include <QPushButton>
15 #include <QMessageBox>
16
17 class QLua : public QObject
18 {
19 Q_OBJECT
20 public:
21 QLua(QObject *parent = 0);
22 ~QLua();
23
24 // lua
25 void init();
26 void close();
27
28 void pushFunction(QString funcName, lua_CFunction func);
29
30 void beginModule(QString);
31 void addType(QString, lua_CFunction deleteFunc);
32 void moduleTypeFunction(QString, lua_CFunction);
33 void endModule();
34
35 void run(QString);
36 signals:
37
38 public slots:
39
40 private:
41 lua_State *luaState;
42
43 bool isStartModule;
44 };
45
46 #endif // QLUA_H
QLua.cpp
1 #include "qlua.h"
2
3 QLua::QLua(QObject *parent) :
4 QObject(parent)
5 , luaState(NULL)
6 , isStartModule(false)
7 {
8 init();
9 }
10
11 QLua::~QLua()
12 {
13 close();
14 }
15
16 void QLua::init()
17 {
18 luaState = luaL_newstate();
19 luaL_openlibs(luaState);
20 }
21
22 void QLua::close()
23 {
24 if(luaState != NULL)
25 {
26 lua_close(luaState);
27 luaState = NULL;
28 }
29 }
30
31 void QLua::pushFunction(QString funcName, lua_CFunction func)
32 {
33 if (funcName.isEmpty()) return;
34 if (func == NULL) return;
35
36 lua_pushcfunction(luaState, func);
37 lua_setglobal(luaState, funcName.toLocal8Bit().data());
38 }
39
40 void QLua::beginModule(QString name)
41 {
42 if(luaState == NULL) return;
43
44 if (isStartModule == false)
45 {
46 tolua_open(luaState);
47 tolua_module(luaState, NULL, 0);
48 isStartModule = true;
49 }
50
51 const char *str = name.isEmpty()? NULL : name.toLocal8Bit().data();
52 tolua_beginmodule(luaState, str);
53 }
54
55 void QLua::addType(QString name, lua_CFunction deleteFunc)
56 {
57 if (luaState == NULL) return;
58 if (name.isEmpty()) return;
59 if (deleteFunc == NULL) return;
60
61 tolua_usertype(luaState, name.toLocal8Bit().data());
62 const char *str = name.toLocal8Bit().data();
63 tolua_cclass(luaState, str, str, "", deleteFunc);
64 beginModule(name);
65 }
66
67 void QLua::moduleTypeFunction(QString name, lua_CFunction func)
68 {
69 if(luaState == NULL) return ;
70 if (name.isEmpty()) return;
71
72 const char *str = name.toLocal8Bit().data();
73 tolua_function(luaState, str, func);
74 }
75
76 void QLua::endModule()
77 {
78 tolua_endmodule(luaState);
79 }
80
81 void QLua::run(QString str)
82 {
83 luaL_loadbuffer(luaState, str.toLocal8Bit().data(), str.length(), "line");
84 lua_pcall(luaState, 0, 0, 0);
85 }
QLua类可以方便的实现一些简单的Lua操作,如初始化,关闭,运行Lua代码,绑定函数等。
笔者目前想做到的是能在Lua代码中自有的生成Qt对象,然后能连接Qt原生对象的信号与槽。那么如何实现呢?
Qt中连接信号与槽的函数是QObject::connect(QObject * a, SIGNAL(), QObject * b, SLOT())。首先,我们要弄清楚SIGNAL和SLOT到底是什么。
从connect的参数列表中,我们可以很清晰的看到SIGNAL和SLOT的结果都是char*类型的字符串。我们可以直接在SIGNAL上点右键,转到SIGNAL的定义。或者做个简单的实验。测试如下Qt代码:
qDebug() << QString::fromLocal8Bit(SIGNAL(clicked()));
qDebug() << QString::fromLocal8Bit(SLOT(close()));
得到的结果很有意思:
"2clicked()" "1close()"
简单总结就是我们只要传入函数名的字符串,加上前缀即可。SIGNAL的前缀是2,SLOT的前缀是1。
这样我们就可以实现在Lua中的connect函数了。仍在使用C++编写,然后绑定到Lua里。如下:
1 static int connect(lua_State* state)
2 {
3 QObject * a = (QObject*)tolua_tousertype(state, 1, 0);
4 const char * signal = tolua_tostring(state, 2, 0);
5 QObject * b = (QObject*)tolua_tousertype(state, 3, 0);
6 const char * slot = tolua_tostring(state, 4, 0);
7
8 QObject::connect(a, QString("2%0").arg(signal).toLocal8Bit().data(),
9 b, QString("1%0").arg(slot).toLocal8Bit().data());
10 }
绑定时可以使用上面的QLua类:
lua.pushFunction("connect", connect);
完整main.cpp代码如下:
#include "include/lua.hpp"
#include "qlua.h"
#include <QWidget>
#include <QApplication>
#include <QFile>
#include <QDebug>
static int test(lua_State* state)
{
QPushButton* a = (QPushButton*)tolua_tousertype(state, 1, 0);
if(a)
a->show();
}
static int connect(lua_State* state)
{
QObject * a = (QObject*)tolua_tousertype(state, 1, 0);
const char * signal = tolua_tostring(state, 2, 0);
QObject * b = (QObject*)tolua_tousertype(state, 3, 0);
const char * slot = tolua_tostring(state, 4, 0);
QObject::connect(a, QString("2%0").arg(signal).toLocal8Bit().data(),
b, QString("1%0").arg(slot).toLocal8Bit().data());
}
static int tolua_new_QWidget(lua_State* state)
{
QWidget* widget = new QWidget();
tolua_pushusertype(state, widget, "QWidget");
return 1;
}
static int tolua_delete_QWidget(lua_State* state)
{
qDebug() << "delete Start";
QWidget* widget = (QWidget* )tolua_tousertype(state, 1, 0);
if(NULL != widget)
{
qDebug() << "delete~";
widget->close();
delete widget;
}
return 1;
}
static int tolua_Show_QWidget(lua_State* state)
{
QWidget* widget = (QWidget* )tolua_tousertype(state, 1, 0);
if(widget != NULL)
{
widget->show();
}
return 1;
}
static int tolua_new_QPushButton(lua_State* state)
{
QPushButton* button = new QPushButton();
tolua_pushusertype(state, button, "QPushButton");
return 1;
}
static int tolua_delete_QPushButton(lua_State* state)
{
QPushButton* button = (QPushButton* )tolua_tousertype(state, 1, 0);
if(NULL != button)
{
button->close();
delete button;
}
return 1;
}
static int tolua_Show_QPushButton(lua_State* state)
{
QPushButton* button = (QPushButton* )tolua_tousertype(state, 1, 0);
if(button != NULL)
{
button->show();
}
return 1;
}
static int tolua_setText_QPushButton(lua_State* state)
{
QPushButton* button = (QPushButton* )tolua_tousertype(state, 1, 0);
const char * text = tolua_tostring(state, 2, 0);
if(button != NULL)
{
button->setText(QString::fromLocal8Bit(text));
}
return 1;
}
static int tolua_Resize_QWidget(lua_State* state)
{
QWidget* widget = (QWidget* )tolua_tousertype(state, 1, 0);
double a = tolua_tonumber(state, 2, 0);
double b = tolua_tonumber(state, 3, 0);
if(widget)
{
widget->resize((int)a, (int)b);
}
return 1;
}
static int QApplication_instance(lua_State* state)
{
tolua_pushusertype(state, QApplication::instance(), "QApplication");
return 1;
}
static int QApplication_quit(lua_State* state)
{
QApplication * app = (QApplication *)tolua_tousertype(state, 1, 0);
if(app)
app->quit();
return 1;
}
static int QApplication_delete(lua_State*)
{
return 1;
}
int main(int argc, char * argv[])
{
Q_INIT_RESOURCE(resources);
QApplication a(argc, argv);
QLua lua;
lua.beginModule("");
lua.addType("QWidget", tolua_delete_QWidget);
lua.moduleTypeFunction("new", tolua_new_QWidget);
lua.moduleTypeFunction("show", tolua_Show_QWidget);
lua.moduleTypeFunction("resize", tolua_Resize_QWidget);
lua.endModule();
lua.addType("QPushButton", tolua_delete_QPushButton);
lua.moduleTypeFunction("new", tolua_new_QPushButton);
lua.moduleTypeFunction("show", tolua_Show_QPushButton);
lua.moduleTypeFunction("setText", tolua_setText_QPushButton);
lua.endModule();
lua.addType("QApplication", QApplication_delete);
lua.moduleTypeFunction("instance", QApplication_instance);
lua.moduleTypeFunction("quit", QApplication_quit);
lua.endModule();
lua.endModule();
lua.pushFunction("test", test);
lua.pushFunction("connect", connect);
// 读取资源文件
QFile file("://test.lua");
file.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream in(&file);
in.setCodec("UTF-8");
// 执行
lua.run(in.readAll());
return a.exec();
}
test.lua代码如下:
widget = QWidget:new()
widget:show()
button = QPushButton:new()
button:setText("China")
button:show()
connect(button, "clicked()", widget, "close()")
connect(button, "clicked()", button, "close()")
print("run over")
程序运行结果如下:

点击China按钮,button和widget的窗口都会关闭。
至此,已经实现了连接 Qt 对象的信号与槽。
如果继续做下去,完全可以实现在Lua脚本中使用Qt的类和对象,写出Lua的Gui程序。
附完整代码工程文件:http://pan.baidu.com/s/1ntmFdjj
附个偶的博客地址:http://www.cnblogs.com/IT-BOY/
===================================================================
话接上篇。通过前几篇博客,我们实现在Lua脚本中执行Qt类中函数的方法,以及在Lua脚本中连接Qt对象的信号与槽。
但是,我们也能发现,如果希望在Lua脚本中执行Qt类的函数,就必须绑定一个真正实现功能的函数。如QWidget::show(),需要写一个在栈中取出widget指针,widget调用show()函数的方式。如果希望在Lua中调用大量函数,就需要编写大量的C++实现函数。有没有什么省时省力的好方法呢?
上一篇中我们实现了在Lua脚本中连接信号与槽。我们只是传过去了两个QObject的对象,和两个字符串的函数名。我们并没有具体实现那个函数,但是槽函数顺利执行了。这给了笔者启发。如果我传过去一个函数名,理论上我可以找一个信号连接并激发它,也就达到了执行的目的。
OK,我们写这样一个函数:
static int exec(lua_State *state)
{
QObject* obj = (QObject* )tolua_tousertype(state, 1, 0);
const char * slot = tolua_tostring(state, 2, 0);
if(obj != NULL)
{
RunSLOT run;
QObject::connect(&run, SIGNAL(sendSignal()),
obj, QString("1%0()").arg(slot).toLocal8Bit().data());
}
return 1;
}
取得QObject对象,以及字符串的槽函数名称。然后我们新建了一个RunSLOT对象,连接了run和obj对象。这样就能执行字符串代表的函数?
继续看RunSLOT类:
#ifndef RUNSLOT_H
#define RUNSLOT_H
#include <QObject>
class RunSLOT : public QObject
{
Q_OBJECT
public:
RunSLOT(QObject *parent = 0);
~RunSLOT();
signals:
void sendSignal();
};
#endif // RUNSLOT_H
#include "runslot.h"
RunSLOT::RunSLOT(QObject *parent) :
QObject(parent)
{
}
RunSLOT::~RunSLOT()
{
emit sendSignal();
}
在析构是发出sendSignal信号,激发槽函数。而RunSLOT类对象run在遇到‘}’自动析构(C++局部变量原则),也就间接的执行了槽函数。
在绑定函数时将exec绑定到类里,然后写个测试脚本看看:
widget = QWidget:new()
widget:exec("show")
button = QPushButton:new()
button:exec("show")
connect(button, "clicked()", widget, "hide()")
成功显示一个窗体,一个按钮。
完整main.cpp如下:
// own
#include "include/lua.hpp"
#include "qlua.h"
#include "runslot.h"
// qt
#include <QWidget>
#include <QApplication>
#include <QFile>
#include <QDebug>
static int connect(lua_State* state)
{
QObject * a = (QObject*)tolua_tousertype(state, 1, 0);
const char * signal = tolua_tostring(state, 2, 0);
QObject * b = (QObject*)tolua_tousertype(state, 3, 0);
const char * slot = tolua_tostring(state, 4, 0);
QObject::connect(a, QString("2%0").arg(signal).toLocal8Bit().data(),
b, QString("1%0").arg(slot).toLocal8Bit().data());
return 1;
}
static int exec(lua_State *state)
{
QObject* obj = (QObject* )tolua_tousertype(state, 1, 0);
const char * slot = tolua_tostring(state, 2, 0);
if(obj != NULL)
{
RunSLOT run;
QObject::connect(&run, SIGNAL(sendSignal()),
obj, QString("1%0()").arg(slot).toLocal8Bit().data());
}
return 1;
}
static int QObject_delete(lua_State* state)
{
qDebug() << "delete Start";
QObject* obj = (QObject* )tolua_tousertype(state, 1, 0);
if(NULL != obj)
{
qDebug() << "delete~";
delete obj;
}
return 1;
}
static int QWidget_new(lua_State* state)
{
QWidget* widget = new QWidget();
tolua_pushusertype(state, widget, "QWidget");
return 1;
}
static int QPushButton_new(lua_State* state)
{
QPushButton* button = new QPushButton();
tolua_pushusertype(state, button, "QPushButton");
return 1;
}
static int QWidget_resize(lua_State* state)
{
QWidget* widget = (QWidget* )tolua_tousertype(state, 1, 0);
double a = tolua_tonumber(state, 2, 0);
double b = tolua_tonumber(state, 3, 0);
if(widget)
{
widget->resize((int)a, (int)b);
}
return 1;
}
int main(int argc, char * argv[])
{
Q_INIT_RESOURCE(resources);
QApplication a(argc, argv);
QLua lua;
lua.beginModule("");
lua.addType("QWidget", QObject_delete);
lua.moduleTypeFunction("new", QWidget_new);
lua.moduleTypeFunction("resize", QWidget_resize);
lua.moduleTypeFunction("exec", exec);
lua.endModule();
lua.addType("QPushButton", QObject_delete);
lua.moduleTypeFunction("new", QPushButton_new);
lua.moduleTypeFunction("exec", exec);
lua.endModule();
lua.endModule();
lua.pushFunction("connect", connect);
// 读取资源文件
QFile file("../../LuaTest/test.lua");
file.open(QIODevice::ReadOnly | QIODevice::Text);
QTextStream in(&file);
in.setCodec("UTF-8");
// 执行
lua.run(in.readAll());
return a.exec();
}
我们可以在将绑定exec函数写在QLua类里,添加类时就绑定这个函数。
当然,这个方法的局限性在于执行的函数无参数,而且必须是槽函数。如果想自由一个,可以在调用时写上参数,参数类型,C++这边判断并选择不同的信号连接方式,一劳永逸。
通过这个例子,我们可以发现,Qt本身就具备由字符串执行函数的能力,按照我的见解,这就是一种半动态。笔者会继续做下去,因为这个真的很有趣。
完整例程代码下载:http://pan.baidu.com/s/1sjFDiRb
转载注明出处:http://www.cnblogs.com/IT-BOY/
===================================================================
===================================================================
===================================================================



