Skip to content
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

Builder API to register classes, functions, properties, signals #4

Open
Bromeon opened this issue Oct 3, 2022 · 3 comments
Open

Builder API to register classes, functions, properties, signals #4

Bromeon opened this issue Oct 3, 2022 · 3 comments
Labels
c: register Register classes, functions and other symbols to GDScript feature Adds functionality to the library hard Opposite of "good first issue": needs deeper know-how and significant design work.

Comments

@Bromeon
Copy link
Member

Bromeon commented Oct 3, 2022

Functionality that is currently available through proc-macros should ideally be exposed in a programmatic builder API, too.

Some challenges:

  • The proc-macros combined with the plugin system detect very fine-grained customization, e.g. every single method in GodotExt.
    • This would have to be manually annotated in a builder, possibly using static type information (generic arguments).
  • Some of the traits and functions generated by proc-macros are internal.
    • The builder API could be more high-level and stable.
  • Functions are rather verbose to register through builders, with generic-tuples. Think about ways to provide a simple interface.
    • In GDNative, this was done via Method trait, which is very flexible and general, but required full implementation for every single type. This might serve as a basis, with more higher-level concretizations.
@Bromeon Bromeon added feature Adds functionality to the library c: register Register classes, functions and other symbols to GDScript labels Oct 3, 2022
@Bromeon Bromeon mentioned this issue Oct 3, 2022
Closed
bors bot added a commit that referenced this issue Feb 12, 2023
68: PR #4/5 Astolfo feature/builtin-quaternion r=Bromeon a=RealAstolfo

Co-Authored-By: Thomas ten Cate <ttencate@gmail.com>

Implemented Quaternion to the best of my current ability to resemble godot's, meant to be merged after #67 

Co-authored-by: RealAstolfo <astolfo.gman@gmail.com>
Co-authored-by: Jan Haller <bromeon@gmail.com>
@Bromeon Bromeon added the hard Opposite of "good first issue": needs deeper know-how and significant design work. label Mar 21, 2023
Hapenia-Lans pushed a commit to Hapenia-Lans/gdextension that referenced this issue May 26, 2023
# This is the 1st commit message:

Parse gdextension_interface.h declarations using regex

# This is the commit message #2:

AsUninit trait to convert FFI pointers to their uninitialized versions

# This is the commit message godot-rust#3:

GodotFfi::from_sys_init() now uses uninitialized pointer types

# This is the commit message godot-rust#4:

Introduce GDExtensionUninitialized*Ptr, without changing semantics

# This is the commit message godot-rust#5:

Adjust init code to new get_proc_address mechanism

# This is the commit message godot-rust#6:

Make `trace` feature available in godot-ffi, fix interface access before initialization

# This is the commit message godot-rust#7:

Compatibility layer between Godot 4.0 and 4.1 (different GDExtension APIs)

# This is the commit message godot-rust#8:

Add GdextBuild to access build/runtime metadata

# This is the commit message godot-rust#9:

Detect 4.0 <-> 4.1 mismatches in both directions + missing `compatibility_minimum = 4.1`

# This is the commit message godot-rust#10:

Detect legacy/modern version of C header (also without `custom-godot` feature)

# This is the commit message godot-rust#11:

CI: add jobs that use patched 4.0.x versions

# This is the commit message godot-rust#12:

Remove several memory leaks by constructing into uninitialized pointers

# This is the commit message godot-rust#13:

CI: memcheck jobs for both 4.0.3 and nightly

# This is the commit message godot-rust#14:

Remove ToVariant, FromVariant, and VariantMetadata impls for pointers

This commit splits SignatureTuple into two separate traits:
PtrcallSignatureTuple and VarcallSignatureTuple. The latter is a child
of the former. PtrcallSignatureTuple is used for ptrcall and only
demands GodotFuncMarshall of its arguments. VarcallSignatureTuple is
used for varcall and additionally demands ToVariant, FromVariant, and
VariantMetadata of its arguments, so pointers cannot benefit from the
optimizations provided by varcall over ptrcall.

# This is the commit message godot-rust#15:

Adds FromVariant and ToVariant proc macros

# This is the commit message godot-rust#16:

