以上时函数,但是全局C函数满天飞肯定是不行的 更好一点的情况是把C++类注册进Lua
Quick-Cocos2d-x 3.3绑定自定义C++类02:生成并使用tolua++工具
Quick-Cocos2d-x 创建自定义Lua绑定C++类
Cocos2d-x下Lua调用自定义C++类和函数的最佳实践
----------------------------------------------------
到公司已经两个周了,学习Lua已经开始在项目中使用,但是由于使用的lua函数基本上都是公司在上面进行了一次封装的,没有源代码对两种语言的交互详情还是不甚了解。如:如果向LUA注册一个对象给LUA使用,如何调用LUA中函数这些在公司的SDK看来就是一个简单的RegisterObject对象的几个属性进行填写就行了。
今天主要是对在Lua中如何调用C++函数和在C++中如何调用Lua函数进行学习。
在LUA中要调用C++函数,那就要在C++中对Lua进行注册,如何注册呢?需要一个宏来完成功能,就是lua_register(L,"add",add),该宏对应的是两个函数,一个是lua_pushcfunction(L,f),lua_setglobal(L,n);可以看出是先对函数进行压栈、然后给函数设置一个在lua中的调用名称,注意此处的n不是表示一个整数什么的,是name的意思,表示函数的注册名。而此处lua_setglobal也是一个宏,
#define lua_setglobal(L,s) lua_setfield(L, LUA_GLOBALSINDEX, s)
可以看出其实lua_setglobal只是lua_setfield的一个特例,因为LUA_GLOBALSINDEX是一直存在的,所以使用干函数来简单操作,当然如果我们要注册的函数不注册到全局的,而是在其他某个我们定义的表内可访问,那就要调用lua_setfield函数了。说到这个话题,设计到的函数就会越来越多,比如lua_getglobal与lua_setglobal对于,lua_getfield与lua_getfield对应,我们用到的时候再解释,不用到时就不说了。
上面提到了使用lua_register函数进行注册,这里要强调的是该函数对注册的函数原型是有要求的,不是任何函数都可以注册,函数原型如下:
int (func*)(lua_State* L);我们应该能够理解,LUA和C++的交互通过栈来进行传参,因此只需要给一个参数lua_State表当前操作栈就行了,至于该函数有几个参数,返回值这些在func函数中去处理就好了,该函数返回值表示C++函数会向栈中放多少个值,而至于需要几个参数可以通过lua_tostring、lua_tointeger等函数通过传入栈中的序号来获取。由于注册函数原型固定了,但我们在写C++函数的时候不可能代码长短什么的都往该函数中塞,在实际中我们常常是只使用该函数来获取从LUA读取参数和返回参数,所以该函数一般是这样的形式:
int funcname(lua_State* L)
{
//此处可以做数据类型检查这些
..
//取值
arg1 = lua_tostring(L,-1);
arg2 = lua_tostring(L,-2);
//调用真正的函数
dosomething_function
//进行参数压栈等也可以再上面调用的函数中压栈
return n;//此处的n是C++向栈中压入的参数个数,如果和压入栈个数不一致,可能导致栈失衡
}
注册函数来说就上面几个步骤而已,比起注册C++类来说简单得多,注册C++类现在我也还没有掌握,在此处就不说了,下面说的是我们注册了C++函数现在该到LUA中去调用了,现在在test.lua文件中添加这样一个函数
function lua_add(a,b)
return add(a,b)
end
现在如果在命令行执行该文件应该会失败,因为我上面注册的C++函数不是注册成一个动态库,我是直接在控制台可执行文件中写的,因此我就需要在
C++中来调用该lua_add函数了。C++中需要先知道有lua_add这样一个函数,需要先使用luaL_dofile(test.lua)来加载该文件,加载进去后该函数就存在于全局表中了,于是使用lua_getglobal函数来从表中获取函数地址lua_getglobal(L,"lua_add"),该函数是在全局表中查找lua_add函数并把它压到栈顶,到这里刚开始学习的时候会很疑惑,当初我看lua中很多函数介绍的时候就觉得很不解,很多函数就是对栈操作,单独看一个函数实在看不出想达到什么目的,但和其他函数配合使用功能就强大了,所以需要对lua中这些对栈操作的函数都有一个认识,才能够组合出自己需要的功能来。前面说把函数找到压栈了,下面就是要调用函数,调用函数前需要传参数,如何传?还是把参数放到栈中、如果我们需要给函数传两个参数,把连个参数压栈,使用函数lua_pushinteger(L,4)表示向栈中压入一个整数4,其他类型如lua_pushstring,lua_pushlstring等很多。参数也进栈了,下面才是开始真正调用,如何调?lua提供的函数lua_pcall来调用,该函数第一个参数为lua_State表示当前的栈吧,第二个参数表示要调用函数的参数个数,第三个表示被调用函数的返回值个数,这个不要想C++中函数只有一个返回值,这是表示我们要向lua中返回多少个值;第四个参数表示错误处理函数在栈上的索引,为0表示没有错误处理函数,和lua_pcall一样用于调用栈中函数的函数有lua_call和lua_cpcall等,这里不仔细介绍。在上面的函数调用中,由于返回值存在与栈中,在取回返回值后,需要调用lua_pop(L)对栈进行清空。
下面是一个测试代码:
- #include <stdio.h>
- extern "C"
- {
- #include <lua.h>
- #include <lualib.h>
- #include <lauxlib.h>
- }
- #include <string>
- using namespace std;
- int add(lua_State* L)
- {
- int a = lua_tointeger(L,1);//取得函数参数
- int b = lua_tointeger(L,2);
- lua_pushinteger(L,a+b);//入栈返回值
- return 1;//1表示压入栈数据个数
- }
- struct luaL_Reg luaCppReg[] =//可以使用该结构体一次注册多个函数,则需要调用luaL_register函数,此处没有使用
- {
- {"add",add},
- {NULL,NULL}
- };
- int _tmain(int argc, _TCHAR* argv[])
- {
- char buff[256];
- int error;'
- lua_State *L = luaL_newstate(); /* opens Lua,由于我使用的是lua5.2版本,lua_open函数不存在了 */
- luaopen_base(L); /* opens the basic library 这些是在引入一些库,就如如果add函数在编译成dll后如果在lua中要使用需要require “动态库名"一样*/
- luaopen_table(L); /* opens the table library这些库是加在这里只是测试 */
- luaopen_io(L); /* opens the I/O library */
- luaopen_string(L); /* opens the string lib. */
- luaopen_math(L); /* opens the math lib. */
- lua_register(L,"add",add);//注册add函数,好像还可以使用luaL_register函数注册,该函数使用结构体的方式
- luaL_dofile(L,"test.lua");//加载lua文件,回将里面的函数加载到全局表中
- lua_getglobal(L,"lua_add");//查找lua_add函数,并压入栈底
- lua_pushinteger(L,6);//函数参数1
- lua_pushinteger(L,5);//函数参数2
- lua_pcall(L,2,1,0);//调用lua_add函数,同时会对lua_add及两个参加进行出栈操作,并压入返回值
- int result = lua_tointeger(L,-1);//从栈中取回返回值
- lua_pop(L,1);//清栈,由于当前只有一个返回值
- printf("result = %d",result);
- lua_close(L);//关闭lua环境
- return 0;
- }
--------------------------------------------
lua
一.lua堆栈
要理解lua和c++交互,首先要理解lua堆栈。
简单来说,Lua和C/c++语言通信的主要方法是一个无处不在的虚拟栈。栈的特点是先进后出。
在lua中,lua堆栈就是一个struct,堆栈索引的方式可是是正数也可以是负数,区别是:正数索引1永远表示栈底,负数索引-1永远表示栈顶。如图:
二.堆栈的操作
因为lua与c/c++是通过栈来通信,lua提供了C API对栈进行操作。
#include <iostream>
#include <string.h>
using namespace std;
extern C
{
#include lua.h
#include lauxlib.h
#include lualib.h
}
void
main()
{
//1.创建一个state
lua_State *L = luaL_newstate();
//2.入栈操作
lua_pushstring(L, I am so cool~);
lua_pushnumber(L,
20
);
//3.取值操作
if
( lua_isstring(L,
1
)){
//判断是否可以转为string
cout<<lua_tostring(l,
1
)<<endl;
- 调用lua_pushnumber()将参数的平均值压栈。
- lua_register(L, "avg", average); //第二参数为Lua中调用的函数名
- lua_gettop函数返回栈顶的索引值。因为在Lua中栈是从1开始编号的,因此该函数获得的值就是参数的个
可以简单理解为luaL_newstate返回一个指向堆栈的指针,其它看注释应该能懂了吧。
//2.加载lua文件
int
bRet = luaL_loadfile(L,hello.lua);
if
(bRet)
<br>
需要注意的是:函数参数里的lua_State是私有的,每一个函数都有自己的栈。当一个c/c++函数把返回值压入Lua栈以后,该栈会自动被清空。
lua和c++是通过一个虚拟栈来交互的。
c++调用lua实际上是:由c++先把数据放入栈中,由lua去栈中取数据,然后返回数据对应的值到栈顶,再由栈顶返回c++。</p>
lua调c++也一样:先编写自己的c模块,然后注册函数到lua解释器中,然后由lua去调用这个模块的函数。</p>
- #include "stdafx.h"
- #include<stdio.h>
-
- extern "C" { //如不用extern会出现连接错误,编译成了C++文件
- #include <lua.h>
- #include <lualib.h>
- #include <lauxlib.h>
- }
-
- int average(lua_State *L){
- int n= lua_gettop(L);
- int i;
- double sum=0;
- for(i=0;i<n;i++){
- sum+=lua_tonumber(L,-1);
- lua_pop(L,1);
- }
- lua_pushnumber(L,sum/n);
- if(!lua_isnumber(L,-1))
- luaL_error(L,"push error!\n");
- return 1;
- }
- int _tmain(int argc, _TCHAR* argv[])
- {
- lua_State* L = lua_open();
- luaL_openlibs(L); //新版本库添加的方法
-
- lua_register(L,"avg",average);
- luaL_dofile(L,"cof.lua");
-
- getchar();
- return 0;
- }
- ———————————————————Lua和C语言的交互详解——————————————————————
-
交互常用的函数:
压入栈:
对于每种可以呈现在Lua中的C类型,API都有一个对应的压入函数,我这里把它们都列出来:
void lua_pushnil(lua_State *L);
void lua_pushboolean(lua_State *L, int bool);
void lua_pushnumber(lua_State *L, lua_Number n);
void lua_pushinteger(lua_State *L, lua_Integer n);
void lua_pushlstring(lua_State *L, const char *s, size_t len);
void lua_pushstring(lua_State *L, const char *s);
上面的函数非常简单,从命名就能知道它们的含义。这里不多说
查询元素:
为了检查一个元素是否为特定的类型,API提供了一系列的函数lua_is*,其中*可以是任意Lua类型。这些函数有lua_isnumber、lua_isstring和lua_istable等,所有这些函数都有同样的原型:
实际上,lua_isnumber不会检查值是否为数字类型,而是检查值是否能转换为数字类型。lua_isstring也具有同样的行为,这样就出现一种状况,对于能转换成string的值
就出现了一个lua_type函数,它会返回栈中元素的类型,每种类型都对应一个常亮,这些常亮定义在头文件lua.h中,它们是:
#define LUA_TNONE (-1)
#define LUA_TNIL 0
#define LUA_TBOOLEAN 1
#define LUA_TLIGHTUSERDATA 2
#define LUA_TNUMBER 3
#define LUA_TSTRING 4
#define LUA_TTABLE 5
#define LUA_TFUNCTION 6
#define LUA_TUSERDATA 7
#define LUA_TTHREAD 8
取值
我们一般使用lua_to*函数用于从栈中获取一个值,有以下常用的取值函数:
lua_Number lua_tonumber (lua_State *L, int idx);
lua_Integer lua_tointeger (lua_State *L, int idx);
int lua_toboolean (lua_State *L, int idx);
const char *lua_tolstring (lua_State *L, int idx, size_t *len);
size_t lua_objlen (lua_State *L, int idx);
lua_CFunction lua_tocfunction (lua_State *L, int idx);
void *lua_touserdata (lua_State *L, int idx);
lua_State *lua_tothread (lua_State *L, int idx);
const void *lua_topointer (lua_State *L, int idx);
如果指定的元素不具有正确的类型,调用这些函数也不会有问题。在这种情况下,lua_toboolean、lua_tonumber、lua_tointeger和lua_objlen会返回0,而其它函数会返回NULL
其它栈操作
除了在C语言和栈之间交换数据的函数外,API还提供了以下这些用于普通栈操作的函数:
int lua_gettop (lua_State *L);
void lua_settop (lua_State *L, int idx);
void lua_pushvalue (lua_State *L, int idx);
void lua_remove (lua_State *L, int idx);
void lua_insert (lua_State *L, int idx);
void lua_replace (lua_State *L, int idx);
lua_gettop函数返回栈中元素的个数,也可以说是栈顶元素的索引。lua_settop将栈顶设置为一个指定的位置,即修改栈中元素的数 量,如果之前的栈顶比新设置的更高,那么高出来的这些元素会被丢弃;反之,会向栈中压入nil来补足大小;比如,调用以下语句就能清空栈:
lua_settop(L, 0);
————————————————————
Lua和C++语言的交互详解
——————————————————————
table操作
在Lua中,对于table这种bug一样存在的东西,如果C API无法操作table,那我们还能不能愉快的玩耍了。让我们来看看C API如何操作table。现在有如下Lua语句:
复制代码 代码如下:
background = {r = 0.3, g = 1, b = 0.5}
那么,C API如何读取这段代码,将其中的每个字段都解析出来呢。我先把代码贴上来,然后一句一句的分析:
复制代码 代码如下:
// 读取全局的数据到栈中
lua_getglobal(L, "background");
if (!lua_istable(L, -1))
{
// 如果不是table,就显示错误信息
cout << "It's not a table." << endl;
return 0;
}
// 读取table中字段的值,将值压入栈中
lua_getfield(L, -1, "r");
// 读取栈中的值
if (!lua_isnumber(L, -1))
{
// 如果不是实数,就显示错误信息
cout << "It's not a number." << endl;
return 0;
}
double fValue = lua_tonumber(L, -1);
cout << "r => " << fValue << endl;
原谅我省略了luaL_newstate这样的代码。好了,读取一个table,同读取一个全局的变量是一个道理的。分为以下几步:
1.使用lua_getglobal读取这个变量,将table读取到栈中;
2.使用lua_getfield读取table中字段的值,将字段的值读取到栈中;
3.使用lua_to*系列函数,将字段的值从栈中读取出来。
这是读取table的操作,那设置table的操作呢?我们可以将我们自己的值写入到栈中,这该怎么操作?
所以,C++调用Lua中的函数,分为以下几步:
使用lua_getglobal来获取函数,然后将其压入栈;
如果这个函数有参数的话,就需要依次将函数的参数也压入栈;
这些准备工作都准备就绪以后,就调用lua_pcall开始调用函数了,调用完成以后,会将返回值压入栈中;
最后取返回值得过程不用多说了,调用完毕。
------------------------------------------------------------------------------------------------
Lua和C之间的交互
(一) Lua 调C函数
1. 什么样类型的函数可以被Lua调用
typedef int (*lua_CFunction) (lua_State *L);
2. 符合类型的函数怎样处理后才可以被Lua调用
使用lua_register或者 lua_pushfunction和lua_setglobal()把要调用的函数加入到lua状态机中。
#define lua_register(L,n,f) /
(lua_pushcfunction(L, f), lua_setglobal(L, n))
lua_register的第二个参数就是Lua脚本中对这个函数的调用名称。
举例:
如果C函数名称是foo,使用lua_registe注册(L,”acfoo”,foo),那么在Lua脚本中使用acfoo来表示使用foo函数.
3. Lua 如何调用c函数
简单,使用注册的名称直接调用
4. 如何传递参数和计算结果
① 使用堆栈交互
引用使用手册上的一段话:
Lua 使用一个虚拟栈来和 C 传递值。栈上的的每个元素都是一个 Lua 值(nil,数字,字符串,等等)。
无论何时 Lua 调用 C,被调用的函数都得到一个新的栈,这个栈独立于 C 函数本身的堆栈,也独立于以前的栈。(在 C 函数里,用 Lua API 不能访问到 Lua 状态机中本次调用之外的堆栈中的数据),它里面包含了 Lua 传递给 C 函数的所有参数,而 C 函数则把要返回的结果也放入堆栈以返回给调用者。
方便起见,所有针对栈的 API 查询操作都不严格遵循栈的操作规则。而是可以用一个索引来指向栈上的任何元素:正的索引指的是栈上的绝对位置(从一开始);负的索引则指从栈顶开始的偏移量。更详细的说明一下,如果堆栈有 n 个元素,那么索引 1 表示第一个元素(也就是最先被压入堆栈的元素)而索引 n 则指最后一个元素;索引 -1 也是指最后一个元素(即栈顶的元素),索引 -n 是指第一个元素。如果索引在 1 到栈顶之间(也就是,1 ≤ abs(index) ≤ top)我们就说这是个有效的索引。
② 从Lua脚本中获取参数
int n = lua_gettop(L);
/* get each argument */
lua_tostring(lua_State *L, int index)
…….
index: 1—左边第一个参数,2—左边第二个参数,......
③ 返回返回值
按顺序返回,Lua按照返回顺序接受
Lua_pushXXX(L,第一个返回值)
Lua_pushXXX(L,第二个返回值)
………
Lua调用C函数例子:
C程序:
static int average(lua_State *L)
{
/* get number of arguments */
int n = lua_gettop(L);
double sum = 0;
int i;
/* loop through each argument */
for (i = 1; i <= n; i++)
{
if (!lua_isnumber(L, i))
{
lua_pushstring(L, "Incorrect argument to 'average'");
lua_error(L);
}
/* total the arguments */
sum += lua_tonumber(L, i);
}
/* push the average */
lua_pushnumber(L, sum / n); //第一个返回值
/* push the sum */
lua_pushnumber(L, sum); //第二个返回值
/* return the number of results */
return 2;
}
void LuaCallC()
{
/* initialize Lua */
lua_State * L = lua_open();
/* load Lua base libraries */
luaL_openlibs(L);
/* register our function */
lua_register(L, "average", average);
/* run the script */
luaL_dofile(L, "average.lua");
/* cleanup Lua */
lua_close(L);
}
Lua脚本,average.lua:
avg, sum = average(20,40,50,60,80) ----------------为啥如此多的参数????? yang
print("The average is ", avg)
print("The sum is ", sum)
(二) Lua 从C库中调用
1. 生成C函数库
① 所有可以被Lua调用的函数必须是lua_CFunction类型
② 所有被调用的函数加入到一个luaL_reg数组中
③ 一个luaopen_*(*表示库的名称)供lu调用库时打开库
使用luaL_register(lua_State *L,
const char *libname,
const luaL_Reg *l)
libname,注册lua使用这个库时的使用名称
luaL_Reg *l,把luaL_Reg数组里的函数注册到lua栈里,供lua调用
注意:BCB默认导出的c函数前面加了下划线,因此在动态库工程中加入一个def文件,在生成时不用加下划线。内容是:
Export
FunName = _FunName (FunName表示要导出的函数名称,Lua使用的库中就是luaopen_*)
2. Lua使用c库
require(libname) – 打开使用的库
libname.FunName – 使用c库中提供的函数
Lua调用C函数库例子:
C库代码,C函数的名称”dllforlua.dll”
static int lua_msgbox(lua_State* L)
{
const char* message = luaL_checkstring(L, 1);
const char* caption = luaL_optstring(L, 2, "");
int result = MessageBox(NULL, message, caption, MB_YESNO);
lua_pushnumber(L, result);
return 1;
}
static const luaL_Reg mylib[] =
{
{"msgbox", lua_msgbox},
{NULL, NULL}
};
int __declspec(dllexport) luaopen_dllforlua(lua_State* L)
{
luaL_register(L, "dllforlua", mylib);
return 1;
}
Lua脚本,Test.lua
require(“dllforlu”)
dllforlua.msgbox("Hey, it worked!", "Lua Message Box");
(三) C调Lua 函数
1. 初始化Lua环境
Lua_open或者:lua_newstate
luaL_newstate(调用lua_newstate,并且设置了一个恐慌函数)
2. 加载Lua标准库
Lua_openlibs(打开所有标准库)
不打开所有库,打开需要的库:
Luaopen_base
……….
3. 加载Lua和函数函数
luaL_dofile()
lua_getglobal()
大小写敏感,名字于Lua脚本的函数名称大小写完全一致
4. 压入参数
不同类型采用不同的函数,按照从左往右的顺序依次压栈
lua_pushnumber,lua_pushstring,…..
5. 执行函数
lua_call, lua_pcall
6. 获取返回值
不同类型使用不同的函数,注意索引,获取前要检查类型
7. 从栈中弹出返回值
lua_pop()
8. 关闭Lua状态机
lua_close()
C程序掉用Lua函数例子:
C函数:
void CCallLua()
{
// Create a LUA VMachine
lua_State *L;
//L = luaL_newstate();
L = lua_open();
//Load Libraries
luaL_openlibs(L);
// 运行脚本 /
luaL_dofile(L, "clua.lua");
lua_getglobal(L,"Sum");
lua_pushnumber(L,2);//第一个参数
lua_pushnumber(L,3);//第二个参数
lua_pushnumber(L,4);//第三个参数
lua_pcall(L,3,2,0);
double sum=0,ave=0;
if(lua_isnumber(L,1))
{
sum=lua_tonumber(L,1);
}
if(lua_isnumber(L,2))
{
ave=lua_tonumber(L,2);
}
lua_pop(L,2);
cout<<"Sum ="<<sum
<<"/nAve ="<<ave<<endl;
// 清除Lua
lua_close(L);
getchar();
}
Lua脚本Clua.lua:
function Sum(...)
local s=0
local num=0
for k,v in pairs{...} do
s = s + v
num = k
end
return s,s/num
end
交互常用的函数:
对于每种可以呈现在Lua中的C类型,API都有一个对应的压入函数,我这里把它们都列出来:
void lua_pushboolean(lua_State *L, int bool);
void lua_pushnumber(lua_State *L, lua_Number n);
void lua_pushinteger(lua_State *L, lua_Integer n);
void lua_pushlstring(lua_State *L, const char *s, size_t len);
void lua_pushstring(lua_State *L, const char *s);
#define LUA_TNIL 0
#define LUA_TBOOLEAN 1
#define LUA_TLIGHTUSERDATA 2
#define LUA_TNUMBER 3
#define LUA_TSTRING 4
#define LUA_TTABLE 5
#define LUA_TFUNCTION 6
#define LUA_TUSERDATA 7
#define LUA_TTHREAD 8
我们一般使用lua_to*函数用于从栈中获取一个值,有以下常用的取值函数:
lua_Integer lua_tointeger (lua_State *L, int idx);
int lua_toboolean (lua_State *L, int idx);
const char *lua_tolstring (lua_State *L, int idx, size_t *len);
size_t lua_objlen (lua_State *L, int idx);
lua_CFunction lua_tocfunction (lua_State *L, int idx);
void *lua_touserdata (lua_State *L, int idx);
lua_State *lua_tothread (lua_State *L, int idx);
const void *lua_topointer (lua_State *L, int idx);
其它栈操作
除了在C语言和栈之间交换数据的函数外,API还提供了以下这些用于普通栈操作的函数:
int lua_gettop (lua_State *L);
void lua_settop (lua_State *L, int idx);
void lua_pushvalue (lua_State *L, int idx);
void lua_remove (lua_State *L, int idx);
void lua_insert (lua_State *L, int idx);
void lua_replace (lua_State *L, int idx);
lua_gettop函数返回栈中元素的个数,也可以说是栈顶元素的索引。lua_settop将栈顶设置为一个指定的位置,即修改栈中元素的数 量,如果之前的栈顶比新设置的更高,那么高出来的这些元素会被丢弃;反之,会向栈中压入nil来补足大小;比如,调用以下语句就能清空栈:
Lua和C++语言的交互详解
——————————————————————table操作
在Lua中,对于table这种bug一样存在的东西,如果C API无法操作table,那我们还能不能愉快的玩耍了。让我们来看看C API如何操作table。现在有如下Lua语句:
background = {r = 0.3, g = 1, b = 0.5}
那么,C API如何读取这段代码,将其中的每个字段都解析出来呢。我先把代码贴上来,然后一句一句的分析:
// 读取全局的数据到栈中
lua_getglobal(L, "background");
if (!lua_istable(L, -1))
{
// 如果不是table,就显示错误信息
cout << "It's not a table." << endl;
return 0;
}
// 读取table中字段的值,将值压入栈中
lua_getfield(L, -1, "r");
// 读取栈中的值
if (!lua_isnumber(L, -1))
{
// 如果不是实数,就显示错误信息
cout << "It's not a number." << endl;
return 0;
}
double fValue = lua_tonumber(L, -1);
cout << "r => " << fValue << endl;
原谅我省略了luaL_newstate这样的代码。好了,读取一个table,同读取一个全局的变量是一个道理的。分为以下几步:
1.使用lua_getglobal读取这个变量,将table读取到栈中;
2.使用lua_getfield读取table中字段的值,将字段的值读取到栈中;
3.使用lua_to*系列函数,将字段的值从栈中读取出来。
这是读取table的操作,那设置table的操作呢?我们可以将我们自己的值写入到栈中,这该怎么操作?
所以,C++调用Lua中的函数,分为以下几步:
使用lua_getglobal来获取函数,然后将其压入栈;
如果这个函数有参数的话,就需要依次将函数的参数也压入栈;
这些准备工作都准备就绪以后,就调用lua_pcall开始调用函数了,调用完成以后,会将返回值压入栈中;
最后取返回值得过程不用多说了,调用完毕。
------------------------------------------------------------------------------------------------
Lua和C之间的交互
(一) Lua 调C函数
1. 什么样类型的函数可以被Lua调用
typedef int (*lua_CFunction) (lua_State *L);
2. 符合类型的函数怎样处理后才可以被Lua调用
使用lua_register或者 lua_pushfunction和lua_setglobal()把要调用的函数加入到lua状态机中。
#define lua_register(L,n,f) /
(lua_pushcfunction(L, f), lua_setglobal(L, n))
lua_register的第二个参数就是Lua脚本中对这个函数的调用名称。
举例:
如果C函数名称是foo,使用lua_registe注册(L,”acfoo”,foo),那么在Lua脚本中使用acfoo来表示使用foo函数.
3. Lua 如何调用c函数
简单,使用注册的名称直接调用
4. 如何传递参数和计算结果
① 使用堆栈交互
引用使用手册上的一段话:
Lua 使用一个虚拟栈来和 C 传递值。栈上的的每个元素都是一个 Lua 值(nil,数字,字符串,等等)。
无论何时 Lua 调用 C,被调用的函数都得到一个新的栈,这个栈独立于 C 函数本身的堆栈,也独立于以前的栈。(在 C 函数里,用 Lua API 不能访问到 Lua 状态机中本次调用之外的堆栈中的数据),它里面包含了 Lua 传递给 C 函数的所有参数,而 C 函数则把要返回的结果也放入堆栈以返回给调用者。
方便起见,所有针对栈的 API 查询操作都不严格遵循栈的操作规则。而是可以用一个索引来指向栈上的任何元素:正的索引指的是栈上的绝对位置(从一开始);负的索引则指从栈顶开始的偏移量。更详细的说明一下,如果堆栈有 n 个元素,那么索引 1 表示第一个元素(也就是最先被压入堆栈的元素)而索引 n 则指最后一个元素;索引 -1 也是指最后一个元素(即栈顶的元素),索引 -n 是指第一个元素。如果索引在 1 到栈顶之间(也就是,1 ≤ abs(index) ≤ top)我们就说这是个有效的索引。
② 从Lua脚本中获取参数
int n = lua_gettop(L);
/* get each argument */
lua_tostring(lua_State *L, int index)
…….
index: 1—左边第一个参数,2—左边第二个参数,......
③ 返回返回值
按顺序返回,Lua按照返回顺序接受
Lua_pushXXX(L,第一个返回值)
Lua_pushXXX(L,第二个返回值)
………
Lua调用C函数例子:
C程序:
static int average(lua_State *L)
{
/* get number of arguments */
int n = lua_gettop(L);
double sum = 0;
int i;
/* loop through each argument */
for (i = 1; i <= n; i++)
{
if (!lua_isnumber(L, i))
{
lua_pushstring(L, "Incorrect argument to 'average'");
lua_error(L);
}
/* total the arguments */
sum += lua_tonumber(L, i);
}
/* push the average */
lua_pushnumber(L, sum / n); //第一个返回值
/* push the sum */
lua_pushnumber(L, sum); //第二个返回值
/* return the number of results */
return 2;
}
void LuaCallC()
{
/* initialize Lua */
lua_State * L = lua_open();
/* load Lua base libraries */
luaL_openlibs(L);
/* register our function */
lua_register(L, "average", average);
/* run the script */
luaL_dofile(L, "average.lua");
/* cleanup Lua */
lua_close(L);
}
Lua脚本,average.lua:
avg, sum = average(20,40,50,60,80) ----------------为啥如此多的参数????? yang
print("The average is ", avg)
print("The sum is ", sum)
(二) Lua 从C库中调用
1. 生成C函数库
① 所有可以被Lua调用的函数必须是lua_CFunction类型
② 所有被调用的函数加入到一个luaL_reg数组中
③ 一个luaopen_*(*表示库的名称)供lu调用库时打开库
使用luaL_register(lua_State *L,
const char *libname,
const luaL_Reg *l)
libname,注册lua使用这个库时的使用名称
luaL_Reg *l,把luaL_Reg数组里的函数注册到lua栈里,供lua调用
注意:BCB默认导出的c函数前面加了下划线,因此在动态库工程中加入一个def文件,在生成时不用加下划线。内容是:
Export
FunName = _FunName (FunName表示要导出的函数名称,Lua使用的库中就是luaopen_*)
2. Lua使用c库
require(libname) – 打开使用的库
libname.FunName – 使用c库中提供的函数
Lua调用C函数库例子:
C库代码,C函数的名称”dllforlua.dll”
static int lua_msgbox(lua_State* L)
{
const char* message = luaL_checkstring(L, 1);
const char* caption = luaL_optstring(L, 2, "");
int result = MessageBox(NULL, message, caption, MB_YESNO);
lua_pushnumber(L, result);
return 1;
}
static const luaL_Reg mylib[] =
{
{"msgbox", lua_msgbox},
{NULL, NULL}
};
int __declspec(dllexport) luaopen_dllforlua(lua_State* L)
{
luaL_register(L, "dllforlua", mylib);
return 1;
}
Lua脚本,Test.lua
require(“dllforlu”)
dllforlua.msgbox("Hey, it worked!", "Lua Message Box");
(三) C调Lua 函数
1. 初始化Lua环境
Lua_open或者:lua_newstate
luaL_newstate(调用lua_newstate,并且设置了一个恐慌函数)
2. 加载Lua标准库
Lua_openlibs(打开所有标准库)
不打开所有库,打开需要的库:
Luaopen_base
……….
3. 加载Lua和函数函数
luaL_dofile()
lua_getglobal()
大小写敏感,名字于Lua脚本的函数名称大小写完全一致
4. 压入参数
不同类型采用不同的函数,按照从左往右的顺序依次压栈
lua_pushnumber,lua_pushstring,…..
5. 执行函数
lua_call, lua_pcall
6. 获取返回值
不同类型使用不同的函数,注意索引,获取前要检查类型
7. 从栈中弹出返回值
lua_pop()
8. 关闭Lua状态机
lua_close()
C程序掉用Lua函数例子:
C函数:
void CCallLua()
{
// Create a LUA VMachine
lua_State *L;
//L = luaL_newstate();
L = lua_open();
//Load Libraries
luaL_openlibs(L);
// 运行脚本 /
luaL_dofile(L, "clua.lua");
lua_getglobal(L,"Sum");
lua_pushnumber(L,2);//第一个参数
lua_pushnumber(L,3);//第二个参数
lua_pushnumber(L,4);//第三个参数
lua_pcall(L,3,2,0);
double sum=0,ave=0;
if(lua_isnumber(L,1))
{
sum=lua_tonumber(L,1);
}
if(lua_isnumber(L,2))
{
ave=lua_tonumber(L,2);
}
lua_pop(L,2);
cout<<"Sum ="<<sum
<<"/nAve ="<<ave<<endl;
// 清除Lua
lua_close(L);
getchar();
}
Lua脚本Clua.lua:
function Sum(...)
local s=0
local num=0
for k,v in pairs{...} do
s = s + v
num = k
end
return s,s/num
end