还有个迭代器,基础语法基本已经说完了,后面想到啥再补充,之后的教程会从以下方面来讲:
当然,我的理解也是很基础的,特别是在界面设计上,我都是用的默认控件的默认设置,不会去自定义控件内容。要想做出特别炫酷的程序,你还得依赖其他语言和工具的基础。例如用HTML和CSS来实现界面。
参考文档:
https://bbs.aardio.com/doc/reference/libraries/kernel/table/meta.html
https://bbs.aardio.com/doc/reference/the%20language/operator/overloading.html
主要是用于重载运算符和内置函数的行为。
表可以定义另一个表作为元表,然后在元表里定义元方法来定义操作符或内置函数操作表的一些行为。这种类似于Python的魔法方法,在Python中使用__eq__
定义==
的行为,而在aardio中用_eq
元方法来定义==
的行为。
举个例子,python中print会调用对象的__str__
或__repr__
来打印一个对象,而aardio也是调用tostring来打印一个对象,但表默认并没有定义_tostring
元方法,导致打印出来的内容是table: 03B2E3A8
的格式
我们可以通过给表定义_tostring
元方法,来使io.print
或者console.log
正常显示表
import console;
io.open()
var tab = {
a=1;
b=2;
@{
_tostring = function(...) {
// 元方法里不能调用触发元方法的函数,比如_tostring里不能调用tostring
// _get元方法可以通过[[k]]运算符来避开元方法,通过.和[]会触发_get,而[[]]不会
return table.tostring(owner);
}
}
}
io.print("没有定义元方法" , {});
io.print("定义了元方法" , tab);
console.pause(true);
输出如下:
没有定义元方法 table: 03A8E2F8
定义了元方法 {
a=1;
b=2
}
这个就不细说了,应该很容易理解。
io.open(); tab = { x=10 ; y=20 };
tab2 = { x=12 ; y=22 }
//c = tab + tab2; //这样肯定会出错,因为 table默认是不能相加的
//创建一个元素,元表中的__add函数重载加运算符。
tab@ = {
_add = function(b) {
return owner.x + b.x
};
}
c = tab + tab2; //这时候会调用重载的操作符 tab@._add(tab2)
io.print( c ) //显示22
还有一个很常用的元方法是_get
和_set
,是定义访问对象属性时触发的。利用这个可以让代码量少很多,看起来逻辑也更清晰。
这里举个实际例子,我在封装sunny的时候,遇到个很累人的事。sunny的dll导出函数,返回值有些是指针,你需要手动给他转成字符串,而且还需要手动释放这个指针指向的内存,也就是说你调用一次导出函数,就得写至少三行代码(调用、转字符串和释放)。
那么,有没有一种方法,定义完这个导出函数,在使用的时候就调用函数释放内存,并转成字符串返回,而不用我每次都手动释放和转字符串。
先定义request类,现在只需要给它定义一个messageId属性和_meta元方法:
namespace sunny;
class request{
ctor(messageId){
this.messageId = messageId;
}
@_meta;
}
@后面跟的是元表的名称,你可以将元表定义在名字空间里,这样看起来代码更舒服。下面在类的名字空间里定义dll方法和元表.
namespace request{
//释放指针的函数
Free = ::SunnyDLL.api("Free","void(pointer p)");
// 下面的函数第一个参数都是messageId
DelRequestHeader = ::SunnyDLL.api("DelRequestHeader","void(int id,str h)");
GetRequestBodyLen = ::SunnyDLL.api("GetRequestBodyLen","int(int id)");
GetRequestBody = ::SunnyDLL.api("GetRequestBody","pointer(int id)");
// 定义一个中间方法
// name是要调用的导出函数,messageId则是导出函数的第一个参数
xcall = function(name, messageId, len){
var func = self[name];
if(!func) error("不支持的函数!");
function proxyFunc(...){
var v = func(messageId, ...);
var result;
if(type(v) == type.pointer){
if(len) result = ..raw.tostring(v,1,len);
else result = ..raw.tostring(v);
Free(v);
}else{
result = v;
}
return result;
}
return proxyFunc;
}
// 定义元表
_meta = {
_get = function(k){
return xcall(k, owner.messageId);
}
}
}
这个代码初看可能有点费劲,我们拆解着来看。
首先前面几行只是定义了四个dll的导出函数,然后下面定义了_meta
这个表。
而_meta里只定义了一个元方法_get
,它的作用是当你访问对象的属性时会触发这个方法,然后给你返回值。比如我先实例化一个request对象
r = request(111111);
// 当访问r.GetRequestBody时,这个对象没有GetRequestBody属性,所以会触发_get元方法
// 得到的返回值就是 返回它的返回值也就是`xcall("GetRequestBody", owner.messageId)`.
console.log(r.GetRequestBody)
这里的owner就是指r这个对象。然后定义了xcall这个函数,它里面又定义了一个函数proxyFunc,并将它作为返回值,这种被称为闭包。先分析下xcall方法
// 这里的self指的是当前名字空间,也就是request,name则是需要调用的方法名,例如是GetRequestBody
// 这里func的值就等于GetRequestBody,也就是::SunnyDLL.api("GetRequestBody","pointer(int id)");
var func = self[name];
// 如果func是null的话,说明当前名字空间下没有这个函数,也就不是我们定义的sunny导出函数
if(!func) error("不支持的函数!");
// 定义了proxyFunc函数,`xcall(k, owner.messageId)`返回的值就是proxyFunc函数,这里的三个点表示传入任意个参数,类似于Python中的*args
function proxyFunc(...){
// 调用GetRequestBody(messageId, ...)
var v = func(messageId, ...);
// 定义返回结果
var result;
// 如果结果是指针的话
if(type(v) == type.pointer){
// 就把它转为字符串,二进制数据需要指定长度,不然就是到
参与评论
手机查看
返回顶部