【问题标题】:Can C sort at compile time?C可以在编译时排序吗?
【发布时间】:2015-05-29 21:28:15
【问题描述】:

是否可以在 C 编译时对元素进行排序?

语法是次要的,我在想这样一个宏:

SORT(9, -1, 12, 4)   // expands to: -1, 4, 9, 12
SORT(dog, cat, cow)  // expands to: cat, cow, dog

但我不会对任何 API 皱眉,只要它在不发出单个 CPU 指令的情况下进行排序。

要求相当宽松:

  • 纯 C,没有 C++。保持在 C 标准内会很好,但已建立的语言扩展是公平的游戏。
  • 编译器是唯一允许的工具。不欢迎 Unix sort 或自制代码生成器,因为它们会使构建步骤复杂化。
  • 任何元素类型都可以。如果它可以排序就可以了,例如数字,但不是字符串。
  • 固定长度的 API 很好。必须调用 SORT_5 来对 5 个元素进行排序。

我意识到解决方案可能会求助于一些几乎无法编译的语言巫术,我只是想知道它是否可能。

【问题讨论】:

  • 我称之为预处理器。基本上,您编写一个解析器,在调用编译器之前修改源代码。要么这样,要么只是手动整理东西。
  • 我认为你可以在 C++14 中使用可变参数模板来实现。但在纯 C 语言中,我不这么认为。
  • 您可以在 C 中使用不同的宏函数来执行此操作,以使用 ?: 语法在每个 i 位置获取每个 n 元素。但我认为它不会在编译时使用字符串进行评估。 int 应该被评估,但实际上没有办法强制编译器不使用 const_expr。
  • 如果Boost.Preprocessor做不到,那估计也做不到了。
  • Having to call SORT_5 to sort 5 elements is OK 然后你可以手动写SORT_5():你可以用完全不可读的? :链来做。 (不完全是预处理器排序,但如果参数是常量,它可能会在编译时进行评估。)或者编写一个程序为你生成SORT_2..50()

标签: c sorting compile-time


【解决方案1】:

不是真的,你不能在编译时在 C 中排序(至少如果你想对足够多的编译时常量整数进行排序;在这种情况下,有 300 个宏或函数名为 SORT_1SORT_2 , ... SORT_300 不实用,除非您生成这些您不想生成的函数或宏),但是....

实用的方法是使用您自己的或其他一些预处理器(例如gpp)并让它进行排序。或者简单地说,在某些包含的文件中包含数字并生成该排序文件(例如,使用awksortmake 规则)

您还可以考虑使用 link-time-optimization 链接到 LTO 编译的 libc,使用其 LTO 优化的 qsort。这不常见,AFAIK,你不能保证编译器足够聪明,可以内联你的 LTO qsort 等......(AFAIK,当前的 C 编译器不这样做)。

如果使用最近的GCC(4.8、4.9 或即将在 2015 年 3 月发布的 5.0)进行编译,您可以自定义它(例如使用 MELT 或使用您自己的 C++ 插件)来定义您的 __builtin_my_compile_time_sort(或者一些_Pragma) 来完成这项工作。这是特定于编译器的(可能意味着几天的工作,除非你已经知道 MELT)

最简单的方法是接受稍微复杂的构建步骤。这没什么大不了的。

【讨论】:

    【解决方案2】:

    这是一个宏观方法:

    #define GET_1_of_3(a, b, c) ((a) < (b) ? ((c) < (a) ? (c) : (a)) : ((c) < (b) ? (c) : (b)))
    #define GET_2_of_3(a, b, c) ((c) > (a) && (c) < (b) || (c) < (a) && (c) > (b) ? (c) : ((b) > (a) && (b) < (c) || (b) < (a) && (b) > (c) ? (b) : (a)))
    #define GET_3_of_3(a, b, c) ((a) > (b) ? ((c) > (a) ? (c) : (a)) : ((c) > (b) ? (c) : (b)))
    #define SORT_3(a, b, c) GET_1_of_3(a, b, c),GET_2_of_3(a, b, c),GET_3_of_3(a, b, c)
    
    void main(){
        int x[3] = { SORT_3(6,2,3) };
        printf("%d, %d, %d", x[0], x[1], x[2]);
    }
    

    这适用于 int 并且适用于 C,但是对于没有来自 C++ 的 const_expr 的字符串是不可能的。很显然,你是在为大量的宏编写来支持大量的SORT_X

    【讨论】:

    • 很好,我认为甚至可以自动编写表达式以获取第 n 个元素。它可以用于带有多字符文字的小字符串(以及它们带来的所有可移植性问题)。
    • 对一个可怕的、脆弱的、可能是灾难性的答案投反对票多少次是可以接受的?考虑: int i=0; int y[3] = {6, 2, 3}; int z[3] = { SORT_3(y[i++], y[i++], y[i++]) };.你得到了什么?至少,在宏中对参数的使用加上括号,将逻辑运算符(&& 和 || 正确)加上括号并打开 -Wall。
    • 我在(a) 等周围添加了括号,因为这是持久宏的必需实践,但请记住,这显然是一个演示开始的玩具示例。另外,用括号括起来 && 和 ||是浪费时间,几乎从来没有帮助代码可读。你会写出(3*x) + (4*y) 的代数吗?
    【解决方案3】:

    让我们考虑对只能是0或1的数字进行排序。对于两个数字,下面代码中的SORT2可以对它们进行排序:

    #define SORT2(a,b) SORT2_##a##b
    #define SORT2_00 0,0
    #define SORT2_01 0,1
    #define SORT2_10 0,1
    #define SORT2_11 1,1
    

    这当然可以扩展到更大的范围和更多的参数。

    【讨论】:

    • 这可能适用于任何事情,但您必须为每个可能的输入进行定义。如果没有非常大的文件,您甚至无法支持对 2 个通用 int 进行排序。但在某些有限的应用程序中,例如小型枚举,它可能是可行的。
    • 我知道这些限制,但问题是“任何元素类型都可以”。也许可以“编码”更大的数字以减少所需的#defines 的数量,例如,可以将123 编码为((1,2),3) 并使用一些专门编写的宏来比较两个数字。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-10-29
    • 1970-01-01
    • 2014-01-16
    • 1970-01-01
    • 1970-01-01
    • 2020-04-21
    • 2022-10-05
    相关资源
    最近更新 更多