aardio教程三) 元表、元方法

这篇具有很好参考价值的文章主要介绍了aardio教程三) 元表、元方法。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

前言

还有个迭代器,基础语法基本已经说完了,后面想到啥再补充,之后的教程会从以下方面来讲:

  • 基础库的使用,比如string、table等
  • 基础控件的使用,比如listview、tab等
  • aardio和Python交互,比如给Python写个界面
  • 自带的范例程序
  • 我写的一些小程序

当然,我的理解也是很基础的,特别是在界面设计上,我都是用的默认控件的默认设置,不会去自定义控件内容。要想做出特别炫酷的程序,你还得依赖其他语言和工具的基础。例如用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){
	    // 就把它转为字符串,二进制数据需要指定长度,不然就是到\0结束
		if(len) result = ..raw.tostring(v,1,len);
		else result = ..raw.tostring(v);
		// 调用导出函数释放内存
		Free(v);
	}else{
	    // 如果是其他类型数据就直接返回,比如数值或null
		result = v;
	}
	return result;
}

这样一番折腾,起了什么效果呢,看一下下面两段代码,如果不利用元方法的话,你使用dll导出函数得这么写

// 导入request名字空间
improt request;
// 调用名字空间下的函数
var messageId = 111111
var pResult = request.GetRequestBody(messageId);
// 将指针转为字符串
var result = raw.tostring(pResult,1);
// 释放内存
request.Free(pResult);
// 再使用其他导出函数也需要重复写这几行代码

看着就几行代码,但是你想想调用一个函数都得写好几行,如果调用多次呢。而定义了xcall和_meta之后,只需要这样写代码:

improt request;
var messageId = 111111;
var req = request(messageId);
var result = req.GetRequestBody();
// 后面调用都只需要用req.方法名()调用,不需要管raw.tostring和Free了

因为req是可以复用的,所以我调用任何导出函数都只需要写一行代码,使用sunny库的代码也变得更简洁易懂了。

官方例子

给表创建一个代理,监控表属性的访问和设置:

// 创建一个代理,为另一个table对象创建一个替身以监控对这个对象的访问
function table.createProxy(tab) {
    var real = tab;//在闭包中保存被代理的数据表tab
    var _meta = {
        _get = function(k){
            io.print(k+"被读了");
            return real[k];
        };
        _set = function (k,v){
            io.print(k+"被修改值为"+v)
            real[k]=v; //删除这句代码就创建了一个只读表
        }
    }
    var proxy = {@_meta};//创建一个代理表
    
    return proxy; //你要访问真正的表?先问过我吧,我是他的经纪人!!!
}

//下面是使用示例

tab = {x=12;y=15};
proxy = table.createProxy(tab);//创建一个代理表,以管理对tab的存取访问

io.open();
c = proxy.x; //显示 "x被读了"
proxy.y = 19; //显示 "y被修改值为19"
io.print(proxy.y); //显示 "y被读了" 然后显示19

所有的元方法

