1、 Lua脚本对c++接口的调用
1.1 简单示例
Lua脚本通过c++链接库调用c++提供的接口。c++提供的链接库需要按某种“样式”书写,才能够被lua脚本调用。
先通过一个简单示例来给大家一个很初步的认识:
首先建立一个空的c++动态链接库程序;
在其中建立test.h与test.cpp两个文件,其内容如下:
// test.h
// include headers
extern "C"
{
#include "lua.h"
#include "lualib.h"
#include "lua.hpp"
}
// export to lua functions
static int Lua_TestA(lua_State* L);
static int Lua_TestB(lua_State* L);
// reg exporting lua functions to lua
static const LuaL_reg LUADllFunctions[] =
{
{"TestA",Lua_TestA},
{"TestB",Lua_TestB},
{NULL,NULL}
}
// dll export function
extern "C"
{
int __declspec(dllexport) LibExportFun(Lua_State* L);
}
// test.cpp
//
#include "test.h"
#include <string>
#include <windows.h>
int LibExportFun(lua_State* L)
{
luaL_openlib(L,"LUALibFuns",LUADllFunctions,0);
return 1;
}
int Lua_TestA(lua_State* L)
{
// 弹出消息框
MessageBox(NULL,"Test In C","Test",MB_OK);
return 1;
}
int Lua_TestB(lua_State* L)
{
// add two string
// 获取参数个数
int iNum = lua_gettop(L);
if(iNum != 2)
return 0;
// string A
size_t lenA;
const char* sA = lua_tolstring(L,1,&lenA);
// string B
size_t lenB;
cong char* sB = lua_tolstring(L,2,&lenB);
std::string strA = sA;
std::string strB = sB;
std::string strRe = sA + sB;
// 参数返回
lua_pushstring(L,strRe.c_str());
return 1;
}
以上部分为c++链接库部分需要完成工作,下边就是如何在Lua脚本中调用上边提供的接口,当然,首先需要将编译的c++链接库编译正确。
Lua脚本调用示例:
local dll = "E:/test/test.dll"; -- 实际使用时请输入链接库的绝对文件路径
local testlib = package.loadlib(dll,"LibExportFun");
if !testlib then
-- end
end
-- 相当于调用链接库的导出函数
testlib();
-- 因为在连接库的导出函数中完成了函数的注册,所以此时可以安全的 调用链接库提供的接口函数了
LUALibFuns.TestA();
sRe = LuALibFuns.TestB("TA","TB");
print(sRe); -- 应该输出TATB
到此,希望您将上边所有代码自己编译、运行下,然后运行下我们的lua脚本,发现接口被正确调用了吧。如果是刚刚接触Lua的同学,是不是很激动啊,下边我们会继续稍微深入研究每条语句的含义,同时会做一些相应的扩展。
1.2 深入理解,举一反三
在头文件Test.h中,我们会看到两个以前没有见过的标识符,lua_State和LuaL_reg。它们是什么?用来做什么用?
lua_State:
对lua_State的分析,我们首先的认知是,它是一个函数参数。暂时,我们假设Lua脚本在调用我们导出的函数时,自动将参数组合成一个统一的lua_State结构,这样来完成通信。
luaL_reg:
我们可以在代码中查看到luaL_reg的定义:
typedef struct luaL_Reg {
const char *name;
lua_CFunction func;
} luaL_Reg;
它是一个结构,一个通过名字描述一个对应的函数。我们再来看lua_CFunction函数定义;
typedef int (*lua_CFunction) (lua_State *L);
可以发现,我们导出给Lua脚本使用的函数必须返回int型,并且参数为lua_State*。这样我们就可以理解为什么我们两个导出给lua脚本使用的函数定义形式必须那个样子。
也许您会问,为什么必须设计的传递lua_State*这个类型的参数?这样有个好处,接口统一,关于这点有点类似下边的情况,
假设你提供了一个链接库供其它人使用,接口形式如int add(int,int);是计算两个数的和,平时运行的很好,但有一天用户希望能够直接完成三个数的求和,这样就不需要分两次调用接口了,如何更改呢?很可能会添加一个接口形式如int add(int,int,int),这样调用者很可能需要重新编译程序,这样比较麻烦,所以一个库的接口一定要设计的具有比较好的扩展性,尽量保持接口的稳定,统一性。
今天暂时先写到这里,改天继续写。