内置编译标记

仓颉语言提供了一些预定义的编译标记,可以通过这些编译标记控制仓颉编译器的编译行为。

源码位置

仓颉提供了几个内置编译标记,用于在编译时获取源代码的位置。

  • @sourcePackage() 展开后是一个 String 类型的字面量,内容为当前宏所在的源码的包名
  • @sourceFile() 展开后是一个 String 类型的字面量,内容为当前宏所在的源码的文件名
  • @sourceLine() 展开后是一个 Int64 类型的字面量,内容为当前宏所在的源码的代码行

这几个编译标记可以在任意表达式内部使用,只要能符合类型检查规则即可。示例如下:

func test1() {
    let s: String = @sourceFile()  // The value of `s` is the current source file name
}

func test2(n!: Int64 = @sourceLine()) { /* at line 5 */
    // The default value of `n` is the source file line number of the definition of `test2`
    println(n) // print 5
}

条件编译

条件编译使用 @When 标记,是一种在程序代码中根据特定条件选择性地编译不同代码段的技术。条件编译的作用主要体现在以下几个方面:

  • 平台适应:支持根据当前的编译环境选择性地编译代码,用于实现跨平台的兼容性。
  • 功能选择:支持根据不同的需求选择性地启用或禁用某些功能,用于实现功能的灵活配置。例如,选择性地编译包含或排除某些功能的代码。
  • 调试支持:支持调试模式下编译相关代码,用于提高程序的性能和安全性。例如,在调试模式下编译调试信息或记录日志相关的代码,而在发布版本中将其排除。
  • 性能优化:支持根据预定义的条件选择性地编译代码,用于提高程序的性能。

关于条件编译的具体内容,可以参考条件编译章节,这里不再额外展开。

@FastNative

为了提升与 C 语言互操作的性能,仓颉提供 @FastNative 标记用于优化对 C 函数的调用。值得注意的是 @FastNative 只能用于 foreign 声明的函数。

使用示例如下:

@FastNative
foreign func strlen(str: CPointer<UInt8>): UIntNative

开发者在使用 @FastNative 修饰 foreign 函数时,应确保对应的 C 函数满足以下两点要求:

  1. 函数的整体执行时间不宜太长。例如:不允许函数内部存在很大的循环;不允许函数内部产生阻塞行为,如,调用 sleepwait 等函数。
  2. 函数内部不能调用仓颉方法。

@Attribute

仓颉语言内部提供 @Attribute 标记,开发者通过内置的 @Attribute 来对某个声明设置属性值,从而达到标记声明的目的。属性值可以是 identifier 或者 string,下面是一个简单的例子,这段示例代码为变量 cnt 添加了一个 identifier 类型的属性 State,为变量 bcnt 添加了一个 string 类型的属性 "Binding"

@Attribute[State] var cnt = 0       // identifier
@Attribute["Binding"] var bcnt = 0  // string

同时,标准库 std.ast 包提供了 getAttrs() 方法用于获取节点的属性,以及 hasAttr(attrs: String) 方法用于判断当前节点是否具有某个属性,下面是一个具体的例子。

宏定义如下:

public macro Component(input: Tokens): Tokens {
    var varDecl = parseDecl(input)
    if (varDecl.hasAttr("State")) { // 如果改节点被标记了属性且值为 “State” 返回 true, 否则返回 false
        var attrs = varDecl.getAttrs() // 返回一组 Tokens
        println(attrs[0].value)
    }
    return input
}

宏调用如下:

@Component(
    @Attribute[State] var cnt = 0
)