在编译时计算一组常量表达式的最大值

Posted

技术标签:

【中文标题】在编译时计算一组常量表达式的最大值【英文标题】:Calculating maximum value of a set of constant expressions at compile time 【发布时间】:2019-05-06 06:40:52 【问题描述】:

我试图在编译时计算 Rust 过程宏(派生宏)内的一组常量的最大值。

宏看起来像:

fn get_max_len() -> TokenStream 
    // Each TokenStream represents a constant expression
    let len: Vec<TokenStream> = get_constant_lengths();

    quote! 
      // #(#len),* gets expanded to #len[0], #len[1], #len[2]...
      const LEN: usize = std::cmp::max(#(#len),*);
    

问题在于 std::cmp::max 是一个函数,因此不能在常量表达式中使用(至少在 const fn 稳定之前 - 如果可能的话,我想保持稳定的 Rust)。

如何在编译时计算一组常量的最大值?

我也许可以编写一个max! 宏,它基本上可以递归地构造一个巨大的ifs 链,但我希望有一个更简洁的解决方案。

【问题讨论】:

为什么不只评估max outsidequote 宏调用,只把结果值放在里面? 不幸的是,这种情况下的常量通常是来自接口的关联常量,因此我无法直接访问该值 - 我所拥有的是将编译器作为常量评估的表达式。为了计算出这些值,我必须实现一个可以提取值的评估器,在某些情况下是递归的,这是不可行的。 ifs 的巨大链if 目前也不允许在常量表达式中使用;你是这个意思吗? 是的,这就是我的意思——如果不允许,我完全没有想法...... 您可以使用[a, b][(a &lt; b) as usize] 在编译时计算两个值的最大值。我将把它留给读者从 sn-p 到更多元素的通用解决方案。 【参考方案1】:

虽然常量评估不支持if 或其他控制流,但有一种方法可以根据二进制条件选择值:

[a, b][(a < b) as usize]

这是做什么的

创建一个包含您要在其中选择的两个元素的数组 创建任意布尔表达式 将所述表达式转换为usize 使用该值索引到上面创建的数组中

如果条件为false,则选择第一个元素,如果条件为true,则选择第二个元素。

虽然该方案理论上可以通过对多个转换的bools 进行数学运算来计算索引,从而扩展到任意长度的数组,但采用函数式方法并嵌套上述表达式似乎更简单:

const fn max(a: usize, b: usize) -> usize 
    [a, b][(a < b) as usize]


const MAX: usize = max(max(max(5, 6), 42), 3);

从 Rust 1.31 开始,const fn 可用于稳定的编译器。

【讨论】:

以上是关于在编译时计算一组常量表达式的最大值的主要内容,如果未能解决你的问题,请参考以下文章

编译器错误:函数调用必须在常量表达式中具有常量值

常量表达式

常量表达式 & constexpr

Java技术专题「编译器专题」重塑认识Java编译器的执行过程(常量优化机制)

编译器的常量优化

编译器的常量优化