godot-core: builtin: reimplement Plane functions/methods

# This is the commit message godot-rust#17:

impl GodotFfi for Option<T> when T is pointer sized and nullable godot-rust#240

Additionally FromVariant and ToVariant are also implemented for Option<Gd<T>>
to satisfy all the requirements for ffi and godot_api.

# This is the commit message godot-rust#18:

Fix UB in virtual method calls that take objects
Fix incorrect incrementing of refcount when calling in to godot
Fix refcount not being incremented when we receive a refcounted object in virtual methods

# This is the commit message godot-rust#19:

fix UB caused by preload weirdness

# This is the commit message godot-rust#20:

Implements swizzle and converts from/to tuples
@Bromeon
Copy link
Member Author

Bromeon commented Nov 13, 2023

While this feature is still far away (definitely not this year), here are already some ideas of how proc-macro APIs could map to the builder API. The philosophy is to keep the mapping somewhat intuitive, reusing names where possible and avoid too much magic.

For example, given:

#[derive(GodotClass)]
#[class(base=Node2D)]
struct MyClass {
    #[base]
    base: Base<Node2D>,

    #[onready]
    late_init: OnReady<PackedInt32Array>,
    
    #[export]
    integer: i32,
}

#[godot_api]
impl MyClass {
    #[func]
    fn regular() -> Gd<Other> {...}

    #[func(gd_self)]
    fn do_sth(this: Gd<Self>, arg: i32) {...}
}

#[godot_api]
impl INode2D for MyClass {
    fn init(base: Base<Node2D>) -> Self {...}
    fn ready(&mut self); {...}
    fn to_string() -> GString {...}
}

With builders and zero proc-macros, it could look something like:

struct MyClass {
    base: Base<Node2D>,
    late_init: OnReady<PackedInt32Array>,
    integer: i32,
}

impl MyClass {
    fn regular() -> Gd<Other> {...}
    fn do_sth(this: Gd<Self>, arg: i32) {...}
}

// NEW: explicit GodotClass 
impl GodotClass for MyClass {
    // base, memory etc
}

impl INode2D for MyClass {
    // NEW:
    fn register(b: &mut ClassBuilder<MyClass>) {
        // Fields
        b.export_var("integer").get(Self::get_integer).set(Self::set_integer);
        b.base(Self::get_base); // returns &mut Base
        b.onready(Self::get_late_init); // returns &mut OnReady
        
        // Different kinds of methods, e.g. static/instance/Gd<Self>
        b.func("regular").method(Self::regular);
        b.func("do_sth").method_gd_self(Self::do_sth);
        
        // Special methods (we need to tell which ones are overridden, compiler can't detect it)
        b.interface() // requires INode2D bound
         .overrides(IMethod::Init | IMethod::Ready | IMethod::ToString);
    }

    fn init(base: Base<Node2D>) -> Self {...}
    fn ready(&mut self); {...}
    fn to_string() -> GString {...}
}

// in a global registration hook:
fn register_classes(b: &mut LibraryBuilder) {
    b.add_class::<MyClass>();
    ...
}

@StatisMike
Copy link
Contributor

This would be great! Additional problems like the inability to register methods coming from different trait impl wouldn't be a problem then - they could be just manually added in register().

@Bromeon
Copy link
Member Author

Bromeon commented Mar 28, 2024

Just for completeness, I'd also like to mention fully dynamic builder APIs.

Meaning, class methods are registered based on Callable and there would be no traits to fulfill. In essence, the whole builder API would be type-erased and lower-level, closely resembling the raw C APIs. Use case might be an editor plugin that doesn't expect each class to correspond to a Rust struct, but rather allows dynamic creation of different types and nodes -- via UI, config file or network. In other words, this would allow to add new classes and methods without writing new Rust code.

I don't think we should support this, at least not at the moment. But maybe we should also establish use cases for a typed builder API more clearly, to differentiate it better from features that the proc-macro API already provides.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
c: register Register classes, functions and other symbols to GDScript feature Adds functionality to the library hard Opposite of "good first issue": needs deeper know-how and significant design work.
Projects
None yet
Development

No branches or pull requests

2 participants