-
-
Notifications
You must be signed in to change notification settings - Fork 3.9k
bevy_reflect: Add reflect_fn!
macro
#16754
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Copilot reviewed 1 out of 1 changed files in this pull request and generated no suggestions.
This solution looks sensible. Was thinking about making this an attribute macro, but that's both impossible and wouldn't allow reflecting foreign functions. Would it make sense to also allow an alternate syntax where one doesn't write a function body and the macro generates code to call a function of the provided name and arguments? let func = reflect_fn!(
fn insert_at(index: usize, value: i32, list: &mut Vec<i32>) -> &i32
); |
Ooh good idea! I'll try and add that! |
I tried to also get arbitrary paths to work but it's not as easy in macro_rules land (though, I think proc macros would struggle in a similar way). I ran into two problems:
So I don't think I'll be able to get that working unfortunately. |
And to be clear, this isn't a blocker at all. Just an unfortunate limitation haha |
You could still do at least |
64a585b
to
ef8a505
Compare
Yeah I thought about it but decided against it since if it's going to support paths, I'd want it to support generic paths (e.g. But maybe that's too strict? Should we only allow for non-generic use-style paths? (i.e. |
Oh, they added that utility to the stdlib, neat. i only knew about |
Okay updated the PR to support full paths to functions in scope! This still relies on wrapping it in It also ensures that the macro code is more legible and easier to maintain since we don't have to do any complex parsing. |
f1dac90
to
2283b95
Compare
Another thing we could consider is prepending the function ident (if not referencing a function in scope) with the let func = reflect_fn!(
fn add(a: i32, b: i32) -> &i32 {
a + b
}
);
// From this:
// assert_eq!(func.name().unwrap(), "add");
// To this:
assert_eq!(func.name().unwrap(), "my_crate::math::add"); Alternatively, we could make that feature opt-in with a separate syntax like:
Again, not a blocker for this PR, but something extra we could consider adding—either to this PR or in a followup. |
Objective
The
IntoFunction
trait can convert a fair number of functions intoDynamicFunction
instances. However, it doesn't work for all functions. Due to limitations with how we're able to blanket implement the trait, it doesn't work for certain configurations. For example, it only allows for 15 or fewer arguments. Additionally, any reference return types need to be bound to the first argument's lifetime.This makes it impossible to do stuff like this:
For these cases, users must instead define their
DynamicFunction
manually:This, however, is tedious and prone to errors. For example, there's nothing stopping a user from forgetting to specify the return type. It's also easy to forget to update the type of an argument in the
SignatureInfo
if the actual expected type ever changes, which can cause problems with overloaded functions.Solution
This PR introduces the
reflect_fn!
macro.With this macro, we can automatically and safely generate our
DynamicFunctions
much easier:The macro also allows for special configuration as well, such as:
DynamicFunctionMut
SignatureInfo
Now, there will still be cases where
DynamicFunction
will need to be manually created, but with this macro hopefully those cases are much rarer.Testing
You can test locally by running the doc tests:
Showcase
A new
reflect_fn
macro allows for generatingDynamicFunction
andDynamicFunctionMut
instances in a cleaner and more robust way.We can take this code:
and condense it down into this:
And since
insert_at
is a real function that's already in our current scope, we can use it directly: