-
Notifications
You must be signed in to change notification settings - Fork 212
create a macro to create interfaces for externally defined types #200
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
Comments
This seems very nice to have, but with two types to define and a bunch of attributes to sprinkle around, I find the proposed API a little bit ugly. I would prefer a (procedural) function-like macro that defines a struct directly in this case. Another problem that I think should be considered before implementation is the interaction with non-refcounted types. Calling methods on these objects are Also, since all calls are performed through All considered, I would propose an alternate syntax as follows: external_interface! {
// TODO: Maybe allow users to add custom trait bounds on T?
// TODO: Maybe the syntax is too close to normal structs and might be confusing?
// The macro invocation and interface "keyword" should signify the difference though.
pub interface struct Adder<T: GodotObject> {
// Only implemented for reference types
pub fn add(&mut self, amount: i64) -> ExtResult<()>;
// Unsafe aliases must be explicitly named, as automatic addition of `unsafe_` versions
// may result in name collisions
pub unsafe fn unsafe_add = add(&mut self, amount: i64) -> ExtResult<()>;
// Implemented for all GodotObject, but only unsafe version
pub unsafe fn tell(&self) -> ExtResult<i64>;
}
} ...which would expand to something like: pub struct Adder<T> {
variant: Variant,
_marker: PhantomData<T>,
}
// TODO: Maybe a separate trait NewRef for semantic clarity?
impl<T: GodotObject + Clone> Adder<T> {
pub fn add(&mut self, amount: i64) -> ExtResult<()> {
// - snip -
}
}
impl<T: GodotObject> Adder<T> {
pub unsafe fn unsafe_add(&mut self, amount: i64) -> ExtResult<()> {
// - snip -
}
pub unsafe fn tell(&self) -> ExtResult<i64> {
// - snip -
}
}
impl<T: GodotObject> From<T> for Adder<T> {
fn from(base: T) -> Adder {
Adder(base.to_variant(), PhantomData)
}
} |
Yes, I understand and also think that it's not the nicest API, however what I like about it is that there is no "custom syntax" involved (if you don't count attributes as custom syntax) and also an IDE should be able to pick up all of this without effort. One of the biggest problems I personally had with the bindings in the past was that it used a DSL inside a macro to define classes (which is still possible). IDEs had a hard time with this and also users had to remember the custom syntax. I personally think the procedural macro solution using attributes is a lot clearer, this is something that I also wanted to aim for with this. We could probably easily get rid of the That said, I am not married to this design, I would still prefer it to be based around attributes rather than direct macro invokations, just for clarity's sake.
That is interesting. We could also just force all of those function to always be marked as
Indeed, I was including it as it would disallow using a There is some prior art of this in the form of WebAssembly interface types, which are also implemented in |
I read its implementation a bit. It looks like they're replacing the trait declaration with a struct that has the same name. An extra That's said, this is certainly magic, and I'm not sure if that will count as a form of "custom syntax", as it'll still require specific knowledge of the bindings for users to use properly, otherwise they'll get seemingly unreasonable error messages when they try to type Personally, I think this is a worse kind of custom syntax compared to function-like macros, as attributes are implicit in their custom-ness. When you use a function-like macro, the invocation reminds you that you're not using the base language. Attributes hide this away, and that is certainly useful in many cases, but "type
That's fair. |
Objects that have scripts written in other languages attached can be passed to methods in Rust, the functionality provided by those scripts can't be accessed easily.
This is a proposal to create a procedural macro to use a
trait
as the description for an interface of an object.An implementation of such a trait would use
godot_variant_call
to perform the calls.All functions in the
trait
have to return a result type to represent the varcall failing.When an object is used as an implementation of this interface no immediate checks are performed. Doing type-checking is not possible in the general case, so checks should be performed lazily.
This could work like this:
The text was updated successfully, but these errors were encountered: