探索如何使用 Swift
宏来使您的代码库更具表现力和更易于阅读。在我们探索宏如何帮助您避免编写重复代码并了解如何在您的应用程序中使用它们时,请继续编写代码。我们将分享宏的构建块,向您展示如何对其进行测试,并指导您了解如何从宏中发出编译错误。
@freestanding(expression)
public macro stringify<T>(_ value: T) -> (T, String) = #externalMacro(module: "WWDCMacros", type: "StringifyMacro")
这段代码是一个使用
Swift
的宏定义的例子。代码中定义了一个名为stringify
的宏函数,该函数接受一个参数value
,并返回一个元组(T, String)
。宏函数使用了#externalMacro
指令来引用外部模块中的宏定义。
宏函数的作用是将传入的值转换为字符串形式,并返回原始值和对应的字符串。宏函数的具体实现在名为"StringifyMacro"
的外部模块中,该模块的实现可以在代码中其他地方进行定义和导入。
此代码片段中的@freestanding(expression)
注解表示该宏定义是在无依赖环境中运行的,即不依赖于任何其他代码或库。
在上面的注解中,
@freestanding(expression)
表示这个宏函数是一个自由函数(freestanding function)
宏,它可以在任何上下文中使用,而不仅仅局限于特定的语法结构或类型。
在这个特定的宏函数中,expression
是一个泛型参数,用于接受传入的表达式作为参数。在宏函数的实现中,可以使用这个表达式进行进一步的处理和展开。
例如,在StringifyMacro
的expansion
方法中,通过node.argumentList.first?.expression
获取传入的表达式作为参数,并对该表达式进行处理和展开。这个表达式可以是任何合法的表达式,根据具体的使用情况来确定。
总结来说,expression
在这个上下文中表示宏函数接受的表达式参数。
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
方法是宏函数的具体实现。该方法接受两个参数:node
和context
,分别表示宏函数的展开节点和展开上下文。
在expansion
方法中,首先通过node.argumentList.first?.expression
来获取宏函数的第一个参数表达式。如果参数不存在,则抛出一个错误。
然后,通过字符串插值的方式构建一个新的表达式语法树,该表达式由原始参数表达式和该参数表达式的描述字符串组成。这里使用了literal
辅助函数来将字符串转化为表达式。
最后,将构建的表达式返回作为宏函数的展开结果。
总的来说,上述代码实现了一个简单的宏函数,它将传入的参数表达式转化为一个包含原始参数和参数描述的元组表达式。这样,使用该宏函数时,可以方便地获取参数的值和描述信息。
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
的模块,该模块可能包含了宏函数的实现。
然后,定义了两个整数变量a
和b
,分别赋值为17
和25
。
接下来,使用#stringify
宏函数对表达式a + b
进行字符串化操作,将其转化为一个元组(result, code)
。
最后,通过
运行该代码,将输出以下内容:
The value 42 was produced by the code "a + b"
这说明宏函数将表达式 a + b
转化为了字符串 "a + b"
,并将计算结果 42
赋值给了 result
。
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
的单元测试。
首先,定义了一个名为WWDCTests
的XCTestCase
子类。在该类中,声明了一个名为testMacro
的测试方法。
在testMacro
方法中,调用了assertMacroExpansion
函数,用于断言宏函数的扩展结果是否符合预期。
assertMacroExpansion
函数接受三个参数:
input:
要扩展的源代码字符串。
expandedSource:
期望的宏扩展后的源代码字符串。
macros:
包含了宏函数名称和对应的宏类型的字典。在测试代码中,使用#stringify(a + b)
作为输入,表示要对表达式a + b
进行字符串化操作。预期的宏扩展结果是(a + b, "a + b")
,即将表达式本身(a + b)
和它的字符串表示形式"a + b"
组成一个元组。
最后,通过定义了一个名为testMacros
的字典,将宏函数名称"stringify"
和对应的宏类型StringifyMacro.self
关联起来。
这样,测试代码就准备好了,可以通过运行测试来验证宏函数的扩展结果是否与预期一致。
网友评论