如何编写一个 returns 基于字符串的结构实现方法的宏?
How do I write a macro that returns the implemented method of a struct based on a string?
受到的启发,我想获得一个基于str
的结构的实现方法,类似于:
macro_rules! comp {
(struct $name:ident {
$($field_name:ident : $field_type:ty,)*
}
impl $name2:ident {
$(pub fn $func_name:ident($($args:tt)*) $bk:block)*
}
) => {
//basic component
struct $name {
$($field_name: $field_type),*
}
impl $name {
$(pub fn $func_name($($args)*) $bk)*
// the generated function
pub fn get_method(index: &str) -> &'static dyn Fn() {
$(if stringify!($func_name) == index {
return $func_name; // (***)
})*
//
// Some code here to return the right function
//
}
}
};
}
fn main() {
comp! {
struct S {
field: String,
}
impl S {
pub fn method1() {
println!("method1 called");
}
pub fn method2() {
println!("method2 called");
}
}
}
// the functionality should achieved
// S::get_method("method1") == S::method1
// S::get_method("method2") == S::method2
}
本来想用return $func_name
获取函数指针,但是好像不行;标有 (***)
的代码行出现错误:
error[E0423]: expected function, found macro `stringify`
--> src/main.rs:19:22
|
19 | $(if stringify($func_name) == index {
| ^^^^^^^^^ not a function
...
31 | / comp! {
32 | | struct S {
33 | | field: String,
34 | | }
... |
42 | | }
43 | | }
| |_____- in this macro invocation
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
help: use `!` to invoke the macro
|
19 | $(if stringify!($func_name) == index {
| ^
error[E0425]: cannot find value `method1` in this scope
--> src/main.rs:36:20
|
36 | pub fn method1() {
| ^^^^^^^ not found in this scope
error[E0425]: cannot find value `method2` in this scope
--> src/main.rs:39:20
|
39 | pub fn method2() {
| ^^^^^^^ not found in this scope
error[E0308]: mismatched types
--> src/main.rs:19:19
|
19 | $(if stringify($func_name) == index {
| ___________________^
20 | | return $func_name; // (***)
21 | | })*
| |_________________^ expected reference, found `()`
...
31 | / comp! {
32 | | struct S {
33 | | field: String,
34 | | }
... |
42 | | }
43 | | }
| |_____- in this macro invocation
|
= note: expected reference `&'static (dyn std::ops::Fn() + 'static)`
found unit type `()`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
如何完成?
首先感谢@Shepmaster,如果你的问题不复杂,你的建议对大多数情况都是适用的。然而,它需要处理 struct
,用户提供的一段代码,在这种情况下,我想出的唯一方法是使用宏规则来实现
经过一段时间,我终于搞定了。
在我们的例子中,简而言之,我们可以做
fn get_method(ind: &str) -> Option<&dyn Fn()> {
// the default type of $name::$func is fn(), function pointer
// it is recommended to convert into &dyn Fn() if you impl many methods
let methods = vec![ $(&$name::$func as &dyn Fn()),* ];
let inds = vec![$(stringify!($func)),*];
let mut i = 0;
for item in inds.iter() {
if item == &ind {
break;
} else {
i +=1;
}
}
if i <= inds.len() - 1 {
Some(methods[i])
} else {
None
}
}
受到str
的结构的实现方法,类似于:
macro_rules! comp {
(struct $name:ident {
$($field_name:ident : $field_type:ty,)*
}
impl $name2:ident {
$(pub fn $func_name:ident($($args:tt)*) $bk:block)*
}
) => {
//basic component
struct $name {
$($field_name: $field_type),*
}
impl $name {
$(pub fn $func_name($($args)*) $bk)*
// the generated function
pub fn get_method(index: &str) -> &'static dyn Fn() {
$(if stringify!($func_name) == index {
return $func_name; // (***)
})*
//
// Some code here to return the right function
//
}
}
};
}
fn main() {
comp! {
struct S {
field: String,
}
impl S {
pub fn method1() {
println!("method1 called");
}
pub fn method2() {
println!("method2 called");
}
}
}
// the functionality should achieved
// S::get_method("method1") == S::method1
// S::get_method("method2") == S::method2
}
本来想用return $func_name
获取函数指针,但是好像不行;标有 (***)
的代码行出现错误:
error[E0423]: expected function, found macro `stringify`
--> src/main.rs:19:22
|
19 | $(if stringify($func_name) == index {
| ^^^^^^^^^ not a function
...
31 | / comp! {
32 | | struct S {
33 | | field: String,
34 | | }
... |
42 | | }
43 | | }
| |_____- in this macro invocation
|
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
help: use `!` to invoke the macro
|
19 | $(if stringify!($func_name) == index {
| ^
error[E0425]: cannot find value `method1` in this scope
--> src/main.rs:36:20
|
36 | pub fn method1() {
| ^^^^^^^ not found in this scope
error[E0425]: cannot find value `method2` in this scope
--> src/main.rs:39:20
|
39 | pub fn method2() {
| ^^^^^^^ not found in this scope
error[E0308]: mismatched types
--> src/main.rs:19:19
|
19 | $(if stringify($func_name) == index {
| ___________________^
20 | | return $func_name; // (***)
21 | | })*
| |_________________^ expected reference, found `()`
...
31 | / comp! {
32 | | struct S {
33 | | field: String,
34 | | }
... |
42 | | }
43 | | }
| |_____- in this macro invocation
|
= note: expected reference `&'static (dyn std::ops::Fn() + 'static)`
found unit type `()`
= note: this error originates in a macro (in Nightly builds, run with -Z macro-backtrace for more info)
如何完成?
首先感谢@Shepmaster,如果你的问题不复杂,你的建议对大多数情况都是适用的。然而,它需要处理 struct
,用户提供的一段代码,在这种情况下,我想出的唯一方法是使用宏规则来实现
经过一段时间,我终于搞定了。 在我们的例子中,简而言之,我们可以做
fn get_method(ind: &str) -> Option<&dyn Fn()> {
// the default type of $name::$func is fn(), function pointer
// it is recommended to convert into &dyn Fn() if you impl many methods
let methods = vec![ $(&$name::$func as &dyn Fn()),* ];
let inds = vec![$(stringify!($func)),*];
let mut i = 0;
for item in inds.iter() {
if item == &ind {
break;
} else {
i +=1;
}
}
if i <= inds.len() - 1 {
Some(methods[i])
} else {
None
}
}