1.什么是仓颉宏

在企业级的软件开发市场,java语言长期处于绝对领先的位置,其中的原因比较多,比如开放性、生态等等,除此之外,java语言在开发过程中的易用性,也为此做出了贡献,比如java中的注解,通过简单的一个标记,就能完成复杂的功能,而spring等第三方框架的出现,更是让注解如虎添翼,大大简化了java程序的开发工作,初学者经过一段时间的培训,就可以使用基于注解的spring进行企业级开发。

仓颉语言作为一种面向应用层的通用开发语言,和java的开发范围高度重合,在对标java注解这一方向上,仓颉的宏在性能上有一定的优势,因为仓颉的宏是编译期展开的,不需要运行时处理,这样,性能会更高一点。不过,java注解和仓颉的宏并不完全一致,各有各的特点,使用得当的话都可以简化开发工作。

2.仓颉宏的定义

仓颉中宏的功能类似于函数,用来对输入的代码序列进行处理,输出处理后的代码序列,这个从输入代码映射到输出代码的过程称为宏展开。

和函数相比,宏定义具有如下特点:

宏定义所在的 package 需使用 macro package 来声明。宏定义需要使用关键字 macro。宏定义的输入和输出类型必须是 Tokens。宏调用需要使用 @。

3.仓颉宏的使用示例

以下的示例代码模拟了登录以及入库、出库、查看库存等动作,包括多个类和顶层函数,假如需要把所有的函数调用都记录日志,也就是记录调用开始和结束的时间,以及传入的参数和返回的值,并且支持日志写入到控制台或者日志文件。

main(): Unit {

//模拟登录

if (!login("admin", "qD@0532")) {

println("用户名或者密码错误!")

return

}

//添加图书

let book = Book("仓颉语言元编程")

//模拟入库

book.input(88)

//模拟出库

book.output(66)

//查看库存

println(book.stock)

}

//登录

func login(userName: String, password: String): Bool {

let user = User.getUserByUserName(userName)

return user.passwd == password

}

//用户

public class User {

public User(var userId: Int64, var userName: String, var passwd: String) {}

//根据用户名称查找用户信息

public static func getUserByUserName(userName: String) {

return User(1, userName, "qD@0532")

}

}

public class Book {

public Book(var bookName: String, var stock: Int64) {}

public init(bookName: String) {

this.bookName = bookName

this.stock = 0

}

//入库

public func input(count: Int64) {

this.stock += count

return stock

}

//出库

public func output(count: Int64) {

this.stock -= count

return stock

}

}

要完成这样的功能,常规写法可能需要几百行代码才能实现,而且代码会嵌入到现有的函数中,带来一定的调试困难。但是,如果有了元编程的支持,通过宏定义一切就可以迎刃而解了。

假如已经自定义了一个名称为Cradle的宏,它可以通过切面织入的方式,自动拦截函数的调用,并且按照属性宏的入参来执行向控制台或者日志文件写入日志,那么,针对上面的要求,通过在函数和类的定义位置添加上Cradle宏调用即可,修改后的代码如下:

import cradle.*

from std import time.*

from std import collection.*

from std import fs.*

from std import io.*

from std import os.posix.*

@Cradle

main(): Unit {

//模拟登录

if (!login("admin", "qD@0532")) {

println("用户名或者密码错误!")

return

}

//添加图书

let book = Book("仓颉语言元编程")

//模拟入库

book.input(88)

//模拟出库

book.output(66)

//查看库存

println(book.stock)

}

//登录

@Cradle[console|logfile]

func login(userName: String, password: String): Bool {

let user = User.getUserByUserName(userName)

return user.passwd == password

}

//用户

@Cradle[console|logfile]

public class User {

public User(var userId: Int64, var userName: String, var passwd: String) {}

//根据用户名称查找用户信息

public static func getUserByUserName(userName: String) {

return User(1, userName, "qD@0532")

}

}

@Cradle[console|logfile]

public class Book {

public Book(var bookName: String, var stock: Int64) {}

public init(bookName: String) {

this.bookName = bookName

this.stock = 0

}

//入库

public func input(count: Int64) {

this.stock += count

return stock

}

//出库

public func output(count: Int64) {

this.stock -= count

return stock

}

}

可以看到,一共只加了四个注解,没有破坏原来函数的结构,既简单明了,又做到了逻辑分离。

下面是运行后的效果,包括输出到控制台的日志和写入到日志文件的日志:

通过上述示例,可以看到,基于仓颉元编程的仓颉宏功能还是很强大的,通过自定义宏可以帮助开发者实现天马行空的想法,在注重性能和效率的企业级开发上更是大有可为。

4.仓颉元编程示例获取

《仓颉语言元编程》是由张磊编写,清华大学出版社出版的一本仓颉元编程学习的入门书籍,该书从元编程的概念开始,逐步讲解仓颉元编程的基础知识、抽象语法树的常用用法,以及如何定义和使用仓颉宏。

该书包括将近60个完整的元编程示例,最新示例代码已全面适配仓颉最新的版本0.51.4,并上传到码云,全部可以免费下载:

https://gitee.com/zl3624/cangjie_meta_programming