Skip to content

Use inventory or linkme to register scripts automatically #350

@ghost

Description

While using the bindings, one might create a new script type but forget to register it in init. It's a minor annoyance, but one that often leads to confusing errors when the new type is used, and is not handled by the compiler. Implementing automatic script registration improves usability and reduces the chances of runtime bugs.

Provisions for automatic script registration seem to exist in the bindings for D, Kotlin, and Nim.

Currently, there are two crates in the Rust ecosystem that enable relatively easy automatic registration: inventory and linkme. The former is implemented through module initialization/teardown functions. The latter is based on link_section attributes. Both allow us to collect a list of fn(&mut InitHandle) -> () pointers before nativescript_init and use them to automatically register all script types that derived NativeClass. The expected user-facing API is the same regardless of the implementation detail:

// The derive macro will automatically insert the relevant code.
#[derive(NativeClass)]
struct Foo;

// A separate attribute can be used for types with manual `NativeClass` impls.
#[gdnative::register]
struct Bar;

impl NativeClass for Bar {
    /* - snip - */
}

// No callbacks necessary. It just works!
godot_gdnative_init!();
godot_nativescript_init!();
godot_gdnative_terminate!();

Manual registration should still be possible for types with manual NativeClass impls:

struct ILikeBoilerplateCode;

impl NativeClass for ILikeBoilerplateCode {
    /* - snip - */
}

fn init(handle: gdnative::init::InitHandle) {
    handle.add_class::<ILikeBoilerplateCode>();
}

godot_gdnative_init!();
godot_nativescript_init!(init);
godot_gdnative_terminate!();

Implementation options

As mentioned before, there are two crates that can be used. They have slightly different pros/cons from each other, that are given here. In any case, this will be an implementation detail, and we should be able to swap implementations freely at a later time.

Both crates need some support with platform compatibility before they can be used in godot-rust.

inventory

Pros:

  • Supports Android and iOS out of the box.

Cons:

linkme

Pros:

  • Works at link time. No life-before-main or runtime cost.

Cons:

  • No official support for both Android and iOS (no issues in repo). Implementation difficulty unknown.

Considerations

Compatibility

It's possible to maintain compatibility with current manually registering code by making InitHandle track registered types, and ignore types that are already registered.

Tool scripts

A #[gdnative::tool] attribute may be added for tool scripts, alongside gdnative::register.

Metadata

Metadata

Assignees

No one assigned

    Labels

    c: exportComponent: export (mod export, derive)featureAdds functionality to the libraryhelp wanted

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions