【问题标题】:How do I change a function's qualifiers via conditional compilation?如何通过条件编译更改函数的限定符?
【发布时间】:2018-09-03 21:54:23
【问题描述】:

我有一个可以作为const 实现的函数:

#![feature(const_fn)]

// My crate would have:

const fn very_complicated_logic(a: u8, b: u8) -> u8 {
    a * b
}

// The caller would have:

const ANSWER: u8 = very_complicated_logic(1, 2);

fn main() {}

我想继续支持无法定义此类函数的稳定 Rust。这些稳定的消费者将无法在conststatic 中使用该函数,但应该能够在其他上下文中使用该函数:

// My crate would have:

fn very_complicated_logic(a: u8, b: u8) -> u8 {
    a * b
}

// The caller would have:    

fn main() {
    let answer: u8 = very_complicated_logic(1, 2);
}

如何有条件地编译我的代码,以便我的 crate 的冒险用户可以启用 const fn 支持,稳定的用户仍然可以使用我的代码,并且我不必编写每个函数两次?

同样的问题应该适用于函数的其他修饰符,但我不确定这些修饰符会根据某些条件发生变化的具体情况:

  • default
  • unsafe
  • extern
  • pub(和其他可见性修饰符)

【问题讨论】:

    标签: rust conditional-compilation


    【解决方案1】:

    宏来救援!

    #![cfg_attr(feature = "const-fn", feature(const_fn))]
    
    #[cfg(not(feature = "const-fn"))]
    macro_rules! maybe_const_fn {
        ($($tokens:tt)*) => {
            $($tokens)*
        };
    }
    
    #[cfg(feature = "const-fn")]
    macro_rules! maybe_const_fn {
        ($(#[$($meta:meta)*])* $vis:vis $ident:ident $($tokens:tt)*) => {
            $(#[$($meta)*])* $vis const $ident $($tokens)*
        };
    }
    
    maybe_const_fn! {
        #[allow(unused)] // for demonstration purposes
        pub fn very_complicated_logic(a: u8, b: u8) -> u8 {
            internally_complicated_logic(a, b)
        }
    }
    
    maybe_const_fn! {
        fn internally_complicated_logic(a: u8, b: u8) -> u8 {
            a * b
        }
    }
    
    #[cfg(test)]
    mod tests {
        use super::*;
    
        #[cfg(feature = "const-fn")]
        #[test]
        fn use_in_const() {
            const ANSWER: u8 = very_complicated_logic(1, 2);
            drop(ANSWER);
        }
    
        #[test]
        fn use_in_variable() {
            let answer: u8 = very_complicated_logic(1, 2);
            drop(answer);
        }
    }
    

    Cargo.toml:

    [features]
    const-fn = []
    

    由于宏只能扩展为完整的语法片段(即宏不能简单地扩展为const),我们必须将整个函数包装在宏中并保留其中的某些部分未解析,以便我们可以注入@987654326 @在适当的地方。然后,解析器可以将整个事物解析为一个函数定义。

    属性和可见性限定符需要特殊处理,因为它们必须出现在const 之前。我正在使用 vis 匹配器 (available since Rust 1.30.0) 来简化宏的实现。

    【讨论】:

      猜你喜欢
      • 2019-04-20
      • 1970-01-01
      • 2021-10-28
      • 2011-02-20
      • 2021-03-27
      • 1970-01-01
      • 2013-11-08
      • 1970-01-01
      相关资源
      最近更新 更多