-
-
Notifications
You must be signed in to change notification settings - Fork 218
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
Virtual Function Dispatch #134
Comments
This would appear in an impl block as follows: #[godot_api]
impl MyClass {
#[func(override)]
fn virtual_fn(some_int: i64) {...}
} Thinking about that, the macro
However, it does not see the base class. That one is passed in to the struct definition's Or we directly use the second approach.
This seems quite straightforward and intuitive in the way that traits work in Rust. The only drawback I see is that one might end up with lots of impl blocks. If you look at the
There are three ways to deal with this:
We also need to consider what to do with the "quasi-virtual methods" currently in |
For a trait-based implementation, i'd at least advocate for having virtual traits accumulate. Though if a class isn't adding any new virtual methods we can just have it reuse the old parent's trait. Like we'd have (names pending): // node.rs
pub struct Node { ... }
pub trait NodeVirtualMethods { ... }
// node_3d.rs
pub struct Node3D { ... }
pub use NodeVirtualMethods as Node3DVirtualMethods; which would make it easy for the user to either name the trait themselves, or for a macro to auto-generate the name, while avoiding repeating every virtual method for every single class that has a virtual method. I'd also like advocate for keeping all virtual-ish methods in the generated traits too. |
Yes, if we go the trait way I was thinking of having it cumulative. So, from the user's perspective, they always have to write exactly two |
The current
But they can probably be integrated into the "cumulative trait" as well. Conceptually, they are close to the virtuals in the sense that they are invoked at fixed time points in the object lifecycle. |
I think they can be integrated into the cumulative trait well enough. |
136: Virtual function dispatch r=Bromeon a=Mercerenies See #134. This MR replaces `GodotExt` with a new trait for each built-in Godot class. The trait is the name of the Godot class followed by the word "Virtual". So, for instance, a Rust class that extends `Node2D` would implement the trait called `godot::engine::Node2DVirtual`. This trait contains all of the virtual methods belonging to `Node2D` and its ancestors, as well as a few special-cased methods that used to be on `GodotExt` (`init`, `to_string`, and `register_class`). All methods are optional. Any unimplemented methods call `unimplemented!()` on the Rust side and never get registered as virtual methods on the Godot side. Internally, `ImplementsGodotExt` has been split out into `ImplementsGodotVirtual` (which *only* contains real Godot virtual methods), `GodotToString`, and `GodotRegisterClass`. All of these are automatically implemented when you implement the `ClassNameVirtual` trait, and *only* the ones that you actually define are implemented (so, for example, `GodotToString` is only implemented if you actually define a `to_string` function in your trait `impl`). The user never sees any of this. From the user's perspective, the expectation *used* to be "define an inherent impl and implement `GodotExt`". Now the expectation is "define an inherent impl and implement `ClassNameVirtual` for your class". The tests and dodge-the-creeps example have been updated to reflect these changes. Co-authored-by: Silvio Mayolo <mercerenies@comcast.net>
Currently, aside from a few special cases for common virtual functions like
_ready
and_process
(which exist on a significant percentage of Godot objects), godot-rust does not currently support overriding virtual functions from built-in Godot classes. For instance,CanvasItem._draw
cannot be overridden in Rust currently. There are 1159 virtual functions in Godot, split across 48 classes.Virtual functions go through a different dispatch mechanism than other functions in Godot. Regular functions are registered with
classdb_register_extension_class_method
, while virtual functions are queried at runtime viaGDExtensionClassCreationInfo.get_virtual_func
. Currently, godot-rust special-cases a few virtual functions inGodotExt
but does not provide a mechanism for general overriding.This has been discussed a bit before, such as in the comments of #76 and #102. From what I can tell, there are two main ideas that have been floated for this.
impl
, and to mark them with a special annotation such as#[func(override)]
rather than#[func]
. This would be fairly simple to implement. The downside is that we lose out of type safety; there's nothing on the Rust side that stops us from overriding a function_draw()
with one that takes arguments, and we'll only discover our mistake at runtime.impl GodotExt for CustomStruct
, we would have themimpl
the one particular trait that matches the type they're subclassing, such asimpl GodotNode2DExt for CustomStruct
. Then we have some magic (probably in#[godot_api]
) that generatesGodotExt
and dynamically dispatches to the correct methods. This is more complex to implement (and requires some significant code generation to get all of the traits), but it has the advantage of being completely type-safe. Our generated traits would probably default-implement all trait methods with an appropriate default, so that users only have to override the methods they actually want from the class (It's possible they'll all justpanic!
, which is not ideal but is at least consistent).The text was updated successfully, but these errors were encountered: