#proc-macro #compile-time #constants #creation #syntax #string #token-stream

const_gen_proc_macro

创建一个过程宏,用于在编译时添加创建常量的功能

1 个不稳定版本

0.1.1 2023年7月10日
0.1.0 2023年7月10日

#896 in 过程宏

MIT/Apache

69KB
1.5K SLoC

目标

该库的目标是帮助创建过程宏,其中过程宏的目的是创建常量。此外,它旨在实现以下目标:

  • 过程宏的内容将使用 Rust 语法的一个子集(以提高可读性);
  • 过程宏将允许编译时功能,这些功能可能在目标平台上不可用(例如:标准库或浮点运算);
  • 过程宏的用户将获得有用的编译时错误信息。

示例

过程宏的一个用例是提供创建常量的额外编译时功能。

一个简单的(尽管是多余的)示例是将两个字面字符串连接起来(类似于 concat! 宏规则)。

该库提供了一个 API,使得创建这样的过程宏变得非常简单。例如,以下代码就是一个完整的过程宏,提供了上述功能:

#[proc_macro]
pub fn string_concat(tokens: TokenStream) -> TokenStream {
    let mut string_path = Path::new();
    string_path.add_function(
        "concat",
        &(&|first: String, second: String| -> String {
            first + &second
        } as &dyn Fn(String, String) -> String),
   );

   let mut env = ProcMacroEnv::new();
   env.add_path("string", string_path);
   env.process(tokens)
}

此代码创建了一个名为“string”的单一路径的过程宏环境。在此路径下有一个名为“concat”的单个函数,它接受两个字符串并返回一个字符串(这是连接后的结果)。

以下是一个使用此过程宏的代码示例

string_concat! {
    let first = "First";
    let second = "Second";
    const STRING: &str = string::concat(first, second);
}

assert_eq!(STRING, "FirstSecond");

注意,在宏结束时,两个变量‘first’和‘second’将从作用域中移除;只有常量被保留。

也可以创建和使用对象。例如,可以使用以下代码创建一个具有“concat”方法的“String”对象:

#[proc_macro]
pub fn string_concat(tokens: TokenStream) -> TokenStream {
    let mut string_type = ObjectType::new();
    string_type.add_method(
        "concat",
        &(&|first: &String, second: String| -> String { first.to_owned() + &second }
            as &dyn Fn(&String, String) -> String),
    );
    // sealing the ObjectType means it is no longer mutable and can now instantiate objects
    let string_type = string_type.seal();

    let mut string_path = Path::new();
    let string_new =
        &|first: String| -> Object { string_type.new_instance(first) } as &dyn Fn(String) -> Object;
    string_path.add_function("new", &string_new);

    let mut env = ProcMacroEnv::new();
    env.add_path("string", string_path);
    env.process(tokens)
}

并按以下方式使用它

string_concat! {
    let prefix = string::new("A ");
    let variable = "Variable";
    const VARIABLE: &str = prefix.concat(variable);
    const LITERAL: &str = prefix.concat("Literal");
}

assert_eq!(VARIABLE, "A Variable");
assert_eq!(LITERAL, "A Literal");

虽然这两个示例是人为的,但它们有助于展示库的功能。一个更实际的示例是在编译时添加目标平台上不可用的功能。

例如,假设目标平台是Arduino Uno的微处理器(atmega328p)。为此编写的代码通常是在没有标准库和没有浮点运算的情况下编译的。如果您想在编译时使用同时使用浮点和标准库的库,您可以使用此库轻松地将该功能粘接到proc宏中;然后使用该proc宏创建一个常量。

一个更具体的例子是表示正弦波固定间隔点的常量数组。proc宏中的计算可以利用标准库和浮点数,但生成的常量数组可以是u8s。

依赖关系

~320–780KB
~19K SLoC