2 个版本
0.1.1 | 2021年12月7日 |
---|---|
0.1.0 | 2020年9月4日 |
#1336 在 过程宏 中
46KB
647 行
inline-proc
此crate提供在您的代码中直接编写过程宏的能力,而不是必须使用另一个crate。
示例
use inline_proc::inline_proc;
#[inline_proc]
mod example {
metadata::ron!(
edition: "2021",
clippy: true,
dependencies: {
"quote": "1",
},
exports: (
bang_macros: {
"def_func": "define_function",
},
),
);
pub fn define_function(_: proc_macro::TokenStream) -> proc_macro::TokenStream {
quote::quote!(
fn macro_function() {
println!("Hello from a proc macro!");
}
).into()
}
}
def_func!();
macro_function();
// => Hello from a proc macro!
工作原理
inline-proc
会将您的代码放入一个具有路径 {临时目录}/inline-proc-crates/{包名}-{重要包版本}-{模块名}
的crate中。例如,如果您在Linux中对位于 my-nice-crate
中的模块 my_module
调用 inline-proc
,该crate的版本为 0.7.3
,将在 /tmp/inline-proc-crates/my-nice-crate-0.7-my_module
中创建一个临时crate。
然后,它使用Cargo将其编译为 dylib
,并将所有输出的错误转换为过程宏的错误,使其看起来与内联编写代码相同。请注意,过程宏目前在稳定版上无法发出警告,因此如果您想那样做,您必须使用nightly。
它输出 macro_rules!
宏,这些宏扩展为对私有宏 inline_proc::invoke_inline_macro!
的调用。此宏接受由 inline_proc
属性宏生成的dylib的路径、该dylib内部宏的名称、宏的类型(bang/derive/attribute)以及宏的输入。它打开dylib并调用宏,返回其结果。
使用生成的宏
由 #[inline_proc]
生成的宏可以直接使用。
use inline_proc::inline_proc;
#[inline_proc]
mod direct_usage {
metadata::ron!(
edition: "2021",
dependencies: {},
exports: (
bang_macros: { "my_bang_macro": "my_bang_macro" },
derives: { "MyDeriveMacro": "my_derive_macro" },
attributes: { "my_attribute_macro": "my_attribute_macro" },
),
);
use proc_macro::TokenStream;
pub fn my_bang_macro(_input: TokenStream) -> TokenStream {
todo!()
}
pub fn my_derive_macro(_item: TokenStream) -> TokenStream {
todo!()
}
pub fn my_attribute_macro(_attr: TokenStream, _item: TokenStream) -> TokenStream {
todo!()
}
}
my_bang_macro!(input tokens);
MyDeriveMacro!(struct InnerItem;);
my_attribute_macro!((attribute tokens) struct InnerItem;);
这对于“bang”宏来说效果很好,但对于属性或衍生宏来说则不是那么好。因此,这个crate提供了属性和衍生宏#[inline_attr]和
InlineDerive
;它们可以这样使用
#
use inline_proc::{InlineDerive, inline_attr};
#[derive(InlineDerive)]
#[inline_derive(MyDeriveMacro)]
struct InnerItem;
#[inline_attr[my_attribute_macro(attribute tokens)]]
struct InnerItem;
它们扩展成与上面相同的代码。
导出宏
为了导出你的宏,你首先需要将你的宏定义改为:"macro_name": ( function: "macro_function", export: true )
。这将做三件事
- 使用
macro_rules!
生成的内容标注#[macro_export]
和#[doc(hidden)]
。 - 让宏接受
inline_proc::invoke_inline_macro
的路径。 - 在宏的名称后添加
_inner
后缀。
然后你创建一个类似这样的包装器
// At the crate root
#[doc(hidden)]
pub use inline_proc::invoke_inline_macro;
// Where your #[inline_proc] is
/// This macro does XYZ.
#[macro_export]
macro_rules! my_macro {
($($tt:tt)*) => {
$crate::my_macro_inner!($crate::invoke_inline_macro, $($tt)*);
}
}
这种间接级别是必要的,因为过程宏没有像MBEs那样获取当前crate的方式($crate
),所以你必须通过MBE方法提供。
crate属性
内联过程宏支持内部crate属性。
use inline_proc::inline_proc;
#[inline_proc]
mod crate_attributes {
#![feature(box_syntax)]
metadata::ron!(
edition: "2021",
dependencies: {},
exports: (bang_macros: { "my_bang_macro": "my_bang_macro" }),
);
use proc_macro::TokenStream;
pub fn my_bang_macro(_input: TokenStream) -> TokenStream {
*box TokenStream::new()
}
}
my_bang_macro!(input tokens);
注意事项
这种方法和常规过程宏相比有几个注意事项
- 编译速度较慢,因为必须调用第二个Cargo实例。
- 无法使用TOML来定义依赖项。
- 导出宏很麻烦。
- 宏只能定义在一个文件中。
- 错误信息不够有帮助。Nightly做了一些改进,但仍然不像本地过程宏错误那样好。
- 不支持衍生辅助属性。
InlineDerive
宏预留了helper
辅助属性,所以你可以用例如将#[my_helper]
替换为#[helper[my_helper]]
。
许可证:MIT OR Apache-2.0
依赖项
~1.3–2.4MB
~48K SLoC