元方法/属性 函数定义 Python中的魔法方法 说明
_weak 用不到
_type 属性 type(obj)函数的行为
_readonly 属性 等于false,_开头的成员也不是只读属性
_defined 感觉没啥用
_keys 属性 可用于table.keys等函数动态获取对象的键名列表(例如动态生成键值对的外部JS对象可使用这个元方法返回成员名字列表
_startIndex 属性 用于table.eachIndex等函数动态指定数组的开始下标。
_get function(k,ownerCall) __getattr____getitem__ 如果读取表中不存在的键会触发_get元方法并返回值
_set function(k,v) __setattr____setitem__ 当你给表的一个缺少的键赋值时会触发_set元方法
_tostring function(...) __str____repr__ tostring(obj, ...)
_tonumber function() tonumber(obj)
_json function() web.json.stringify(obj),可返回一个可被转化为json的值。或者返回一个字符串和true
_toComObject 用于自定义一个表对象如何转换为 COM 对象,可定义为函数,也可以直接定义为对象
_eq function(b) __eq____ne__ ==!=,比较对象时,两个对象的_eq必须是同一个
_le function(b) __le____ge__ <=>=
_lt function(b) __lt____gt__ <>
_add function(b) __add__ +
_sub function(b) __sub__ -
_mul function(b) __mul__ *
_div function(b) __truediv__ /
_lshift function(b) __lshift__ << 左移
_rshift function(b) __rshift__ >> 右移
_mod function(b) __mod__ % 取模
_pow function(b) __pow__ **幂运算
_unm function() __neg__ - 负号
_len function() __len__ #取长运算符,Python中则为len函数
_concat function(b) ++ 连接运算符
_call function(...) __call__ 对象当函数来调用

属性元表

不仅可以给对象定义元表,也可以给对象的属性定义一个元表,有点类似于Python中的property,可以控制属性修改和获取的行为。

如果要看例子的话,可以在aardio的目录全局搜下@_metaProperty

以使用最多的属性text为例,基本每个控件都有一个text属性,你可以很方便的通过.text获取和修改空间显示的文字。

其实不用属性元表也能实现这个效果,代码如下:

import console; 
class staticText{
	getText = function(){
		..console.log("获取到界面文本内容")
	};
	setText = function(v){
		..console.log("将文本("+v+")显示到界面控件上")
	}
	@_meta;
}

namespace staticText{
    _meta = {
        _get = function(k){
        	if(k == "text"){
        	    return owner.getText();
        	}
        };
        _set = function(k,v){
        	if(k == "text"){
        	    return owner.setText(v);
        	}
        }    
    }
}

s = staticText()
console.log(s.text);
s.text = "修改文本";
console.pause(true);

但是如果属性多了的话,就需要一堆的if来判断属性,所以aardio作者就引入了metaProperty这个功能。这样写的代码看起来更简洁和清晰,用法如下:

import console; 
import util.metaProperty;

class staticText{
	getText = function(){
		..console.log("获取到界面文本内容")
	};
	setText = function(v){
		..console.log("将文本("+v+")显示到界面控件上")
	}
	@_metaProperty;
}

namespace staticText{
    _metaProperty = ..util.metaProperty(
        text = {
            _get = function(){
            	return owner.getText();
            };
            _set = function(v){
            	return owner.setText(v);
            }
        };
        // 可以写其他属性
    );
    // 可以打印下_metaProperty看看
    ..console.dump(_metaProperty);
}

s = staticText()
console.log(s.text);
s.text = "修改文本";
console.pause(true);

本文由博客一文多发平台 OpenWrite 发布!文章来源地址https://www.toymoban.com/news/detail-841688.html

到了这里,关于aardio教程三) 元表、元方法的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • aardio教程五) 写Python风格的aardio代码(字符串篇)

    熟悉一个新的语言最麻烦的就是需要了解一些库的使用,特别是基础库的使用。 所以我想给aardio封装一个Python风格的库,Python里的基础库是什么方法名,aardio里也封装同样的方法名。 这样就不需要单独去了解aardio里一些方法的使用细节,可以很方便的将Python代码改成aardio代

    2024年04月08日
    浏览(41)
  • aardio教程二) 进阶语法

    aardio中除了基础数据类型外,其他的复合对象都是table(例如类和名字空间等)。table可以用来存放aardio的任何对象,包括另一个table。 在其他语言中的字典、列表、数组、集合映射等,在aardio中都使用table来实现。 创建字典 创建数组 数组和字典可以混用 使用class定义类

    2024年03月18日
    浏览(57)
  • aardio教程) 搭建自己的扩展库仓库

    aardio中有些经常使用的库,换个项目总需要复制一下,还不便于修改。虽然可以直接把它放到 aardiolib 目录下,也是不便于共享给其他人使用。 最近偶然翻到编辑器里的 工具 - 开发环境 - 扩展库发布工具 ,就想着可以像官方一样,发布自己的扩展库,也便于分享给大家使用

    2024年03月27日
    浏览(36)
  • PostgreSQL详细教程(一)—— 前言

    目录 PostgreSQL简介 PostgreSQL 特征   PostgreSQL 是一个免费的对象-关系数据库服务器(ORDBMS),在灵活的BSD许可证下发行。 PostgreSQL 开发者把它念作 post-gress-Q-L。 PostgreSQL 的 Slogan 是 \\\"世界上最先进的开源关系型数据库\\\"。 PostgreSQL与Oracle一样是使用共享内存的进程结构,而大家都比较

    2024年02月12日
    浏览(47)
  • 【Java基础教程】(四十八)集合体系篇 · 上:全面解析 Collection、List、Set常用子接口及集合元素迭代遍历方式~【文末送书】

    掌握 Java 设置类集的主要目的以及核心接口的使用; 掌握 Collection 接口的作用及主要操作方法; 掌握 Collection 子接口 List、Set 的区别及常用子类的使用; 掌握 Map 接口的定义及使用; 掌握集合的4种输出操作语法结构; 掌握 Properties类的使用 ; 了解类集工具类 Collections 的作

    2024年02月15日
    浏览(57)
  • 《Git入门实践教程》前言+目录

    版本控制系统(VCS)在项目开发中异常重要,但和在校大学生的交流中知道,这个重要方向并未受到重视。具备这一技能,既是项目开发能力的体现,也可为各种面试加码。在学习体验后知道,Git多样化平台、多种操作方式、丰富的资源为业内人士提供了方便的同时,也造成

    2024年02月10日
    浏览(72)
  • 【RabbitMQ教程】前言 —— 中间件介绍

                                                                       💧 【 R a b b i t M Q 教程】前言——中间件介绍 color{#FF1493}{【RabbitMQ教程】前言 —— 中间件介绍} 【 R abbi tMQ 教程】前言 —— 中间件介绍 💧           🌷 仰望天空,妳

    2024年02月08日
    浏览(70)
  • 《Docker极简教程》--前言--Docker的简介

    Docker 是一种用于构建、部署和运行应用程序的开源平台,它使用容器技术来实现轻量级、可移植和自包含的应用程序环境。Docker 的核心思想是将应用程序及其依赖项打包到一个称为容器的封闭单元中,从而消除了在不同环境中运行应用程序时可能出现的许多兼容性和依赖性

    2024年02月21日
    浏览(52)
  • 【k8s完整实战教程0】前言

    系列文章:这个系列已完结,如对您有帮助,求点赞收藏评论。 读者寄语: 再小的帆,也能远航! 【k8s完整实战教程0】前言 【k8s完整实战教程1】源码管理-Coding 【k8s完整实战教程2】腾讯云搭建k8s托管集群 【k8s完整实战教程3】k8s集群部署kubesphere 【k8s完整实战教程4】使用

    2023年04月24日
    浏览(40)
  • 【自制视频课程】C++OpnecV基础35讲——第一章 前言

            首先,opencv是一个广泛使用的计算机视觉库,它提供了丰富的图像处理和计算机视觉算法,可以帮助我们快速地开发出高质量的图像处理应用程序;         其次,opencv是一个开源库,可以免费使用和修改,这为我们提供了一个学习和研究计算机视觉的良好平

    2024年02月05日
    浏览(55)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包