- 小白
-
你就也定义一个形参中的结构体指针接收传过来的结构体指针就OK啦
形参中的结构体指针改变?这什么意思?
给你看个例子吧:
struct node *creat(struct node *l)
{
struct node *head;
head=l;
return head;
}
你是这样吗~
- 瑞瑞爱吃桃
-
扩展Lua的基本方法之一就是为应用程序注册新的C函数到Lua中去。
当我们提到Lua可以调用C函数,不是指Lua可以调用任何类型的C函数(有一些包可以让Lua调用任意的C函数,但缺乏便捷和健壮性)。正如我们前面所看到的,当C调用Lua函数的时候,必须遵循一些简单的协议来传递参数和获取返回结果。相似的,从Lua中调用C函数,也必须遵循一些协议来传递参数和获得返回结果。另外,从Lua调用C函数我们必须注册函数,也就是说,我们必须把C函数的地址以一个适当的方式传递给Lua解释器。
当Lua调用C函数的时候,使用和C调用Lua相同类型的栈来交互。C函数从栈中获取她的参数,调用结束后将返回结果放到栈中。为了区分返回结果和栈中的其他的值,每个C函数还会返回结果的个数(the function returns (in C) the number of results it is leaving on the stack.)。这儿有一个重要的概念:用来交互的栈不是全局变量,每一个函数都有他自己的私有栈。当Lua调用C函数的时候,第一个参数总是在这个私有栈的index=1的位置。甚至当一个C函数调用Lua代码(Lua代码调用同一个C函数或者其他的C函数),每一个C函数都有自己的独立的私有栈,并且第一个参数在index=1的位置。
26.1 C 函数
先看一个简单的例子,如何实现一个简单的函数返回给定数值的sin值(更专业的实现应该检查他的参数是否为一个数字):
static int l_sin (lua_State *L) {
double d = lua_tonumber(L, 1); /* get argument */
lua_pushnumber(L, sin(d)); /* push result */
return 1; /* number of results */
}
任何在Lua中注册的函数必须有同样的原型,这个原型声明定义就是lua.h中的lua_CFunction:
typedef int (*lua_CFunction) (lua_State *L);
从C的角度来看,一个C函数接受单一的参数Lua state,返回一个表示返回值个数的数字。所以,函数在将返回值入栈之前不需要清理栈,函数返回之后,Lua自动的清除栈中返回结果下面的所有内容。
我们要想在Lua使用这个函数,还必须首先注册这个函数。我们使用lua_pushcfunction来完成这个任务:他获取指向C函数的指针,并在Lua中创建一个function类型的值来表示这个函数。一个quick-and-dirty的解决方案是将这段代码直接放到lua.c文件中,并在调用lua_open后面适当的位置加上下面两行:
lua_pushcfunction(l, l_sin);
lua_setglobal(l, "mysin");
第一行将类型为function的值入栈,第二行将function赋值给全局变量mysin。这样修改之后,重新编译Lua,你就可以在你的Lua程序中使用新的mysin函数了。在下面一节,我们将讨论以比较好的方法将新的C函数添加到Lua中去。
对于稍微专业点的sin函数,我们必须检查sin的参数的类型。有一个辅助库中的luaL_checknumber函数可以检查给定的参数是否为数字:当有错误发生的时候,将抛出一个错误信息;否则返回作为参数的那个数字。将上面我们的函数稍作修改:
static int l_sin (lua_State *L) {
double d = luaL_checknumber(L, 1);
lua_pushnumber(L, sin(d));
return 1; /* number of results */
}
根据上面的定义,如果你调用mysin("a"),会得到如下信息:
bad argument #1 to "mysin" (number expected, got string)
注意看看luaL_checknumber是如何自动使用:参数number(1),函数名("mysin"),期望的参数类型("number"),实际的参数类型("string")来拼接最终的错误信息的。
下面看一个稍微复杂的例子:写一个返回给定目录内容的函数。Lua的标准库并没有提供这个函数,因为ANSI C没有可以实现这个功能的函数。在这儿,我们假定我们的系统符合POSIX标准。我们的dir函数接受一个代表目录路径的字符串作为参数,以数组的形式返回目录的内容。比如,调用dir("/home/lua")可能返回{".", "..", "src", "bin", "lib"}。当有错误发生的时候,函数返回nil加上一个描述错误信息的字符串。
#include <dirent.h>
#include <errno.h>
static int l_dir (lua_State *L) {
DIR *dir;
struct dirent *entry;
int i;
const char *path = luaL_checkstring(L, 1);
/* open directory */
dir = opendir(path);
if (dir == NULL) { /* error opening the directory? */
lua_pushnil(L); /* return nil and ... */
lua_pushstring(L, strerror(errno)); /* error message */
return 2; /* number of results */
}
/* create result table */
lua_newtable(L);
i = 1;
while ((entry = readdir(dir)) != NULL) {
lua_pushnumber(L, i++); /* push key */
lua_pushstring(L, entry->d_name); /* push value */
lua_settable(L, -3);
}
closedir(dir);
return 1; /* table is already on top */
}
辅助库的luaL_checkstring函数用来检测参数是否为字符串,与luaL_checknumber类似。(在极端情况下,上面的l_dir的实现可能会导致小的内存泄漏。调用的三个Lua函数lua_newtable、lua_pushstring和lua_settable可能由于没有足够的内存而失败。其中任何一个调用失败都会抛出错误并且终止l_dir,这种情况下,不会调用closedir。正如前面我们所讨论过的,对于大多数程序来说这不算个问题:如果程序导致内存不足,最好的处理方式是立即终止程序。另外,在29章我们将看到另外一种解决方案可以避免这个问题的发生)
26.2 C 函数库
一个Lua库实际上是一个定义了一系列Lua函数的chunk,并将这些函数保存在适当的地方,通常作为table的域来保存。Lua的C库就是这样实现的。除了定义C函数之外,还必须定义一个特殊的用来和Lua库的主chunk通信的特殊函数。一旦调用,这个函数就会注册库中所有的C函数,并将他们保存到适当的位置。像一个Lua主chunk一样,她也会初始化其他一些在库中需要初始化的东西。
Lua通过这个注册过程,就可以看到库中的C函数。一旦一个C函数被注册之后并保存到Lua中,在Lua程序中就可以直接引用他的地址(当我们注册这个函数的时候传递给Lua的地址)来访问这个函数了。换句话说,一旦C函数被注册之后,Lua调用这个函数并不依赖于函数名,包的位置,或者调用函数的可见的规则。通常C库都有一个外部(public/extern)的用来打开库的函数。其他的函数可能都是私有的,在C中被声明为static。
当你打算使用C函数来扩展Lua的时候,即使你仅仅只想注册一个C函数,将你的C代码设计为一个库是个比较好的思想:不久的将来你就会发现你需要其他的函数。一般情况下,辅助库对这种实现提供了帮助。luaL_openlib函数接受一个C函数的列表和他们对应的函数名,并且作为一个库在一个table中注册所有这些函数。看一个例子,假定我们想用一个我们前面提过的l_dir函数创建一个库。首先,我们必须定义库函数:
static int l_dir (lua_State *L) {
... /* as before */
}
第二步,我们声明一个数组,保存所有的函数和他们对应的名字。这个数组的元素类型为luaL_reg:是一个带有两个域的结构体,一个字符串和一个函数指针。
static const struct luaL_reg mylib [] = {
{"dir", l_dir},
{NULL, NULL} /* sentinel */
};
在我们的例子中,只有一个函数l_dir需要声明。注意数组中最后一对必须是{NULL, NULL},用来表示结束。第三步,我们使用luaL_openlib声明主函数:
int luaopen_mylib (lua_State *L) {
luaL_openlib(L, "mylib", mylib, 0);
return 1;
}
luaL_openlib的第二个参数是库的名称。这个函数按照指定的名字创建(或者reuse)一个表,并使用数组mylib中的name-function对填充这个表。luaL_openlib还允许我们为库中所有的函数注册公共的upvalues。例子中不需要使用upvalues,所以最后一个参数为0。luaL_openlib返回的时候,将保存库的表放到栈内。luaL_openlib函数返回1,返回这个值给Lua。(The luaopen_mylib function returns 1 to return this value to Lua)(和Lua库一样,这个返回值是可选的,因为库本身已经赋给了一个全局变量。另外,像在Lua标准库中的一样,这个返回不会有额外的花费,在有时候可能是有用的。)
完成库的代码编写之后,我们必须将它链接到Lua解释器。最常用的方式使用动态连接库,如果你的Lua解释器支持这个特性的话(我们在8.2节已经讨论过了动态连接库)。在这种情况下,你必须用你的代码创建动态连接库(windows下.dll文件,linux下.so文件)。到这一步,你就可以在Lua中直接使用loadlib加载你刚才定义的函数库了,下面这个调用:
mylib = loadlib("fullname-of-your-library", "luaopen_mylib")
将luaopen_mylib函数转换成Lua中的一个C函数,并将这个函数赋值给mylib(那就是为什么luaopen_mylib必须和其他的C函数有相同的原型的原因所在)。然后,调用mylib(),将运行luaopen_mylib打开你定义的函数库。
如果你的解释器不支持动态链接库,你必须将你的新的函数库重新编译到你的Lua中去。除了这以外,还不要一些方式告诉独立运行的Lua解释器,当他打开一个新的状态的时候必须打开这个新定义的函数库。宏定义可以很容易实现这个功能。第一,你必须使用下面的内容创建一个头文件(我们可以称之为mylib.h):
int luaopen_mylib (lua_State *L);
#define LUA_EXTRALIBS { "mylib", luaopen_mylib },
第一行声明了打开库的函数。第二行定义了一个宏LUA_EXTRALIBS作为函数数组的新的入口,当解释器创建新的状态的时候会调用这个宏。(这个函数数组的类型为struct luaL_reg[],因此我们需要将名字也放进去)
为了在解释器中包含这个头文件,你可以在你的编译选项中定义一个宏LUA_USERCONFIG。对于命令行的编译器,你只需添加一个下面这样的选项即可:
-DLUA_USERCONFIG="mylib.h"
(反斜线防止双引号被shell解释,当我们在C中指定一个头文件时,这些引号是必需的。)在一个整合的开发环境中,你必须在工程设置中添加类似的东西。然后当你重新编译lua.c的时候,它包含mylib.h,并且因此在函数库的列表中可以用新定义的LUA_EXTRALIBS来打开函数库。
- bikbok
-
不熟
为什么lua语言中使用全局变量就会造成内存泄漏
到知乎中问2023-06-09 20:57:164
lua 怎么判断有没有一个全局变量
直接require那个lua文件,然后调用。2023-06-09 20:57:252
为什么lua语言中使用全局变量就会造成内存泄漏呢??
会导致多线程不安全。在创建多个lua虚拟机的时候会2个线程同时操作一个变量。这是你代码设计问题。不一定会造成泄露。2023-06-09 20:57:342
lua的基本语法
关键字 关键字就好比java中的 break if else等等一样的效果。lua的关键字如下: 定义变量 全局变量,默认的情况下,定义一个变量都是全局变量, 如果要用局部变量 需要声明为local.例如: 如果变量没有初始化:则 它的值为nil 这和java中的null不同。 Lua中的数据类型 流程控制 如下:类似于if else 函数 lua中也可以定义函数,类似于java中的方法。例如: require 函数 require 用于 引入其他的模块,类似于java中的类要引用别的类的效果。 用法:2023-06-09 20:57:471
Lua和C如何交互 堆栈和全局表概念解析
Lua的设计里面,table是作为容器出现的,它不仅是数据,是对象。也是代码的运行环境。每个lua的function都有且仅有一个环境关联(可以参考setfenv, getfenv),你的lua文件也可以被视为一个大的匿名函数被执行,它的关联环境就是_G。在函数中访问的变量可以分为4种,参数,函数内local,upvalue和所谓的全局变量,参数和内部local都属于函数自身不谈,如果读取的变量不在其中,Lua会查询upvalues中是否存在,存在就使用,这也是闭包的原理,如果依然不存在,Lua会查询函数所关联的环境,通常是_G,所谓全局变量也就是_G本身的字段罢了。那么,反过来看写入值,同样,如果变量名在参数,内部local或者upvalue内,那么自然可以保存在对应的变量内,如果不在,那么自然就应该保存在关联环境的字段中。特别的,函数的环境是可以改变的,通过setfenv(Lua 5.1)或者_ENV(Lua 5.2) 调整了函数的环境后,你可以看到所谓全局变量并不会被保存在_G中,也不会被其他环境是_G的函数读取到。所以,全局变量在Lua中算是一个伪概念,不过对于新手来说会更容易接受。补充: 上面只提及了全局变量的实际概念,倒是没提及它的好处。关于共享数据之类,使用闭包好还是全局好,都是视需求来说,这点也谈不上什么好处。我就从不少人认为Lua的全局变量的坏处部分聊聊,对于大型项目来说,都是按功能分别开发的,为了避免不同模组的变量冲突,就需要要求各个模组仅申明local变量,漏申明什么的,可能造成的问题都很难debug到。(至于使用local形成闭包提高函数运行速度啥的,通常使用脚本的地方并没有追求性能到这个地步,何况大数据处理也都有其他优化的办法。)实际如果希望每个模组不受其他的代码干扰,就应该为每个模组构建沙盒环境,比如我在其它回答中贴过的一段代码:setfenv(1, setmetatable({}, {__index = function(self, key)local val = _G[key] or error("The global " .. key .. " can"t be found.", 2)rawset(self, key, val)return valend}))print(ture) -- Error: the global ture can"t be found.使用setfenv或者_ENV调整当前代码的运行环境后,就拥有了一个完整的沙盒环境,并且还自带拼写错误检查和自动缓存。(当然实际项目使用的代码会复杂的多)使用沙盒环境后,全局变量冲突的情况已经避免了,但还谈不上好处,只能说和使用local变量持平。再进一步来看,沙盒环境能带给我们什么,全局变量的读写(还不存在时)在这个私有环境中是可控的,上面的例子读取,这个还体现不出特别的价值。但写可控可以做的文章很多,其中一种是Lua版decorator :_ENV = Module "Test" -- 启用沙盒环境-- 注册一个修饰器,这个修饰器将函数封装为协程调用方式__Async__()-- 沙盒环境获知DoTask被定义,调用注册的修饰器进行封装,-- 返回的封装函数被保存在沙盒环境的DoTask字段中function DoTask()for i = 1, 10 doprint(i)-- 伪Sleep,停止当前协程,等待1秒继续Task.Sleep(1)endend-- 调用DoTaskDoTask()通过这种技术,我们可以实现很多的语言特性,例如参数验证,重载,debug(记录调用堆栈信息)等。这些都是依赖伪全局变量这个概念才能实现。当然这个话题扯得有点远了,不是大型项目也没必要做到这个地步。2023-06-09 20:58:021
程序开发中模块的理解是什么?
程序开发中模块的理解是什么。程序模块即可由汇编程序、编译程序、装入程序或翻译程序作为一个整体来处理的一级独立的、可识别的程序指令。模块是大型程序指令的一个组成部分。 在Windows中,术语“模块”一般是指任何能被装入内存中运行的可执行代码和数据的集合。更明确地讲,模块指的就是一个.EXE文件(又称为应用程序模块),或一个动态链接库,或一个设备驱动程序,也可能是一个程序包含的能被另一个程序存取的数据资源。模块一词也被用于特指自包含的一段程序。简单来说,模块即可以说是功能的一部分,比如通信模块,也可以是一段程序,比如常量模块。2023-06-09 20:58:121
如何在lua程序中调用另外一个.lua文件中的全局变量或函数?
直接require那个lua文件,然后调用。2023-06-09 20:58:191
lua如何实现 静态变量,多次调用同一个函数时,只初始化一次指定的变量值 没啥分,谢谢帮忙
1、把内容单独放一个文件里lua文件里local i = 0function test() if (i<5) then i=i+1 end;end;2、或者写个生成函数function create_test() local i = 0return function() if(i<5) then i=i+1 end endendtest = create_test()然后多次调test就行了。2023-06-09 20:58:261
lua 里面 local a = a,是在内存里复制了一份a,还是制造了a的“快捷方式”?
首先你得注意一下这行运行时 等号两边两个不同的a的意思local a = xxx表示把xxx赋值给a 但是这时候刚好这个xxx写的又是a但是a必须在定义之后 才会指向本地的变量 所以此时执行a=a是声明本地变量a 并且用全局变量a来赋值给他 以上是第一个要注意的,也就是等号两边两个a 指向的是不同东西 一个是局部变量,一个是全局变量第二个要注意的就是lua里的赋值lua中,基本类型(number boolean string nil)是值赋值 而其他类型(coroutine table function userdata)是引用赋值所谓的引用赋值就是 把两个变量指向同一份地址(也可以说两个变量就是两个指针,也就是你所谓的快捷方式),不同于值赋值 值赋值的改变,相互之间是独立的,而引用赋值相互之间是会影响的2023-06-09 20:58:461
Lua 语言有哪些不足?
1.全局变量和局部变量2.没有continue2023-06-09 20:58:532
lua有关于local和_G的区别
因为默认的_G就是指向全局根据你的需求 就只能给当前全局_G设置一个元表,赋予新值的时候 抛出异常再产生一个_G表 指向原来的全局变量2023-06-09 20:59:031
Linux lua中require "bit"
为了方便代码管理,通常会把lua代码分成不同的模块,然后在通过require函数把它们加载进来。现在看看lua的require的处理流程。函数原型:require(modname) ---modname ---->加载的模块名称首先Lua提供高级的require函数来加载运行库。粗略的说require和dofile完成同样的功能但有两点不同:1、require会搜索目录加载文件2、require会判断是否文件已经加载避免重复加载同一文件。由于上述特征,require在Lua中是加载库的更好的函数。require函数实现了不同lua文件的加载,类似于C++中的include,java中的import。 require使用的路径和普通的路径还是有些区别,我们一般见到的路径都是一个目录列表。require的路径是一个模式列表,每一个模式指明一种由虚文件名(require的参数modname)转成实文件名的方法。更明确地说,每一个模式是一个包含可选的问号(?)的文件名。匹配的时候Lua会首先将问号用虚文件名替换,然后看是否有这样的文件存在。如果不存在继续用同样的方法用第二个模式匹配。例如,路径如下:?; ?.lua; c:windows?; /usr/local/lua/?/?.lua调用require("add")时会试着打开以下这些文件:addadd.luac:windowsadd/usr/local/lua/add/add.lua为了确定路径,Lua首先检查全局变量LUA_PATH是否为一个字符串,如果是则认为这个串就是路径;否则require检查环境变量LUA_PATH的值,如果两个都失败require使用固定的路径(典型的"?;?.lua").require函数的实现原理如下:--require 函数的实现 function require(name)if not package.loaded[name] thenlocal loader = findloader(name) //这一步演示在代码中以抽象函数findloader来表示if loader == nil thenerror("unable to load module" .. name)endpackage.loaded[name] = truelocal res = loader(name)if res ~= nil thenpackage.loaded[name] = resendendreturn package.loaded[name] end require(在lua中它是ll_require函数)函数会在路径中搜索输入的文件路径,大致流程如下:1、package.loaded一个用于控制哪些模块已经加载的表,该表由require使用。当require一个模块名为modname的模块且package.loaded[modname]不为false时,require仅返回package.loaded[modname]存储的值.2、 package.preload为特定模块存储加载器的一个表。查找modname, 如果preload存在,那么就把它作为loader,调用loader(L)3、package.path查找lua库modname,这个库是通过module函数定义的,对于顶层的lua库,文件名和库名是一 样的而且不需要调用显式地在lua文件中调用module函数(在ll_require函数中可以看到处理方式),也就是说lua会根据lua文件直接完 成一个loader的初始化过程。4、package.cpath(实现lua调用C函数)查找c库,这个库是符合lua的一些规范的(export具有一定特征的函数接口),lua先已动态的方式加载该c库(.so),然后在库中查找并调用相应名字的接口,例如:luaopen_hello_world5、已第一个"."为分割,将模块名划分为:(main, sub)的形式,根据package.cpath查找main,如果存在,就加载该库并查询相应的接口:luaopen_main_sub,例如:先查找 hello库,并查询luaopen_hello_world接口6、得到loder后,用modname作为唯一的参数调用该loader函数。当然参数是通过lua的栈传递的,所以loader的原型必须符合lua的规范:int LUA_FUNC(lua_State *L)ll_require会将这个loader的返回值符给package.loaded[modelname],如果loader不返回值同时 package.loaded[modelname]不存在时, ll_require就会把package.loaded[modelname]设为true。最后ll_reuqire把package.loaded [modelname]返回给调用者。require的另一个功能是避免重复加载同一个文件两次。Lua保留一张所有已经加载的文件的列表(使用table保存)。如果一个加载的文件在表中存在require简单的返回;表中保留加载的文件的虚名,而不是实文件名。所以如果你使用不同的虚文件名require同一个文件两次,将会加载两次该文件。比如require "foo"和require "foo.lua",路径为"?;?.lua"将会加载foo.lua两次。我们也可以通过全局变量_LOADED访问文件名列表,这样我们就可以判断文件是否被加载过;同样我们也可以使用一点小技巧让require加载一个文件两次。比如,require "foo"之后_LOADED["foo"]将不为nil,我们可以将其赋值为nil,require "foo.lua"将会再次加载该文件。2023-06-09 20:59:101
lua鼠标宏代码
罗技鼠标宏压枪lua脚本local,fireStatusfalse设置一个全局变量,控制是否启用该功能SetBacklightColor0,0,255设置背光。2023-06-09 20:59:171
lua的_G表是在堆内存还是在全局区
默认全局。因为默认的_G就是指向全局根据你的需求就只能给当前全局_G设置一个元表,赋予新值的时候抛出异常再产生一个G表指向原来的全局变量。定义一个localtable={value=0}然后使用。table.insert插入到另外一个table2中,重复插入2次这时候table2中有3个元素令table.value=2然后通过table2[1].valuetable2[2].valuetable2[3].value这种形式打印出来的值居然都成了2去了函数体外面后打印也是2难道table2中实际存的是table的地址。然后该内存还是在堆上创建的。如果是地址或者引用那为什么出了函数体这个内存没有被回收掉。2023-06-09 20:59:241
Lua怎么和字符串指针兼容
扩展Lua的基本方法之一就是为应用程序注册新的C函数到Lua中去。 当我们提到Lua可以调用C函数,不是指Lua可以调用任何类型的C函数(有一些包可以让Lua调用任意的C函数,但缺乏便捷和健壮性)。正如我们前面所看到的,当C调用Lua函数的时候,必须遵循一些简单的协议来传递参数和获取返回结果。相似的,从Lua中调用C函数,也必须遵循一些协议来传递参数和获得返回结果。另外,从Lua调用C函数我们必须注册函数,也就是说,我们必须把C函数的地址以一个适当的方式传递给Lua解释器。 当Lua调用C函数的时候,使用和C调用Lua相同类型的栈来交互。C函数从栈中获取她的参数,调用结束后将返回结果放到栈中。为了区分返回结果和栈中的其他的值,每个C函数还会返回结果的个数(the function returns (in C) the number of results it is leaving on the stack.)。这儿有一个重要的概念:用来交互的栈不是全局变量,每一个函数都有他自己的私有栈。当Lua调用C函数的时候,第一个参数总是在这个私有栈的index=1的位置。甚至当一个C函数调用Lua代码(Lua代码调用同一个C函数或者其他的C函数),每一个C函数都有自己的独立的私有栈,并且第一个参数在index=1的位置。 26.1 C 函数 static int l_sin (lua_State *L) { double d = lua_tonumber(L, 1); /* get argument */ lua_pushnumber(L, sin(d)); /* push result */ return 1; /* number of results */}任何在Lua中注册的函数必须有同样的原型,这个原型声明定义就是lua.h中的lua_CFunction: typedef int (*lua_CFunction) (lua_State *L); 从C的角度来看,一个C函数接受单一的参数Lua state,返回一个表示返回值个数的数字。所以,函数在将返回值入栈之前不需要清理栈,函数返回之后,Lua自动的清除栈中返回结果下面的所有内容。 我们要想在Lua使用这个函数,还必须首先注册这个函数。我们使用lua_pushcfunction来完成这个任务:他获取指向C函数的指针,并在Lua中创建一个function类型的值来表示这个函数。一个quick-and-dirty的解决方案是将这段代码直接放到lua.c文件中,并在调用lua_open后面适当的位置加上下面两行: lua_pushcfunction(l, l_sin); lua_setglobal(l, "mysin"); 第一行将类型为function的值入栈,第二行将function赋值给全局变量mysin。这样修改之后,重新编译Lua,你就可以在你的Lua程序中使用新的mysin函数了。在下面一节,我们将讨论以比较好的方法将新的C函数添加到Lua中去。 对于稍微专业点的sin函数,我们必须检查sin的参数的类型。有一个辅助库中的luaL_checknumber函数可以检查给定的参数是否为数字:当有错误发生的时候,将抛出一个错误信息;否则返回作为参数的那个数字。将上面我们的函数稍作修改:2023-06-09 20:59:311
关于事件监听机制(lua)或监听发射机制(js)简记
对于事件和监听 我一直没有啥感觉 经历过2个项目 全部是使用的 注册全局对象来直接调用函数的 比如玩家 g_player 比如地图 g_bgMap 但是一个朋友 一直使用的是事件监听框架 通知和回调2种方法。。。贯穿他整个框架 cocos通知有2种 一种是需要节点传递的。。。一般要addChild后才能收到 ui消息基于这一种 一种是全局的。。创建一个node放在内存。。注册全局的回调。。把this放进去。。就ok了。。。我网络消息基于这一种。。。 或者 cc.director.on cc.director.emit 这样整个客户端框架就是基于通知的。。。 这样整个框架很干净了。。 没有多余需要保存的全局变量了。。甚至页面也不需要保存指针了 而我每次都习惯直接用全局变量去解决问题 比如要监听一个进度条滑动 如果按监听的做法 可能是在scrollview里面 注册一个监听 一旦有scrollview的滑动 就发射事件 通知滑动条改变位置 但是我的做法是直接给滑动条注册一个全局指针 g_foo 然后scrollview的滑动时 直接 g_foo.setBarPos 或者注册一个回调 父节点个子节点注册一个回调 子节点接受到注册后保存起来 调用时 父节点最后收到回调 处理逻辑2023-06-09 20:59:391
如何在C++中集成LUA脚本
1. 创建Lua引擎 LuaWrap lua; 或者 LuaWrap* lua = new LuaWrap; 创建一个LuaWrap对象,就是创建一个Lua脚本引擎。并且根据Lua的特性,你可以创建任意多个Lua引擎,甚至可以分布在不同的线程当中。2. 装载并执行脚本程序 你可以从缓冲区中装载Lua脚本: lua.LoadString( "print("Hello World")" ); 当然,你也可以从文件中装入,并执行Lua脚本: Lua.LoadFile("./test.lua"); Lua的脚本,可以是源代码,也可以经过编译后的中间代码。也许你对编译后的中间代码更感兴趣——如果你不希望让源代码赤裸裸的袒露在大家的眼前。3. 获取和设置Lua变量 能够获取和设置脚本变量的内容,是一个最基本的功能。你可以使用GetGlobal和SetGlobal函数来做到这一点: (1) 获取变量: int a = lua.GetGlobal<int>("a"); LuaTable table = lua.GetGlobal<LuaTable>("t"); 这里,<> 里头的类型,就是想要的变量的类型。 (2) 设置变量: lua.SetGlobal("a", a); lua.SetGlobal("t", table);4. 调用Lua函数 使用Call函数,就可以很简单的从你的程序中调用Lua函数: lua.Call<void>("print", "Hello World"); int sum = lua.Call<int>("add", 2, 3); 这里,<> 里头的类型是返回值的类型。5. 如何让Lua也能调用C++的函数 精采的地方来了。假如有下面这样的一个函数: int add(int a, int b) { return a + b; } 如果想让它能够让Lua使用,只需将它注册到Lua引擎当中就可以了: lua.RegisterFunc("add", int(int,int), add); 这样,Lua中就可以用直接使用了: (Lua脚本)sum = add(1, 3) (*) RegisterFunc的功能,就是让你把C++的函数注册到Lua中,供Lua脚本使用。 第一个参数,是想要在Lua中用的函数名。 第二个参数,是C++中函数的原型; C++允许函数重载的,你可以使用函数原型,来选择需要注册到Lua引擎中的那个函数。 第三个参数,就是C++中函数的指针了。6. 如何能让C++的类在Lua中使用 我们先看看下面这个C++类:class MyArray{ std::vector<double> array;public: void setvalue(int index, double value); double getvalue(int index); int size(); const char* ToString();}; 你准备要让Lua能够自由访问并操作这个类。很简单,你只需增加几个宏定义就可以了:class MyArray{ std::vector<double> array;public: void setvalue(int index, double value); double getvalue(int index); int size(); const char* ToString(); // 将一个 class 作为一个 Lua 对象是很容易的,只需要增加以下宏定义。 DEFINE_TYPENAME("My.array"); BEGIN_REGLUALIB("array") LUALIB_ITEM_create("new", MyArray ) // 创建MyArray (注:由于发表的原因,create应为全部大写) LUALIB_ITEM_DESTROY("del", MyArray ) // 消除MyArray。 END_REGLUALIB() BEGIN_REGLUALIB_MEMBER() LUALIB_ITEM_FUNC("size", int (MyArray*), &MyArray::size) LUALIB_ITEM_FUNC("__getindex", double(MyArray*, int), &MyArray::getvalue) LUALIB_ITEM_FUNC("__newindex", void (MyArray*, int, double), &MyArray::setvalue) LUALIB_ITEM_FUNC("__tostring", const char* (MyArray*), &MyArray::ToString) LUALIB_ITEM_DESTROY("__gc", MyArray ) // 垃圾收集时消除对象用。 END_REGLUALIB_MEMBER()}; 只要有了这些宏定义,这个类就是可以在Lua中使用的类了,我们就可以在Lua中注册这个类了: lua.Register<MyArray>() 这样注册以后,我们在Lua中就可以使用这个类了: a = array.new() -- 创建对象,相当于 a = new Myarray a[1] = 10 -- 调用__newindex,也就是C++中的 a->setvalue(1, 10) a[2] = 20 -- 调用__newindex,也就是C++中的 a->setvalue(2, 20) print( a, -- 调用 __tostring,也就是C++中的 a->ToString() a:size(), -- 相当于C++中的 a->size() a[1], -- 调用__getindex,也就是C++中的a->getvalue(1) a[2]) --调用__getindex,也就是C++中的a->getvalue(2) array.del(a) -- 清除对象,相当于 delete a a = nil -- 清空 a,很象C++中的 a = NULL 当然,你也可以不用del这个对象,而是等待Lua帮你自动进行垃圾回收。在Lua进行垃圾回收时,它会自动调用这个对象的 __gc ,相当于 delete。 那么,在C++中要创建MyArray对象,并且传递给Lua全局变量怎么办?就象前面讲过的一样,使用SetGlobal: MyArray* a = new MyArray; lua.SetGlobal("a", a); 要获取该对象,同样的,应该使用GetGlobal: MyArray* a = lua.GetGlobal<MyArray>("a"); 对于传递给Lua的对象,就让Lua来管理该对象的生存周期好了。如果你非要删除它的话,你可以使用DelGlobalObject: lua.DelGlobalObject<MyArray>("a"); 不过这么做的话,你应当明白你在做什么,因为在Lua的脚本中,可能已经在多处引用了这个对象了。删除了其中一个,将导致其它引用对象失效,从而可能引致系统崩溃。 (1) DEFINE_TYPENAME("My.array"); 定义类型的名称。在Lua中,这个类型名称是唯一用来识别C++类型的,你必须为不同的对象给予不同的名称。 (2) BEGIN_REGLUALIB("array") … END_REGLUALIB() 你可以为一个对象定义一个程序库,"array"就是程序库的名字。在程序库中定义的函数是全局函数,在Lua中,使用该函数,需要在函数前加上库的名字,如:array.new()。通常,程序库会包含创建对象的方法。如: LUALIB_ITEM_create("new", MyArray ) // 创建MyArray (注:由于发表的原因,create应为全部大写) 这样子,你才能在Lua中创建MyArray: a = array.new() 你也可以选择增加一个删除对象操作: LUALIB_ITEM_DESTROY("del", MyArray ) // 删除MyArray 这样,你就可以直接删除一个对象了: array.del(a) (3) BEGIN_REGLUALIB_MEMBER() …END_REGLUALIB_MEMBER() 在此处,你可以定义对象的成员函数,也可以重载对象的操作符——是的,就象C++的operator重载。例如: LUALIB_ITEM_FUNC("__newindex", void (MyArray*, int, double), &MyArray::setvalue) 就是重载 operator[] 操作符。Lua中可重载的操作符还有许多,如: __getindex:操作符[],支持读取访问,如 v = a[10] __newindex:操作符[],支持赋值访问,如 a[10] = 1.22 __tostring:将变量转换成字串__add:等同于operator + __add:操作符 + __sub:操作符 – __mul:操作符 × __div:操作符 ÷ __pow:操作符 ^ (乘方) __unm:一元操作符 – __concat:操作符 .. (字符串连接) __eq:操作符 == (a ~= b等价于 not a == b) __lt:操作符 < (a > b 等价于 b < a) __le:操作符 <= (a >= b 等价于 b <= a,要注意的是,如果没有定义"__le",则Lua将会尝试将a<=b 转换成 not (b < a) ) __gc:在垃圾回收时调用此函数,相当于C++的析构函数。强烈建议定义此操作符,以免造成内存泄漏等情况。比如: LUALIB_ITEM_DESTROY("__gc", MyArray ) // 垃圾收集时消除对象用。 (注) 这里要说明一下,在lua中,访问索引操作符是__index,不是__getindex,在luaWrapper库中,为了方便使用,将其映射为__getindex,同时,对__index的定义将会被忽略。2023-06-09 20:59:451
lua for 和 local的区别是什么,还有local详细的使用方法以及资料介绍
_G 指的是全局变量表local _G = _G 就是声明一个同名的局部变量 _G 并把值赋为 全局变量 _G在这个之后,在 local _G 的作用域里, _G 指的都会是局部变量local _ 就是声明一个局部变量而已.具体的提示是:Message: [string "for i,s in ipairs(bl)do _G["B"..i]=Cb(i,s)C..."]:1: bad argument #1 to "ipairs" (table expected, got nil)Time: 09/19/12 07:03:02Count: 4Stack: [C]: in function `ipairs"[string "for i,s in ipairs(bl)do _G["B"..i]=Cb(i,s)C..."]:1: in main chunk[C]: in function `RunScript"InterfaceFrameXMLChatFrame.lua:2131: in function `?"InterfaceFrameXMLChatFrame.lua:4358: in function `ChatEdit_ParseText"InterfaceFrameXMLChatFrame.lua:4052: in function `ChatEdit_SendText"InterfaceFrameXMLChatFrame.lua:2727: in function <InterfaceFrameXMLChatFrame.lua:2720>[C]: ?[C]: in function `UseAction"InterfaceFrameXMLSecureTemplates.lua:275: in function `handler"InterfaceFrameXMLSecureTemplates.lua:560: in function <InterfaceFrameXMLSecureTemplates.lua:508>Locals: (*temporary) = nil(*temporary) = "table expected, got nil"= <function> defined =[C]:-12023-06-09 21:00:031
如何解析Lua中的table数据
调用 GetLuaVar(luastate,"Project.A.one") 把Project.A.one的值取到堆栈上,然后根据类型自己用 lua_tonumber(luastate, -1) 或 lua_tostring(luastate, -1)得数据void GetLuaVar(lua_State* pLuaState,const char* strVarName){const char* pIndS = strVarName;const char* pIndE = strVarName;T_B8 bParts = false;//判断是否分两部分,因为第一部分要用lua_getglobal,其他部分用lua_gettablewhile( 0 != *pIndE ){if( "." == *pIndE ){if(bParts){lua_pushstring(pLuaState,std::string(pIndS,pIndE - pIndS).c_str());lua_gettable(pLuaState,-2);lua_remove(pLuaState,-2);}else{lua_getglobal(pLuaState,std::string(pIndS,pIndE - pIndS).c_str());bParts = true;}++pIndE;pIndS = pIndE;}else{++pIndE;}}if(bParts){lua_pushstring(pLuaState,std::string(pIndS,pIndE - pIndS).c_str());lua_gettable(pLuaState,-2);lua_remove(pLuaState,-2);}else{lua_getglobal(pLuaState,std::string(pIndS,pIndE - pIndS).c_str());}}2023-06-09 21:00:101
Lua 有些字母前加的下划线是什么
替人垂泪到天明。。菩提寺禁裴迪(王维)2023-06-09 21:00:182
lua脚本有静态局部变量吗
静态变量的修饰关键字是static,static可以作用于变量以及函数。由static修饰的,可分为静态局部变量,静态全局变量,静态函数。以使用lua内置的next来判断; if next(a) == 0 then ; 4,应该尽量使用 local 变量而非 global 变量。这是 Lua 初学者最容易犯的错误。2023-06-09 21:00:271
如何让LUA的Amount数值记住在原来基础自动加上1
如果希望每次访问 Amount 的时候让它自增,可以使用__index 但是如果 Amount是全局变量的话,最好就不要这样做了,因为这样的话你就要监听_G,这个的代价相对还是比较大的,所以你最好把amount放到一个表里,比如我放到tab里具体代码如下tab = {}_tab = {}_tab.amount = 0meta = {__index = function(t,k)if k == "amount" then_tab.amount = _tab.amount + 1return _tab.amountelsereturn _tab[k]endend,__newindex = function(t,k,v)_tab[k] = vend}setmetatable(tab,meta)tab.amount = 4print(tab.amount)print(tab.amount)print(tab.amount)实现的过程是建立一个映射表_tab给表tab创建访问和创建原表,对应操作_tab里的元素对应的访问和创建规则就可以你自己定了 有什么疑惑的话可以继续提问,我会跟进,但不保证多久跟进一次2023-06-09 21:00:341
lua中table能否直接赋值
在一个文件中,可以调用在他前面声明的全局变量,并且可以修改别的问题就是,asd要不是二维数组,那么asd[1]只有两个字符,装不下这么多字符的2023-06-09 21:00:503
再lua里一个地方局部变量过多怎么办
2022年6月20日lua代码优化(转) 暂时转了别人一篇,以后再优化 1.使用局部变量local 这是最基础也是最有用的策略,虽然使用全局变量并不能完全避免,但还是.2023-06-09 21:00:572
LUA语言入门
已经发送,发件人:Tulies、 346461**2@qq.com时 间:2012年4月12日(星期四) 下午5:082023-06-09 21:01:043
C++怎么传递一个数组到LUA
lua里的table和C++里的数组差别比较大,我们并不能直接把表简单的传过去,所以我们要把数组转化成一个table里可以存储的方式,因此我们必须遍历一下C++里的数组。2023-06-09 21:01:143
求《Lua程序设计(第2版)》中文版 电子书
什么呀?怎么搞的啊2023-06-09 21:01:441
postgresql 怎么添加聚合索引
sysbench原来自带的lua数据装载脚本是使用以下方式串行装载的,速度比较慢(比单条insert快,但是比COPY慢)。insertintotable1values(),(),().insertintotable2values(),(),().insertintotablenvalues(),(),().使用prepare导入数据的用法举例./sysbench_pg--test=lua/oltp.lua--db-driver=pgsql--pgsql-host=127.0.0.1--pgsql-port=1921--pgsql-user=postgres--pgsql-password=postgres--pgsql-db=postgres--oltp-tables-count=64--oltp-table-size=1000000--num-threads=64prepareprepare表示装载数据,但是它串行的。sysbench0.5中可以在命令行中指定测试时启动的并行线程数,这个测试过程是使用run命令,而且是多线程并发的,所以我们可以使用sysbench的run命令来造数据,而不再使用其提供的prepare命令的方法来造数据。run命令会根据命令行参数--num-threads来指定并发线程数的多少。在sysbench中自定义的lua脚本中要求实现以下几个函数:functionthread_init(thread_id):此函数在线程创建后只被执行一次functionevent(thread_id):每执行一次就会被调用一次。由上可以知道,本次造数据的脚本我们只需要实现thread_init()函数就可以了。生成测试数据的脚本沿用老唐提供的代码:#include#include#include#include#includeuint64_tmy_rand(structrandom_data*r1,structrandom_data*r2){uint64_trand_max=100000000000LL;uint64_tresult;uint32_tu1,u2;random_r(r1,&u1);random_r(r2,&u2);result=(int64_t)u1*(int64_t)u2;result=result%rand_max;returnresult;}intmain(intargc,char*argv[]){structtimevaltpstart;structrandom_datar1,r2;inti;intr;intmax_value;charrand_state1[128];charrand_state2[128];if(argc!=2){printf("Usage:%s ",argv[0]);return1;}max_value=atoi(argv[1]);gettimeofday(&tpstart,NULL);initstate_r(tpstart.tv_usec,rand_state1,sizeof(rand_state1),&r1);srandom_r(tpstart.tv_usec,&r1);gettimeofday(&tpstart,NULL);initstate_r(tpstart.tv_usec,rand_state2,sizeof(rand_state1),&r2);srandom_r(tpstart.tv_usec,&r2);for(i=1;i>sbtest"..table_id..".dat&")os.execute("catsbtest"..table_id..".dat|psql-h"..pgsql_host.."-p"..pgsql_port.."-U"..pgsql_user.."-d"..pgsql_db.."-c"copysbtest"..table_id.."fromstdinwithcsv"")os.execute("rm-fsbtest"..table_id..".dat")endfunctioncreate_index(table_id)db_query("selectsetval("sbtest"..table_id.."_id_seq","..(oltp_table_size+1)..")")db_query("CREATEINDEXk_"..table_id.."onsbtest"..table_id.."(k)")endfunctionthread_init(thread_id)set_vars()print("threadprepare"..thread_id)fori=thread_id+1,oltp_tables_count,num_threadsdocopydata(i)create_index(i)endendfunctionevent(thread_id)os.exit()end用法,必须把psql放到路径中,因为lua中需要用到psql命令exportPATH=/home/digoal/pgsql9.5/bin:$PATH生成数据,速度比以前快多了./sysbench_pg--test=lua/copy.lua--db-driver=pgsql--pgsql-host=127.0.0.1--pgsql-port=1921--pgsql-user=postgres--pgsql-password=postgres--pgsql-db=postgres--oltp-tables-count=64--oltp-table-size=1000000--num-threads=64 un清除数据,droptable./sysbench_pg--test=lua/copy.lua--db-driver=pgsql--pgsql-host=127.0.0.1--pgsql-port=1921--pgsql-user=postgres--pgsql-password=postgres--pgsql-db=postgres--oltp-tables-count=64--oltp-table-size=1000000--num-threads=64cleanuplua全局变量代码:sysbench/scripting/lua/src/lua.h:#definelua_register(L,n,f)(lua_pushcfunction(L,(f)),lua_setglobal(L,(n)))sysbench/scripting/lua/src/lua.h:#definelua_setglobal(L,s)lua_setfield(L,LUA_GLOBALSINDEX,(s))sysbench/scripting/lua/src/lbaselib.c:lua_setglobal(L,"_G");sysbench/scripting/lua/src/lbaselib.c:lua_setglobal(L,"_VERSION");/*setglobal_VERSION*/sysbench/scripting/lua/src/lbaselib.c:lua_setglobal(L,"newproxy");/*setglobal`newproxy"*/sysbench/scripting/script_lua.c:lua_setglobal(state,opt->name);sysbench/scripting/script_lua.c:lua_setglobal(state,"sb_rand");sysbench/scripting/script_lua.c:lua_setglobal(state,"sb_rand_uniq");sysbench/scripting/script_lua.c:lua_setglobal(state,"sb_rnd");sysbench/scripting/script_lua.c:lua_setglobal(state,"sb_rand_str");sysbench/scripting/script_lua.c:lua_setglobal(state,"sb_rand_uniform");sysbench/scripting/script_lua.c:lua_setglobal(state,"sb_rand_gaussian");sysbench/scripting/script_lua.c:lua_setglobal(state,"sb_rand_special");sysbench/scripting/script_lua.c:lua_setglobal(state,"db_connect");sysbench/scripting/script_lua.c:lua_setglobal(state,"db_disconnect");sysbench/scripting/script_lua.c:lua_setglobal(state,"db_query");sysbench/scripting/script_lua.c:lua_setglobal(state,"db_bulk_insert_init");sysbench/scripting/script_lua.c:lua_setglobal(state,"db_bulk_insert_next");sysbench/scripting/script_lua.c:lua_setglobal(state,"db_bulk_insert_done");sysbench/scripting/script_lua.c:lua_setglobal(state,"db_prepare");sysbench/scripting/script_lua.c:lua_setglobal(state,"db_bind_param");sysbench/scripting/script_lua.c:lua_setglobal(state,"db_bind_result");sysbench/scripting/script_lua.c:lua_setglobal(state,"db_execute");sysbench/scripting/script_lua.c:lua_setglobal(state,"db_close");sysbench/scripting/script_lua.c:lua_setglobal(state,"db_store_results");sysbench/scripting/script_lua.c:lua_setglobal(state,"db_free_results");sysbench/scripting/script_lua.c:lua_setglobal(state,"DB_ERROR_NONE");sysbench/scripting/script_lua.c:lua_setglobal(state,"DB_ERROR_DEADLOCK");sysbench/scripting/script_lua.c:lua_setglobal(state,"DB_ERROR_FAILED");sysbench/scripting/script_lua.c:lua_setglobal(L,"db_driver");传入参数,可以把sysbench_pg的参数-替换成_在lua脚本中使用这些变量,例子--pgsql-host=127.0.0.1->对应lua中的变量名pgsql_host--pgsql-port=1921->对应lua中的变量名pgsql_port--pgsql-user=postgres->对应lua中的变量名pgsql_user--pgsql-password=postgres->对应lua中的变量名pgsql_password--pgsql-db=postgres->对应lua中的变量名pgsql_db--oltp-tables-count=64->对应lua中的变量名oltp_tables_count--oltp-table-size=1000000->对应lua中的变量名oltp_table_size--num-threads=64->对应lua中的变量名num_threads2023-06-09 21:02:021
编程中获取全局变量有什么作用?
Global variables解释 全局变量也称为外部变量,它是在函数外部定义的变量。 它不属于哪一个函数,它属于一个源程序文件。其作用域是整个源程序。在函数中使用全局变量,一般应作全局变量说明。 只有在函数内经过说明的全局变量才能使用。 但在一个函数之前定义的全局变量,在该函数内使用可不再加以说明。出处:百度百科http://baike.baidu.com/view/261041.htm2023-06-09 21:02:121
flywithlua怎么设置
flywithlua怎么设置(1)当第一个参数为一个函数时,表示设置该函数的环境 (2)当第一个参数为一个数字时,为1代表当前函数,2代表调用自己的函数,3代表调用自己的函数的函数,以此类推2023-06-09 21:02:223
新手求教 lua中addEventListenerScrollView 怎么调用
涉及到闭包和upvalue以及语义作用域的概念……lua中函数能够使用其外部已创建的非全局变量,并保存其状态,哪怕这个变量已经离开作用域……所以fun1()中的data是指向fun1()声明时已存在的局部变量data(此处已存在指的是在源码中声明的位置,这就是所谓语义作用域,lexical scoping,即变量作用域与程序结构组织有关,而与运行时的顺序无关);由于闭包的原理,fun1()实质还保存了其对应的外部变量,所以即便后来data的值被一个新的变量覆盖(注意此data已非彼data,仅仅名字相同),fun1仍然会使用原先的值。你可以自己试验一下,比如将第二个local data的local去掉,那么就不再是声明一个新的名为data的变量而是仅仅改变已经存在的data的值,那么调用fun1()的结果也会不同2023-06-09 21:02:351
lua里有类似help的函数吗
1.编写一个简单的模块Lua的模块是什么东西呢?通常我们可以理解为是一个table,这个table里有一些变量、一些函数…等等,这不就是我们所熟悉的类吗?没错,和类很像(实际上我说不出它们的区别)。我们来看看一个简单的模块,新建一个文件,命名为game.lua,代码如下:复制代码 代码如下:game = {}function game.play()print("那么,开始吧");endfunction game.quit()print("你走吧,我保证你不会出事的,呵,呵呵");endreturn game;我们定义了一个table,并且给这个table加了两个字段,只不过这两个字段的值是函数而已。至于如何使用模块,那就要用到我们之前介绍过的require了。我们在main函数里这么使用:复制代码 代码如下:local function main()cc.FileUtils:getInstance():addSearchPath("src")game = require("game");game.play();end注意,我们要require其他文件的时候,要把文件路径给设置好,否则会找不到文件。因为我使用的是Cocos Code IDE,直接调用addSearchPath函数就可以了,我的game.lua文件是在src目录下的。好了,运行代码,结果如下:复制代码 代码如下:[LUA-print] 那么,开始吧OK,这就是一个很简单的模块,如果我们习惯了Java、C++等面向对象语言,那也可以简单地把模块理解为类。2.为以后的自己偷懒——避免修改每个函数中的模块名假设我们想把刚刚的game模块改个名字,改成eatDaddyGame,那么,我们需要做以下两件事情:1).修改game.lua的文件名2).修改game.lua的内容,把所有的game改成eatDaddyGame目前的game.lua函数还算少,就两个,实际上一个模块的函数肯定不会少的,那么,要这么去改这些函数,太烦了。如果批量修改,又怕有哪个地方改错。于是,我们可以这么偷懒:复制代码 代码如下:game = {}local M = game;function M.play()print("那么,开始吧");endfunction M.quit()print("你走吧,我保证你不会出事的,呵,呵呵");endreturn M;我们用一个局部变量M来代替了game,于是,以后我们只需要修改前面两个的game就可以了,函数部分的内容完全不需要去修改。这个偷懒其实蛮有用的,某些情况下,修改越少,越安全~3.更进一步的偷懒——模块名参数实际上,我们可以更加得偷懒,以后修改模块名,只需要修改模块的文件名就可以了,文件内容可以不管,具体怎么实现?看代码:复制代码 代码如下:local M = {};local modelName = ...;_G[modelName] = M;function M.play()print("那么,开始吧");endfunction M.quit()print("你走吧,我保证你不会出事的,呵,呵呵");endreturn M;留意一下,这里有一个 local modelName = …“…”就是传递给模块的模块名,在这里其实就是“game”这个字符串。接着,有点微妙了,还记得之前介绍的全局环境_G吗?我们以”game”作为字段名,添加到_G这个table里。于是,当我们直接调用game的时候,其实就是在调用_G["game"]的内容了,而这个内容就是这里的M。能逻辑过来吗?就是这么简单,在你没有忘记_G的前提下~4.利用非全局环境制作更简洁和安全的模块如果说,刚刚已经达到了我们作为高(ai)智(zhe)商(teng)人群的巅峰,那,你就太天真了。巅峰就是要拿来超越的,还记得我们的非全局环境吗?就是那个setfenv函数。我们来看看下面的代码:复制代码 代码如下:local M = {};local modelName = ...;_G[modelName] = M;setfenv(1, M);function play()print("那么,开始吧");endfunction quit()print("你走吧,我保证你不会出事的,呵,呵呵");endreturn M;我们把game.lua这个模块里的全局环境设置为M,于是,我们直接定义函数的时候,不需要再带M前缀。因为此时的全局环境就是M,不带前缀去定义变量,就是全局变量,这时的全局变量是保存在M里。所以,实际上,play和quit函数仍然是在M这个table里。于是,我们连前缀都不用写了,这真是懒到了一个极致,简直就是艺术~另外,由于当前的全局环境是M,所以, 在这里不需要担心重新定义了已存在的函数名,因为外部的全局变量与这里无关了。当然,如果大家现在就运行代码,肯定会报错了。因为我们的全局环境改变了,所以print函数也找不到了。为了解决这个问题,我们看看第5条内容吧~5.解决原全局变量的无法找到的问题——方案1第一个方法,就是我们之前介绍过的,使用继承,如下代码:复制代码 代码如下:local M = {};local modelName = ...;_G[modelName] = M;-- 方法1:使用继承setmetatable(M, {__index = _G});setfenv(1, M);function play()print("那么,开始吧");endfunction quit()print("你走吧,我保证你不会出事的,呵,呵呵");endreturn M;没错,使用__index元方法就能解决这个问题了,当找不到print等函数时,就会去原来的_G里查找。6.解决原全局变量的无法找到的问题——方案2第二个方法更简单,使用一个局部变量把原来的_G保存起来,如下代码:复制代码 代码如下:local M = {};local modelName = ...;_G[modelName] = M;-- 方法2:使用局部变量保存_Glocal _G = _G;setfenv(1, M);function play()_G.print("那么,开始吧");endfunction quit()_G.print("你走吧,我保证你不会出事的,呵,呵呵");endreturn M;这种方法的缺点比较明显,那就是,每次调用print等函数时,都要使用_G前缀。7.解决原全局变量的无法找到的问题——方案3第三个方法比较繁琐,使用局部变量把需要用到的其他模块保存起来,如下代码:复制代码 代码如下:local M = {};local modelName = ...;_G[modelName] = M;-- 方法3:保存需要使用到的模块local print = print;setfenv(1, M);function play()print("那么,开始吧");endfunction quit()print("你走吧,我保证你不会出事的,呵,呵呵");endreturn M;这种方法的缺点更明显了,所有用到的模块都要用局部变量声明一次,烦人。但,就速度而言,第三种方案比第二种方案快,第二种方法又比第一种快。但至于快多少,我也不知道,只是理论上~我也没测试。8.你就笑吧,但,我还想更加偷懒——module函数本以为刚刚介绍的那些技巧已经够偷懒的吧?但Lua似乎知道我们有多懒似的,它竟然把我们把这一切都自动完成了。再来回忆我们刚刚为了偷懒而写的几句代码:复制代码 代码如下:local M = {};local modelName = ...;_G[modelName] = M;setmetatable(M, {__index = _G});setfenv(1, M);就这几句代码,其实我们可以忽略不写,因为,我们有module函数,它的功能就相当于写了这些代码。我们修改一下game.lua的内容,如下代码:复制代码 代码如下:module(..., package.seeall);function play()print("那么,开始吧");endfunction quit()print("你走吧,我保证你不会出事的,呵,呵呵");end注意,前面的几行代码都没了,只留下了一个module函数的调用。module函数的调用已经相当于之前的那些代码了。而package.seeall参数的作用就是让原来的_G依然生效,相当于调用了:setmetatable(M, {__index = _G});再次留意一下,代码末尾的return M也不见了,因为module函数的存在,已经不需要我们主动去返回这个模块的table了。9.结束这篇结束的内容似乎有点多,我也写了一个多小时了。其实我还省略不少东西,比如package.loaded,lua路径查找的规则等等。因为这些Cocos Code IDE,或者说是Cocos2d-x lua,已经帮我们做了,我们不需要去管这些。所以我就避重就轻了,啊不,是顾此失彼…不对~!反正,就是那个意思了~!您可能感兴趣的文章:Lua教程(十一):模块与包详解Lua模块与包学习笔记Lua中的模块(module)和包(package)详解Lua的函数环境、包实例讲解Lua调用自定义C模块Lua中使用模块的一些基础知识使用Lua编写Nginx服务器的认证模块的方法在Lua中使用模块的基础教程Lua极简入门指南(六):模块Lua模块和模块载入浅析解析Lua中的全局环境、包、模块组织结构2023-06-09 21:02:421
请问高手,怎样开发一个魔兽世界插件??软件编程
臭小孩 真天真啊2023-06-09 21:02:589
lua变量是什么意思?
变量就是存值的一个空间,变量需要声明。变量是储存值的地方。 程序中有三种变量: 全局变量、局部变量和表的域。单个名字可以指代一个全局变量也可以指代一个局部变量 (或者是一个函数的形参,这是一种特殊形式的局部变量)。名字指 程序中定义的标识符。所有没有显式声明为局部变量 的变量名都被当做全局变量。 局部变量有其 作用范围 : 局部变量可以被定义在它作用范围中的函数自由使用。在变量的首次赋值之前,变量的值均为 nil。方括号被用来对表作索引:对全局变量以及表的域之访问的含义可以通过元表来改变。 以索引方式访问一个变量 t[i] 等价于 调用 gettable_event(t,i)。 ,有一份完整的关于 gettable_event 函数的说明。 这个函数并没有在 lua 中定义出来,也不能在 lua 中调用。这里我们把提到它只是方便说明问题。)var.Name 这种语法只是一个语法糖,用来表示 var["Name"]:对全局变量 x 的操作等价于操作 _ENV.x。 由于代码块编译的方式, _ENV 永远也不可能是一个全局名字个名字可以指代一个全局变量也可以指代一个局部变量 (或者是一个函数的形参,这是一种特殊形式的局部变量)。名字指 程序中定义的标识符。所有没有显式声明为局部变量 的变量名都被当做全局变量。 局部变量有其 作用范围 : 局部变量可以被定义在它作用范围中的函数自由使用。变量就是存值的一个空间,变量需要声明。变量是储存值的地方。 程序中有三种变量: 全局变量、局部变量和表的域。单个名字可以指代一个全局变量也可以指代一个局部变量 (或者是一个函数的形参,这是一种特殊形式的局部变量)。名字指 程序中定义的标识符。所有没有显式声明为局部变量 的变量名都被当做全局变量。 局部变量有其 作用范围 : 局部变量可以被定义在它作用范围中的函数自由使用。在变量的首次赋值之前,变量的值均为 nil。方括号被用来对表作索引:对全局变量以及表的域之访问的含义可以通过元表来改变。 以索引方式访问一个变量 t[i] 等价于 调用 gettable_event(t,i)。 ,有一份完整的关于 gettable_event 函数的说明。 这个函数并没有在 lua 中定义出来,也不能在 lua 中调用。这里我们把提到它只是方便说明问题。)var.Name 这种语法只是一个语法糖,用来表示 var["Name"]:对全局变量 x 的操作等价于操作 _ENV.x。 由于代码块编译的方式, _ENV 永远也不可能是一个全局名字个名字可以指代一个全局变量也可以指代一个局部变量 (或者是一个函数的形参,这是一种特殊形式的局部变量)。名字指 程序中定义的标识符。所有没有显式声明为局部变量 的变量名都被当做全局变量。 局部变量有其 作用范围 : 局部变量可以被定义在它作用范围中的函数自由使用。2023-06-09 21:03:251
计算机语言开发中环境与全局环境是什么呢?
程序开发中环境就是程序运行的条件,只有满足条件时候逻辑代码才有用的环境。引用一个叫 var 的自由名字(指在任何层级都未被声明的名字) 在句法上都被翻译为 _ENV.var 。 此外,每个被编译的 Lua 代码块都会有一个外部的局部变量叫 _ENV , 因此,_ENV 这个名字永远都不会成为一个代码块中的自由名字。在转译那些自由名字时,_ENV 是否是那个外部的局部变量无所谓。 _ENV 和其它你可以使用的变量名没有区别。 这里特别指出,你可以定义一个新变量或指定一个参数叫这个名字。 当编译器在转译自由名字时所用到的 _ENV , 指的是你的程序在哪个点上可见的那个名为 _ENV 的变量。 (Lua 的可见性规则被 _ENV 用于值的那张表被称为 环境。Lua 保有一个被称为 全局环境 特别环境。它被保存在 C 注册表 ()的一个特别索引下。 在 Lua 中,全局变量 _G 被初始化为这个值。 (_G 不被内部任何地方使用。)当 Lua 加载一个代码块,_ENV 这个上值的默认值就是这个全局环境 。 因此,在默认情况下,Lua 代码中提及的自由名字都指的全局环境中的相关项 (因此,它们也被称为 全局变量 )。 此外,所有的标准库都被加载入全局环境,一些函数也针对这个环境做操作。 你可以用 load (或 loadfile)加载代码块,并赋予它们不同的环境。 (在 C 里,当你加载一个代码块后,可以通过改变它的第一个上值来改变它的环境。)在转译那些自由名字时,_ENV 是否是那个外部的局部变量无所谓。 _ENV 和其它你可以使用的变量名没有区别。 这里特别指出,你可以定义一个新变量或指定一个参数叫这个名字。 当编译器在转译自由名字时所用到的 _ENV , 指的是你的程序在哪个点上可见的那个名为 _ENV 的变量。 (Lua 的可见性规则被 _ENV 用于值的那张表被称为 环境。Lua 保有一个被称为 全局环境 特别环境。它被保存在 C 注册表 ()的一个特别索引下。 在 Lua 中,全局变量 _G 被初始化为这个值。 (_G 不被内部任何地方使用。)当 Lua 加载一个代码块,_ENV 这个上值的默认值就是这个全局环境 。 因此,在默认情况下,Lua 代码中提及的自由名字都指的全局环境中的相关项 (因此,它们也被称为 全局变量 )。 此外,所有的标准库都被加载入全局环境,一些函数也针对这个环境做操作。 你可以用 load (或 loadfile)加载代码块,并赋予它们不同的环境。 (在 C 里,当你加载一个代码块后,可以通过改变它的第一个上值来改变它的环境。2023-06-09 21:03:321
lua全局函数和局部函数的区别
没有显式声明为局部变量的变量名都被当做全局变量。局部变量有其作用范围:局部变量可以被定义在作用范围中的函数自由使用。2023-06-09 21:03:391
Linux lua中require "bit"
为了方便代码管理,通常会把lua代码分成不同的模块,然后在通过require函数把它们加载进来。现在看看lua的require的处理流程。函数原型:require(modname) ---modname ---->加载的模块名称首先Lua提供高级的require函数来加载运行库。粗略的说require和dofile完成同样的功能但有两点不同:1、require会搜索目录加载文件2、require会判断是否文件已经加载避免重复加载同一文件。由于上述特征,require在Lua中是加载库的更好的函数。require函数实现了不同lua文件的加载,类似于C++中的include,java中的import。 require使用的路径和普通的路径还是有些区别,我们一般见到的路径都是一个目录列表。require的路径是一个模式列表,每一个模式指明一种由虚文件名(require的参数modname)转成实文件名的方法。更明确地说,每一个模式是一个包含可选的问号(?)的文件名。匹配的时候Lua会首先将问号用虚文件名替换,然后看是否有这样的文件存在。如果不存在继续用同样的方法用第二个模式匹配。例如,路径如下:?; ?.lua; c:windows?; /usr/local/lua/?/?.lua调用require("add")时会试着打开以下这些文件:addadd.luac:windowsadd/usr/local/lua/add/add.lua为了确定路径,Lua首先检查全局变量LUA_PATH是否为一个字符串,如果是则认为这个串就是路径;否则require检查环境变量LUA_PATH的值,如果两个都失败require使用固定的路径(典型的"?;?.lua").require函数的实现原理如下:--require 函数的实现 function require(name)if not package.loaded[name] thenlocal loader = findloader(name) //这一步演示在代码中以抽象函数findloader来表示if loader == nil thenerror("unable to load module" .. name)endpackage.loaded[name] = truelocal res = loader(name)if res ~= nil thenpackage.loaded[name] = resendendreturn package.loaded[name] end require(在lua中它是ll_require函数)函数会在路径中搜索输入的文件路径,大致流程如下:1、package.loaded一个用于控制哪些模块已经加载的表,该表由require使用。当require一个模块名为modname的模块且package.loaded[modname]不为false时,require仅返回package.loaded[modname]存储的值.2、 package.preload为特定模块存储加载器的一个表。查找modname, 如果preload存在,那么就把它作为loader,调用loader(L)3、package.path查找lua库modname,这个库是通过module函数定义的,对于顶层的lua库,文件名和库名是一 样的而且不需要调用显式地在lua文件中调用module函数(在ll_require函数中可以看到处理方式),也就是说lua会根据lua文件直接完 成一个loader的初始化过程。4、package.cpath(实现lua调用C函数)查找c库,这个库是符合lua的一些规范的(export具有一定特征的函数接口),lua先已动态的方式加载该c库(.so),然后在库中查找并调用相应名字的接口,例如:luaopen_hello_world5、已第一个"."为分割,将模块名划分为:(main, sub)的形式,根据package.cpath查找main,如果存在,就加载该库并查询相应的接口:luaopen_main_sub,例如:先查找 hello库,并查询luaopen_hello_world接口6、得到loder后,用modname作为唯一的参数调用该loader函数。当然参数是通过lua的栈传递的,所以loader的原型必须符合lua的规范:int LUA_FUNC(lua_State *L)ll_require会将这个loader的返回值符给package.loaded[modelname],如果loader不返回值同时 package.loaded[modelname]不存在时, ll_require就会把package.loaded[modelname]设为true。最后ll_reuqire把package.loaded [modelname]返回给调用者。require的另一个功能是避免重复加载同一个文件两次。Lua保留一张所有已经加载的文件的列表(使用table保存)。如果一个加载的文件在表中存在require简单的返回;表中保留加载的文件的虚名,而不是实文件名。所以如果你使用不同的虚文件名require同一个文件两次,将会加载两次该文件。比如require "foo"和require "foo.lua",路径为"?;?.lua"将会加载foo.lua两次。我们也可以通过全局变量_LOADED访问文件名列表,这样我们就可以判断文件是否被加载过;同样我们也可以使用一点小技巧让require加载一个文件两次。比如,require "foo"之后_LOADED["foo"]将不为nil,我们可以将其赋值为nil,require "foo.lua"将会再次加载该文件。2023-06-09 21:03:561
lua 脚本怎么传递参数
static int ABC(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;} lua_register(L, "ABC", ABC);2023-06-09 21:04:052
程序开发中环境与全局环境是什么?
程序开发中环境就是程序运行的条件,只有满足条件时候逻辑代码才有用的环境。引用一个叫 var 的自由名字(指在任何层级都未被声明的名字) 在句法上都被翻译为 _ENV.var 。 此外,每个被编译的 Lua 代码块都会有一个外部的局部变量叫 _ENV , 因此,_ENV 这个名字永远都不会成为一个代码块中的自由名字。在转译那些自由名字时,_ENV 是否是那个外部的局部变量无所谓。 _ENV 和其它你可以使用的变量名没有区别。 这里特别指出,你可以定义一个新变量或指定一个参数叫这个名字。 当编译器在转译自由名字时所用到的 _ENV , 指的是你的程序在那个点上可见的那个名为 _ENV 的变量。 (Lua 的可见性规则被 _ENV 用于值的那张表被称为 环境。Lua 保有一个被称为 全局环境 特别环境。它被保存在 C 注册表 ()的一个特别索引下。 在 Lua 中,全局变量 _G 被初始化为这个值。 (_G 不被内部任何地方使用。)当 Lua 加载一个代码块,_ENV 这个上值的默认值就是这个全局环境 。 因此,在默认情况下,Lua 代码中提及的自由名字都指的全局环境中的相关项 (因此,它们也被称为 全局变量 )。 此外,所有的标准库都被加载入全局环境,一些函数也针对这个环境做操作。 你可以用 load (或 loadfile)加载代码块,并赋予它们不同的环境。 (在 C 里,当你加载一个代码块后,可以通过改变它的第一个上值来改变它的环境。)在转译那些自由名字时,_ENV 是否是那个外部的局部变量无所谓。 _ENV 和其它你可以使用的变量名没有区别。 这里特别指出,你可以定义一个新变量或指定一个参数叫这个名字。 当编译器在转译自由名字时所用到的 _ENV , 指的是你的程序在那个点上可见的那个名为 _ENV 的变量。 (Lua 的可见性规则被 _ENV 用于值的那张表被称为 环境。2023-06-09 21:04:131
求问大神,luaL_Reg怎么用啊
扩展Lua的基本方法之一就是为应用程序注册新的C函数到Lua中去。当我们提到Lua可以调用C函数,不是指Lua可以调用任何类型的C函数(有一些包可以让Lua调用任意的C函数,但缺乏便捷和健壮性)。正如我们前面所看到的,当C调用Lua函数的时候,必须遵循一些简单的协议来传递参数和获取返回结果。相似的,从Lua中调用C函数,也必须遵循一些协议来传递参数和获得返回结果。另外,从Lua调用C函数我们必须注册函数,也就是说,我们必须把C函数的地址以一个适当的方式传递给Lua解释器。当Lua调用C函数的时候,使用和C调用Lua相同类型的栈来交互。C函数从栈中获取她的参数,调用结束后将返回结果放到栈中。为了区分返回结果和栈中的其他的值,每个C函数还会返回结果的个数(the function returns (in C) the number of results it is leaving on the stack.)。这儿有一个重要的概念:用来交互的栈不是全局变量,每一个函数都有他自己的私有栈。当Lua调用C函数的时候,第一个参数总是在这个私有栈的index=1的位置。甚至当一个C函数调用Lua代码(Lua代码调用同一个C函数或者其他的C函数),每一个C函数都有自己的独立的私有栈,并且第一个参数在index=1的位置。26.1 C 函数先看一个简单的例子,如何实现一个简单的函数返回给定数值的sin值(更专业的实现应该检查他的参数是否为一个数字):static int l_sin (lua_State *L) {double d = lua_tonumber(L, 1);/* get argument */lua_pushnumber(L, sin(d));/* push result */return 1;/* number of results */}任何在Lua中注册的函数必须有同样的原型,这个原型声明定义就是lua.h中的lua_CFunction:typedef int (*lua_CFunction) (lua_State *L);从C的角度来看,一个C函数接受单一的参数Lua state,返回一个表示返回值个数的数字。所以,函数在将返回值入栈之前不需要清理栈,函数返回之后,Lua自动的清除栈中返回结果下面的所有内容。我们要想在Lua使用这个函数,还必须首先注册这个函数。我们使用lua_pushcfunction来完成这个任务:他获取指向C函数的指针,并在Lua中创建一个function类型的值来表示这个函数。一个quick-and-dirty的解决方案是将这段代码直接放到lua.c文件中,并在调用lua_open后面适当的位置加上下面两行:lua_pushcfunction(l, l_sin);lua_setglobal(l,"mysin");第一行将类型为function的值入栈,第二行将function赋值给全局变量mysin。这样修改之后,重新编译Lua,你就可以在你的Lua程序中使用新的mysin函数了。在下面一节,我们将讨论以比较好的方法将新的C函数添加到Lua中去。对于稍微专业点的sin函数,我们必须检查sin的参数的类型。有一个辅助库中的luaL_checknumber函数可以检查给定的参数是否为数字:当有错误发生的时候,将抛出一个错误信息;否则返回作为参数的那个数字。2023-06-09 21:04:221
lua中有static变量么
lua里没有static关键字。2023-06-09 21:04:292
为什么全局变量容易导致内存泄漏
题目说法本身不正确.全局变量不一定造成内存泄露,只是可能导致多线程不安全。在创建多个lua虚拟机的时候会2个线程同时操作一个变量。这是你代码设计问题如果一定导致内存泄露,那么就不可能存在全局变量了.2023-06-09 21:04:551
lua中的table表怎样传给c++
首先要把你的表放到一个全局变量然后在C++里使用getglobal 获得这个变量压入一个key取出对应刚才key对应的值百度知道不能帖地址 不然会立即被吞要是还是不会的话留邮箱我把地址发你2023-06-09 21:05:022
lua怎样和字符串指针相兼容
我认为扩展Lua的基本方法之一就是为应用程序注册新的C函数到Lua中去。当我们提到Lua可以调用C函数,不是指Lua可以调用任何类型的C函数(有一些包可以让Lua调用任意的C函数,但缺乏便捷和健壮性)。正如我们前面所看到的,当C调用Lua函数的时候,必须遵循一些简单的协议来传递参数和获取返回结果。相似的,从Lua中调用C函数,也必须遵循一些协议来传递参数和获得返回结果。另外,从Lua调用C函数我们必须注册函数,也就是说,我们必须把C函数的地址以一个适当的方式传递给Lua解释器。当Lua调用C函数的时候,使用和C调用Lua相同类型的栈来交互。C函数从栈中获取她的参数,调用结束后将返回结果放到栈中。为了区分返回结果和栈中的其他的值,每个C函数还会返回结果的个数(the function returns (in C) the number of results it is leaving on the stack.)。这儿有一个重要的概念:用来交互的栈不是全局变量,每一个函数都有他自己的私有栈。当Lua调用C函数的时候,第一个参数总是在这个私有栈的index=1的位置。甚至当一个C函数调用Lua代码(Lua代码调用同一个C函数或者其他的C函数),每一个C函数都有自己的独立的私有栈,并且第一个参数在index=1的位置。26.1 C 函数先看一个简单的例子,如何实现一个简单的函数返回给定数值的sin值(更专业的实现应该检查他的参数是否为一个数字):static int l_sin (lua_State *L) { double d = lua_tonumber(L, 1); /* get argument */ lua_pushnumber(L, sin(d)); /* push result */ return 1; /* number of results */}任何在Lua中注册的函数必须有同样的原型,这个原型声明定义就是lua.h中的lua_CFunction:typedef int (*lua_CFunction) (lua_State *L);从C的角度来看,一个C函数接受单一的参数Lua state,返回一个表示返回值个数的数字。所以,函数在将返回值入栈之前不需要清理栈,函数返回之后,Lua自动的清除栈中返回结果下面的所有内容。我们要想在Lua使用这个函数,还必须首先注册这个函数。我们使用lua_pushcfunction来完成这个任务:他获取指向C函数的指针,并在Lua中创建一个function类型的值来表示这个函数。一个quick-and-dirty的解决方案是将这段代码直接放到lua.c文件中,并在调用lua_open后面适当的位置加上下面两行:lua_pushcfunction(l, l_sin);lua_setglobal(l, "mysin");第一行将类型为function的值入栈,第二行将function赋值给全局变量mysin。这样修改之后,重新编译Lua,你就可以在你的Lua程序中使用新的mysin函数了。在下面一节,我们将讨论以比较好的方法将新的C函数添加到Lua中去。对于稍微专业点的sin函数,我们必须检查sin的参数的类型。有一个辅助库中的luaL_checknumber函数可以检查给定的参数是否为数字:当有错误发生的时候,将抛出一个错误信息;否则返回作为参数的那个数字。将上面我们的函数稍作修改:static int l_sin (lua_State *L) { double d = luaL_checknumber(L, 1); lua_pushnumber(L, sin(d)); return 1; /* number of results */}根据上面的定义,如果你调用mysin("a"),会得到如下信息:bad argument #1 to "mysin" (number expected, got string)注意看看luaL_checknumber是如何自动使用:参数number(1),函数名("mysin"),期望的参数类型("number"),实际的参数类型("string")2023-06-09 21:05:271
如何将C++绑定至Lua
游戏中的使用脚本语言已经成为了一个标准应用。脚本语言能够在游戏开发中扮演一个重要的角色,并且让数据结构化,计划事件,测试和调试这些工作更加容易。脚本语言也能够允许像美术,策划这些非程序专家通过一个高层的抽象脚本来为游戏编写代码。这个抽象层的一部分也能够允许提供给玩家来定制整个游戏。从程序员的角度上来看,把一个脚本语言嵌入到游戏中最主要的问题是如果为脚本语言提供对宿主对象的访问(通常是C/C++对象)。在选择一个脚本语言的时候有两个关键的特性:嵌入相关问题和绑定相关问题。而这些是Lua语言的一些设计的初衷。可是,Lua语言并没有提供任何自动创建绑定的工具,因为这是出于另外一个设计初衷:Lua只是提供机制,而不是策略。因而,就有许多种策略可以用来在Lua中绑定宿主对象。每一种策略都有它的优点和缺点,游戏开发者必须在得到在脚本环境中所需要的功能需求之后确定最好的策略。一些开发者可能只是把C/C++对象映射成简单的数值,但是其他人可能需要实现运行期类型检查机制,甚至是在Lua中扩展宿主的应用。另外一个需要处理的重要问题是,是否允许Lua来控制宿主对象的生命周期。在这篇文章中,我们将探究使用Lua的API来实现不同的宿主对象绑定策略。绑定函数为了说明不同策略的实现,让我们考虑把一个简单的C++类绑定到Lua中。实现的目标是在Lua中实现对类的访问,因此允许脚本通过导出的函数来使用宿主所提供的服务。这里主要的想法是使用一个简单的类来引导我们的讨论。下面讨论的是一个虚构游戏中的英雄类,有几个将会被映射到Lua中的公用方法。class Hero{ public: Hero( const char* name ); ~Hero(); const char* GetName(); void SetEnergy( double energy ); double GetEnergy(); }; 要把类方法绑定到Lua中,我们必须使用Lua的API来编写绑定功能。每一个绑定函数都负责接收Lua的值作为输入参数,同时把它们转化成相应的C/C++数值,并且调用实际的函数或者方法,同时把它们的返回值给回到Lua中。从标准发布版本的Lua中,Lua API和辅助库提供了不少方便的函数来实现Lua到C/C++值的转换,同样,也为C/C++到Lua值的转换提供了函数。例如,luaL_checknumber提供了把输入参数转换到相对应的浮点值的功能。如果参数不能对应到Lua中的数值类型,那么函数将抛出一个异常。相反的,lua_pushnumber把给定的浮点值添加到Lua参数栈的顶端。还有一系列相类似的函数来映射其他的基本的Lua类型和C/C++数据类型。我们目前最主要的目标提出不同的策略来扩展标准Lua库和它为转换C/C++类型对象所提供的功能。为了使用C++的习惯,让我们创建一个叫做Binder的类来封装在Lua和宿主对象中互相转化值的功能。这个类也提供了一个把将要导出到Lua中的模块初始化的方法。class Binder { public: // 构造函数 Binder( lua_state *L ); // 模块(库) 初始化 int init( const char* tname, const luaL_reg* first ); // 映射基本的类型 void pushnumber( double v ); double checknumber( int index ); void pushstring( const char s ); const char* checkstring( int index ); …. // 映射用户定义类型 void pushusertype( void* udata, const char* tname ); void* checkusertype( int index, const char* tname ); }; 类的构造函数接收Lua_state来映射对象。初始化函数接收了将被限制的类型名字,也被表示为库的名称(一个全局变量名来表示在Lua中的类表),并且直接调用了标准的Lua库。例如,映射一个数值到Lua中,或者从Lua映射出来的方法可能是这样的:void Binder::pushnumber( double v ) { lua_pushnumber( L,v ); } double Binder::checknumber( int index ) { return luaL_checknumber( L,index ); } 真正的挑战来自把用户自定义类型互相转换的函数:pushusertype和checkusertype。这些方法必须保证映射对象的绑定策略和目前使用中的一致。每一种策略都需要不同的库的装载方法,因而要给出初始化方法init的不同实现。一旦我们有了一个binder的实现,那么绑定函数的代码是非常容易写的。例如,绑定函数相关的类的构造函数和析构函数是如下代码:static int bnd_Create( lua_state* L ){ LuaBinder binder(L); Hero* h = new Hero(binder.checkstring(L,1)); binder.pushusertype(h,”Hero”); return i; } static int bnd_Destroy( lua_state* L ){ LuaBinder binder(L); Hero * hero = (Hero*)binder.checkusertype( 1, “Hero” ); delete hero; return 0; } 同样的,和GetEnergy和SetEnergy方法的绑定函数能够像如下编码:static int bnd_GetEnergy( lua_state* L ){ LuaBinder binder(L); Hero* hero = (Hero*)binder.checkusertype(1,”Hero”); binder.pushnumber(hero->GetEnergy()); return 1; } static int bnd_SetEnery( lua_State* L ){ LuaBinder binder(L); Hero* hero = (Hero*)binder.checkusertype(1,”Hero”); Hero.setGetEnergy( binder.checknumer(2) ); return 1; } 注意绑定函数的封装策略将被用于映射对象:宿主对象使用对应的check和push方法组来进行映射,同时这些方法也用于以接收关联类型为输入参数。在我们为所有的绑定函数完成编码。我们可以来编写打开库的方法:static const luaL_reg herolib[] = { { “Create”, bnd_Create }, {“Destroy”, bnd_Destory }, {“GetName”, bnd_GetName}, … }; int luaopen_hero( lua_State *L ) { LuaBinder binder(L); Binder.init( “hero”, herolib ); return i; } 绑定宿主对象和Lua数值把C/C++对象和Lua绑定的方法就是把它的内存地址映射成轻量的用户数据。一个轻量的用户数据可以用指针来表示(void *)并且它在Lua中只是作为一个普通的值。从脚本环境中,能够得到一个对象的值,做比较,并且能够把它传回给宿主。我们要在binder类中所实现的这个策略所对应的方法通过直接调用在标准库中已经实现的函数来实现:void Binder::init( const char *tname, const luaL_reg *flist ){ luaL_register( L, tname, flist ); } void Binder::pushusertype( void* udata, const char* tname ){ lua_pushlightuserdata( L, udata ); } void *Binder::checkusertype( int index, const char* tname ){ void *udata = lua_touserdata( L, index ); if ( udata ==0 ) luaL_typerror( L, index, tname ); return udata; } 函数luaL_typerror在上面的实现中用于抛出异常,指出输入参数没有一个有效的相关对象。通过这个映射我们英雄类的策略,以下的Lua便是可用的:Local h = Hero.Create(“myhero”) Local e = Hero.GetEnergy(h) Hero.SetEnergy(h, e-1) Hero.Destroy() 把对象映射成简单值至少有三个好处:简单,高效和小的内存覆盖。就像我们上面所见到的,这种策略是很直截了当的,并且Lua和宿主语言之间的通信也是最高效的,那是因为它没有引入任何的间接访问和内存分配。然而,作为一个实现,这种简单的策略因为用户数据的值始终被当成有效的参数而变得不安全。传入任何一个无效的对象都将回导致宿主程序的直接崩溃。加入类型检查我们能够实现一个简单的实时的类型检查机制来避免在Lua环境中导致宿主程序崩溃。当然,加入类型检查会降低效率并且增加了内存的使用。如果脚本只是用在游戏的开发阶段,那么类型检查机制可以在发布之前始终关闭。换句话说,如果脚本工具要提供给最终用户,那么类型检查就变得非常重要而且必须和产品一起发布。要添加类型检查机制到我们的绑定到值的策略中,我们能够创建一个把每一个对象和Lua相对应类型名字映射的表。(在这篇文章中所有提到的策略里,我们都假定地址是宿主对象的唯一标识)。在这张表中,轻量的数据可以作为一个键,而字符串(类型的名称)可以作为值。初始化方法负责创建这张表,并且让它能够被映射函数调用到。然而,保护它的独立性也是非常重要的:从Lua环境中访问是必须不被允许的;另外,它仍然有可能在Lua脚本中使宿主程序崩溃。使用注册表来存储来确保它保持独立性是一个方法,它是一个全局的可以被Lua API单独访问的变量。然而,因为注册表是唯一的并且全局的,用它来存储我们的映射对象也阻止了其他的C程序库使用它来实现其他的控制机制。另一个更好的方案是只给绑定函数提供访问类型检查表的接口。直到Lua5.0,这个功能才能够被实现。在Lua5.1中,有一个更好的(而且更高效)方法:环境表的使用直接和C函数相关。我们把类型检查表设置成绑定函数的环境表。这样,在函数里,我们对表的访问就非常高效了。每一个函数都需要注册到Lua中,从当前的函数中去继承它的环境表。因而,只需要改变初始化函数的环境表关联就足够了――并且所有注册过的办定函数都会拥有同样一个关联的环境表。现在,我们可以对binder类的执行类型检测的方法进行编码了: void Binder::init(const char* tname, const luaL_reg* flist){ lua_newtable(L); //创建类型检查表 lua_replace(L,LUA_ENVIRONINDEX ); // 把表设置成为环境表 luaL_register( L,tname, flist ); //创建库表 } void Binder::pushusertype(void *udata, const char* tname){ lua_pushlightuserdata(L,udata); //压入地址 lua_pushvalue(L,-1); //重复地址 lua_pushstring(L,tname); //压入类型名称 lua_rawset(L,LUA_ENVIRONINDEX); //envtable[address] = 类型名称 } void* Binder::checkusertype( int index, const char* tname ){ void* udata = lua_touserdata( L,index ); if ( udata ==0 || !checktype(udata, tname) ) luaL_typeerror(L,index,tname); return udata; } 面代码使用一个私有的方法来实现类型检查:int Binder::checktype(void *udata, const char* tname){ lua_pushlightuserdata(L,udata); //压入地址 lua_rawget( L, LUA_ENVIRONINDEX); //得到env[address] const char* stored_tname = lua_tostring(t,-1); int result = stored_tname && strcmp(stored_tname, tname) ==0; lua_pop(L,1); return result; } 通过这些做法,我们使得绑定策略仍然非常高效。同样,内存负载也非常低――所有对象只有一个表的实体。然而,为了防止类型检查表的膨胀,我们必须在销毁对象的绑定函数中释放这些表。在bnd_Destroy函数中,我们必须调用这个私有方法:void Binder::releaseusertype( void* udata ){ lua_pushlightuserdata(L,udata); lua_pushnil(L); lua_settable(L,LUA_ENVIRONINDEX); }2023-06-09 21:05:341
skynet 什么时候调用 luaopen
lua_State* p = lua_open();//创建lua虚拟机的环境。#define lua_open() luaL_newstate()luaopen_base(p);luaopen_math(p);luaopen_string(p);//以上加入这些库,就像C包含头文件lua_settop(p, 0);//清空栈空间lua_getglobal(p, "key"); //取一个全局变量为“key”,压入栈顶int temp = lua_isstring(p, 1); //当判断值是一个字符串或是一个数字(数字总能转换成字符串)时,返回 1 ,否则返回 0 const char* str = lua_tostring(p, 1); //把栈顶的数据转换成字符串。这个函数没有出栈功能,可以使用lua_pop(p,1)将栈顶元素弹出lua_close(p);2023-06-09 21:05:541
lua怎么传值给C++?
把lua内部函数调用流程搞清楚就OK了它内部运行程序的vm感觉就是个栈机另外用全局变量方法也可以2023-06-09 21:06:031
程序代码开发中怎样才能提高我们的开发效率呢?
提高开发效率可以从以下几个方面进行学习。首先多多学习开源项目,分析别人的代码架构,吸取对方优秀的编程思想,理解后下次开发用上第二:记住系统提供给我们的快速开发的方法,常用的方法都有日期函数,字符串函数,文件函数等,比如下面的函数:一个不透明的结构, 它指向一条线程并间接(通过该线程)引用了整个 Lu a 解释器的状态。 L ua 库是完全可重入的: 它没有任何全局变量。 状态机所有的信息都可以通过这个结构访问到。这个结构的指针必须作为第一个参数传递给每一个库函数。 l ua_newstate 是一个例外, 这个函数会从头创建一个 L ua 状态机。l。a_status返回线程 L 的状态。正常的线程状态是 0 (LUA_OK)。 当线程用 lua_resume 执行完毕并抛出了一个错误时, 状态值是错误码。 如果线程被挂起,状态为 LUA_YIELD 。你只能在状态为 LUA_OK 的线程中调用函数。 你可以延续一个状态为 LUA_OK 的线程 (用于开始新协程)或是状态为 LUA_YIELD 的线程 (用于延续协程)。lu a_stringtonumbersize_t lu a_stringtonumber (l ua_State *L, const char *s);将一个零结尾的字符串 s 转换为一个数字, 将这个数字压栈,并返回字符串的总长度(即长度加一)。 转换的结果可能是整数也可能是浮点数, 这取决于 Lua 的转换语法(。 这个字符串可以有前置和后置的空格以及符号。 如果字符串并非一个有效的数字,返回 0 并不把任何东西压栈。 (注意,这个结果可以当成一个布尔量使用,为真即转换成功。)lu a_tobooleanint lu a_toboolean (lu a_State *L, int index);把给定索引处的 Lu a 值转换为一个 C 中的布尔量( 0 或是 1 )。 和 L ua 中做的所有测试一样, lua_toboolean 会把任何不同于 false 和 nil 的值当作真返回; 否则就返回假。 (如果你想只接收真正的 boolean 值, 就需要使用 lua_isboolean 来测试值的类型。)lu a_tocfunctionlu a_CFunction lua_tocfunction (lu a_State *L, int index);把给定索引处的 L ua 值转换为一个 C 函数。 这个值必须是一个 C 函数; 如果不是就返回 NULL 。lu a_tointegerlua_Integer l ua_tointeger (lu a_State *L, int index);等价于调用 l ua_tointegerx, 其参数 isnum 为 NULL。lu a_tointegerxl ua_Integer lua_tointegerx (lua_State *L, int index, int *isnum);将给定索引处的 L。a 值转换为带符号的整数类型 lu a_Integer。 这个 Lu a 值必须是一个整数,或是一个可以被转换为整数 (3)的数字或字符串; 否则,lua_tointegerx 返回 0 。如果 isnum 不是 NULL, *isnum 会被设为操作是否成功。lu a_tolstringconst char *lu a_tolstring (lu a_State *L, int index, size_t *len);把给定索引处的 Lua 值转换为一个 C 字符串。 如果 len 不为 NULL , 它还把字符串长度设到 *len 中。 这个 L ua 值必须是一个字符串或是一个数字; 否则返回返回 NULL 。 如果值是一个数字, lua_tolstring 还会 把堆栈中的那个值的实际类型转换为一个字符串。 (当遍历一张表的时候, 若把 lua_tolstring 作用在键上, 这个转换有可能导致 lua_next 弄错。)lua_tolstring 返回一个已对齐指针 指向 Lua 状态机中的字符串。 这个字符串总能保证 ( C 要求的)最后一个字符为零 ("") , 而且它允许在字符串内包含多个这样的零。因为 Lua 中可能发生垃圾收集, 所以不保证 lua_tolstring 返回的指针, 在对应的值从堆栈中移除后依然有效。2023-06-09 21:06:161
如何将C++绑定至Lua
1.编写.pkg文件然后使用tolua++创建.h/.cpp文件如LuaCocos2d.h/.cpp.pkg文件跟.h文件一样会列出所有类和函数,格式请参见“$cocos2dDir/tools/tolua++/”中的文件。2.处理写.h/.cpp文件为什么不使用pkg和tolua++?如果你想控制所有进程,就要自己编辑Lua绑定函数。有一个“MyClass”类,该类包含三个成员函数。static MyClass * createWithSize(CCSize s)CCSize getSize()void setSize(CCSize s)在“tolua_MyClass.cpp”中编写这些函数:extern "C" {#include "tolua++.h" #include "tolua_fix.h" }#include "MyClass.h" int tolua_MyClass_createWithSize(lua_State *L){ CCSize *s = (CCSize *)tolua_tousertype(L, 2, NULL); MyClass *o = MyClass::createWithSize(s? s : CCSizeZero); tolua_pushusertype(L, o, "MyClass"); return 1;}int tolua_MyClass_getSize(lua_State *L){ MyClass *o = (MyClass *)tolua_tousertype(L, 1, NULL); if(o){tolua_pushusertype(L, Mtolua_new((CCSize)(o->getSize())), "CCSize"); return 1;}int tolua_MyClass_setSize(lua_State *L){ MyClass *o = (MyClass *)tolua_tousertype(L, 1, NULL); CCSize *s = (CCSize *)tolua_tousertype(L, 2, NULL); if(o && s){ o->setSize(s);} return 1;}TOLUA_API int tolua_MyClass_open(lua_State* L){ tolua_open(L); tolua_usertype(L, "MyClass"); tolua_model(L, NULL, 0); tolua_cclass(L, , "MyClass", "", NULL); tolua_beginmodule(L, "MyClass"); tolua_function(L, "createWithSize", tolua_MyClass_createWithSize); tolua_function(L, "getSize", tolua_MyClass_getSize); tolua_function(L, "setSize", tolua_MyClass_setSize); tolua_endmodule(L); tolua_endmodule(L); return 1;}调用“AppDelegate.cpp”中的“tolua_MyClass_open(L)”,传递“lua_State”参数2023-06-09 21:06:232