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
Unwrangling the mess that is godot_wrap_method
, adding generic method support as a side effect
#681
Conversation
This adds the `init::method::Varargs` type, moving logic for argument checking and error reporting out of the macro, into proper library code. This is the first step in simplifying the `godot_wrap_method` macro.
This lifts the unsafe FFI code out of macros and into proper library code as a generic function. Stateful methods are also properly supported using the `Method` trait. This is the second step in simplifying the `godot_wrap_method` macro.
This adds the `FromVarargs` trait and derive macro, which supports using generic structs as argument lists, with the same `#[opt]` syntax for optional arguments. This makes it a lot easier to actually make use of the `Method` interface outside `#[methods]`. This is the final step in simplifying `godot_wrap_method_inner`, which is now a lot easier to follow, and no longer outputs unsafe code into the user crate!
bors try |
tryBuild succeeded: |
C: NativeClass, | ||
F: Method<C>, | ||
{ | ||
pub(super) fn new(class_builder: &'a ClassBuilder<C>, name: &'a str, method: F) -> Self { |
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.
If class_builder
and name
don't have the same lifetime, will this still work?
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.
Since the lifetime isn't carried on ClassBuilder
itself, but is inferred by the compiler, the lifetime of name
simply places a shorter bound on 'a
, which is then inferred by the compiler to be the same as class_builder
. As a result, this does compile: builder.build_method(&format!("foo{}", 123), method).done()
.
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.
Nice changes! It is much more understandable than before.
attributes: ScriptMethodAttributes { | ||
rpc_mode: self.rpc_mode, | ||
}, | ||
method_data: 1 as *mut libc::c_void, |
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.
Could you comment on why 1
for the void*
.
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.
Stateless<F>
is a ZST for any type F
.
pub struct ArgBuilder<'r, 'a, T> { | ||
args: &'r mut Varargs<'a>, | ||
name: Option<&'a str>, | ||
ty: Option<&'a str>, |
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.
Will name
and ty
have the same issue? If they have different lifetimes?
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.
This one does turn out to be problematic since all error objects need to have the same lifetime parameter, which will have to be 'a
since 'r
is temporary. I think it might be best to store a Cow<'static, str>
here instead, since we don't want to allocate all the parameter names when the vast majority of them are just going to be 'static
.
Refactored error macros to forward to a high-level interface. Methods and argument errors now contain site information for error reporting. Strings in arguments errors replaced with `Cow<'a, str>` to avoid lifetime issues.
Added explanation of the ZST pointer in comments. Made strings in
Compared to original PR (path pointing to library):
Compared to current
|
bors try |
tryBuild succeeded: |
Marking as breaking change since I just realized that this breaks code currently using |
Found an issue with the current code: since |
Should be ready for merging! |
bors r+ |
Build succeeded: |
Due to repeated past additions to
godot_wrap_method_inner
, the implementation has become a total mess of unsafe macro code that is very hard to follow. Meanwhile, the manual interface for method registration has largely been neglected, lagging behind the rest of the crate, exposingsys
types and unsafe FFI signatures. Recently, I've found myself in need to register a few methods manually and having to deal with this entire situation, so I've taken the chance to make some improvements to this part of the codebase, with all the hindsight that we now have. This has resulted in satisfactory results:godot_wrap_method_inner
is extracted to the library proper, and can no longer be touched by obscure import/inference problems that come from delayed type checking.Display
implementation. The library can report multiple argument errors at once. "Missing/excessive argument" errors are also more informative.This is not in itself a breaking change, but it's expected that the older unsafe interface will be removed at the next possibility.
As a curiosity, the syntax for manually registering generic methods currently looks like this, and I find it already fairly usable even without further macro goodness:
Unresolved questions
As a side effect of moving the error handling to the library side, error sites all point to the library now. This should be fixable with something on the macro side that provides borrow-able site information, like howResolved.profiling::Signature
is handled? This would mean moving the FFI call out ofgodot_error
into some interface as well.Future development
If desired, it should be fairly easy to add a higher-level, derivable
Mixin
interface based on this. It should also be possible to add support for generic methods directly in#[export]
.