WWDC2023-编写 Swift 宏

探索如何使用 Swift 宏来使您的代码库更具表现力和更易于阅读。在我们探索宏如何帮助您避免编写重复代码并了解如何在您的应用程序中使用它们时,请继续编写代码。我们将分享宏的构建块,向您展示如何对其进行测试,并指导您了解如何从宏中发出编译错误。

1、stringify 宏的声明

@freestanding(expression)
public macro stringify<T>(_ value: T) -> (T, String) = #externalMacro(module: "WWDCMacros", type: "StringifyMacro")

这段代码是一个使用 Swift 的宏定义的例子。代码中定义了一个名为 stringify 的宏函数,该函数接受一个参数 value,并返回一个元组 (T, String)。宏函数使用了 #externalMacro 指令来引用外部??橹械暮甓ㄒ濉?br> 宏函数的作用是将传入的值转换为字符串形式,并返回原始值和对应的字符串。宏函数的具体实现在名为 "StringifyMacro" 的外部??橹?,该??榈氖迪挚梢栽诖胫衅渌胤浇卸ㄒ搴偷既?。
此代码片段中的 @freestanding(expression)注解表示该宏定义是在无依赖环境中运行的,即不依赖于任何其他代码或库。

在上面的注解中,@freestanding(expression) 表示这个宏函数是一个自由函数(freestanding function)宏,它可以在任何上下文中使用,而不仅仅局限于特定的语法结构或类型。
在这个特定的宏函数中,expression是一个泛型参数,用于接受传入的表达式作为参数。在宏函数的实现中,可以使用这个表达式进行进一步的处理和展开。
例如,在 StringifyMacroexpansion 方法中,通过 node.argumentList.first?.expression 获取传入的表达式作为参数,并对该表达式进行处理和展开。这个表达式可以是任何合法的表达式,根据具体的使用情况来确定。
总结来说,expression 在这个上下文中表示宏函数接受的表达式参数。

2、stringify 宏的实现

public struct StringifyMacro: ExpressionMacro {
    public static func expansion(
        of node: some FreestandingMacroExpansionSyntax,
        in context: some MacroExpansionContext
    ) -> ExprSyntax {
        guard let argument = node.argumentList.first?.expression else {
            fatalError("compiler bug: the macro does not have any arguments")
        }

        return "(\(argument), \(literal: argument.description))"
    }
}

上述代码是一个 Swift 宏函数的示例实现,用于实现字符串化操作。
这段代码定义了一个名为 StringifyMacro 的结构体,它采用了 ExpressionMacro 协议。ExpressionMacro 是用于表达式级宏函数的协议,它规定了宏函数需要实现的方法。
StringifyMacro 结构体中的 expansion 方法是宏函数的具体实现。该方法接受两个参数:nodecontext,分别表示宏函数的展开节点和展开上下文。
expansion 方法中,首先通过 node.argumentList.first?.expression 来获取宏函数的第一个参数表达式。如果参数不存在,则抛出一个错误。
然后,通过字符串插值的方式构建一个新的表达式语法树,该表达式由原始参数表达式和该参数表达式的描述字符串组成。这里使用了 literal 辅助函数来将字符串转化为表达式。
最后,将构建的表达式返回作为宏函数的展开结果。
总的来说,上述代码实现了一个简单的宏函数,它将传入的参数表达式转化为一个包含原始参数和参数描述的元组表达式。这样,使用该宏函数时,可以方便地获取参数的值和描述信息。

3、调用 stringify 宏

import WWDC

let a = 17
let b = 25

let (result, code) = #stringify(a + b)

print("The value \(result) was produced by the code \"\(code)\"")

上述代码使用了名为 #stringify 的宏函数来将表达式 a + b转化为字符串。
首先,通过 import WWDC 导入了一个名为 WWDC 的??椋媚?榭赡馨撕旰氖迪帧?br> 然后,定义了两个整数变量 ab,分别赋值为 1725。
接下来,使用 #stringify 宏函数对表达式 a + b 进行字符串化操作,将其转化为一个元组 (result, code)。
最后,通过 print 函数打印出字符串,其中包含了转化后的结果和代码。
运行该代码,将输出以下内容:

The value 42 was produced by the code "a + b"

这说明宏函数将表达式 a + b 转化为了字符串 "a + b",并将计算结果 42 赋值给了 result

4、stringify 宏测试

final class WWDCTests: XCTestCase {
    func testMacro() {
        assertMacroExpansion(
            """
            #stringify(a + b)
            """,
            expandedSource: """
            (a + b, "a + b")
            """,
            macros: testMacros
        )
    }
}

let testMacros: [String: Macro.Type] = [
    "stringify": StringifyMacro.self
]

上述代码是一个针对宏函数 #stringify 的单元测试。
首先,定义了一个名为 WWDCTestsXCTestCase 子类。在该类中,声明了一个名为 testMacro 的测试方法。
testMacro 方法中,调用了 assertMacroExpansion 函数,用于断言宏函数的扩展结果是否符合预期。
assertMacroExpansion 函数接受三个参数:
input:要扩展的源代码字符串。
expandedSource:期望的宏扩展后的源代码字符串。
macros:包含了宏函数名称和对应的宏类型的字典。在测试代码中,使用 #stringify(a + b) 作为输入,表示要对表达式 a + b 进行字符串化操作。预期的宏扩展结果是 (a + b, "a + b"),即将表达式本身 (a + b)和它的字符串表示形式 "a + b" 组成一个元组。
最后,通过定义了一个名为testMacros的字典,将宏函数名称 "stringify" 和对应的宏类型 StringifyMacro.self关联起来。
这样,测试代码就准备好了,可以通过运行测试来验证宏函数的扩展结果是否与预期一致。

最后编辑于
?著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,029评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,238评论 3 388
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,576评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,214评论 1 287
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,324评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,392评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,416评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,196评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,631评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,919评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,090评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,767评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,410评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,090评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,328评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,952评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,979评论 2 351

推荐阅读更多精彩内容