【问题标题】:Swift: macro for __attribute__((section))Swift:__attribute__((section)) 的宏
【发布时间】:2016-03-31 19:48:42
【问题描述】:

这是一个奇怪且非 Swift-thonic 的问题,请耐心等待。
我想在 Swift 中做一些与我目前在 Objective-C/C++ 中做的事情一样的事情,所以我将首先描述它。

我有一些现有的 C++ 代码定义了一个宏,当在表达式代码中的任何位置 中使用该宏时,将在编译时将一个条目插入到二进制文件的表中。换句话说,用户写的是这样的:

#include "magic.h"

void foo(bool b) {
    if (b) {
        printf("%d\n", MAGIC(xyzzy));
    }
}

感谢定义

#define MAGIC(Name) \
  []{ static int __attribute__((used, section("DATA,magical"))) Name; return Name; }()

在编译时实际发生的是一个名为xyzzy(模名称-mangling)的静态变量被创建并分配到我的Mach-O二进制文件的特殊magical部分,所以运行nm -m foo.o 来转储符号显示了很多类似这样的内容:

0000000000000098 (__TEXT,__eh_frame) non-external EH_frame0
0000000000000050 (__TEXT,__cstring) non-external L_.str
0000000000000000 (__TEXT,__text) external __Z3foob
00000000000000b0 (__TEXT,__eh_frame) external __Z3foob.eh
0000000000000040 (__TEXT,__text) non-external __ZZ3foobENK3$_0clEv
00000000000000d8 (__TEXT,__eh_frame) non-external __ZZ3foobENK3$_0clEv.eh
0000000000000054 (__DATA,magical) non-external [no dead strip] __ZZZ3foobENK3$_0clEvE5xyzzy
                 (undefined) external _printf

通过getsectbynamefromheader() 的魔力,我可以加载magical 部分的符号表,扫描它,并找出(通过分解我找到的每个符号)在用户代码中的某个位置,他致电MAGIC(xyzzy)。尤里卡!


我可以在 Swift 中很好地复制整个工作流程的后半部分——从 getsectbynamefromheader() 部分开始。然而,第一部分让我很难过。

  • Swift 没有预处理器,因此不可能像 MAGIC(someidentifier) 那样优雅地拼写魔法。不过,我不希望它太丑。

  • 据我所知,Swift 无法将符号插入给定的部分——没有 __attribute__((section)) 的等价物。不过这没关系,因为我的计划中没有任何内容需要专门的部分;那部分只是让下半场变得更容易。

  • 据我所知,在 Swift 中将符号放入符号表的唯一方法是通过本地结构定义。像这样的:

    func foo(b: Bool) -> Void {
        struct Local { static var xyzzy = 0; };
        println(Local.xyzzy);
    }
    

这行得通,但它有点额外的输入,并且不能在表达式中内联完成(如果我们无论如何都不能在 Swift 中创建 MAGIC 宏,那就没关系了),而且我'我担心 Swift 编译器可能会将其优化掉。


所以,这里有三个问题,都是关于如何让 Swift 做 Swift 不想做的事情:宏、属性和创建抵抗编译器优化的符号。

我知道@asmname,但我认为这对我没有帮助,因为我已经可以自己处理解构了。

我知道 Swift 有“泛型”,但它们似乎更接近于 Java 泛型而不是 C++ 模板;在这种特殊情况下,我认为它们不能用作宏的替代品。

我知道 Swift 编译器的代码 is now open-source;我徒劳地浏览了一些内容;但我无法通读全部,寻找可能根本不存在的技巧。

【问题讨论】:

  • 我觉得 Swift 对于这样的工作来说水平太高了。 C 的存在是有原因的。
  • @BryanChen:允许解决方案涉及调用 C/ObjC 函数。但它必须可以从 Swift 中使用,因为这是编写应用程序/业务逻辑代码的语言,而应用程序/业务逻辑是需要使用 MAGIC 的语言。也就是说:“在 Objective-C 中编写 MAGIC 的部分实现”在这种情况下是可以接受的答案(但显然需要充实实现细节)。 “继续在 Objective-C 中编写所有应用程序逻辑,以便您可以继续使用旧的 MAGIC”在这种情况下不是可接受的答案。

标签: swift swift2 metaprogramming mach-o


【解决方案1】:

这是您关于预处理器(和宏)问题的答案。

Swift 没有预处理器,因此不可能像 MAGIC(someidentifier) 那样优雅地拼写魔法。不过,我不希望它太丑。

Swift 项目有一个预处理器(但是,AFAIK,它没有随 Swift 的二进制文件一起分发)。

来自swift-users 邮件列表:

What are .swift.gyb files?

它是 Swift 的预处理器 团队这样写道,当他们需要建造,比如说,十个几乎相同的 Int 的变体,他们不必从字面上复制和粘贴相同的 代码十次。如果您打开其中一个文件,您会看到它们是 主要是 Swift 代码,但也混杂了一些用 Python 编写的代码。

它不如 C 宏漂亮,但恕我直言,它更强大。 克隆 Swift 的 git repo 后,您可以使用 ./swift/utils/gyb --help 命令查看可用命令。

$ swift/utils/gyb --help

用法等 (TL;DR)...

Example template:

      - Hello -
    %{
         x = 42
         def succ(a):
             return a+1
    }%

    I can assure you that ${x} < ${succ(x)}

    % if int(y) > 7:
    %    for i in range(3):
    y is greater than seven!
    %    end
    % else:
    y is less than or equal to seven
    % end

      - The End. -

When run with "gyb -Dy=9", the output is

      - Hello -

    I can assure you that 42 < 43

    y is greater than seven!
    y is greater than seven!
    y is greater than seven!

      - The End. -

GitHub.Gist 上提供了我的 GYB 使用示例。

对于更复杂的示例,请在 @apple/swift/stdlib/public/core 中查找 *.swift.gyb 文件。

【讨论】:

    猜你喜欢
    • 2013-08-23
    • 1970-01-01
    • 2016-02-13
    • 2021-01-19
    • 1970-01-01
    • 2020-05-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多