godotengine / godot-proposals

Godot Improvement Proposals (GIPs)
MIT License
1.11k stars 69 forks source link

GDNative annotation metadata for extra type information in bindings #2550

Open Bromeon opened 3 years ago

Bromeon commented 3 years ago

Describe the project you are working on

The godot-rust binding.

Describe the problem or limitation you are having in your project

The GDNative API is very rich, but the GDScript language is (for good reasons) very simple, with a limited type system. This often makes GDScript the smallest common denominator and prevents bindings in a more expressive language (C++, Rust, maybe C#) from adding extra type safety.

I found three separate proposals that fall into this category (let me know if here are more, so we can link them in a central place):

Plus, I could imagine that there could be benefits from information about the semantic level, e.g.:

Describe the feature / enhancement and how it helps to overcome the problem or limitation

A generic mechanism to enhance GDNative methods and properties with metadata. This metadata would not be required for a functional GDNative binding, but rather serve as an addition to make usage more idiomatic or safe. Bindings could freely ignore it.

Ideally, such a mechanism is general enough that it doesn't have to be re-invented for each use case (see list of existing proposals above).

Somewhat related to the proposal #828 | Annotations in GDScript, this proposal would extend such information beyond GDScript itself and make it usable in GDNative. In particular, it can cover information that may not be useful for GDScript itself, but for binding languages.

Describe how your proposal will work, with code, pseudo-code, mock-ups, and/or diagrams

As an example, let's look at the Camera API (here modeled in GDScript and merged with base class for simplicity):

var fov: float # degrees
func rotate(axis: Vector3, angle: float) # radians

func get_node(path: NodePath) -> Node # non-null
func get_node_or_null(path: NodePath) -> Node # nullable

Annotations could be added near the C++ engine code that registers the properties and methods. This would result in an api.json with the following data (irrelevant fields omitted):

{
    "name": "Camera",
    "properties": [
        {
            "name": "fov",
            "type": "float",
            "annotations": ["angle_degrees"],
            "getter": "get_fov",
            "setter": "set_fov",
            "index": -1
        }
    ],
    "methods": [
        {
            "name": "rotate",
            "return_type": "void",
            "arguments": [
                {
                    "name": "axis",
                    "type": "Vector3",
                    "has_default_value": false,
                    "default_value": ""
                },
                {
                    "name": "angle",
                    "type": "float",
                    "annotations": ["angle_radians"],
                    "has_default_value": false,
                    "default_value": ""
                }
            ]
        },
        {
            "name": "get_node_or_null",
            "return_type": "Node",
            "annotations": ["nullable_result"],
            "arguments": [
                {
                    "name": "path",
                    "type": "NodePath",
                    "has_default_value": false,
                    "default_value": ""
                }
            ]
        }
    ]
}

A binding (here Rust) could then be generated from api.json as follows:

impl Camera {
    fn fov() -> Angle;                        // dedicated type, auto-converted from degrees
    fn rotate(axis: Vector3, angle: Angle);   // same type, auto-converted to radians

    fn get_node(path: NodePath) -> Node;                   // always returns a node (or error/panic)
    fn get_node_or_null(path: NodePath) -> Option<Node>;   // optional result
}

If this enhancement will not be used often, can it be worked around with a few lines of script? Is there a reason why this should be core and not an add-on in the asset library?

If this is not part of the official GDNative API, bindings would need to manually annotate all methods, and it would be hard to find a solution that benefits different bindings (C++, Rust, C#, ...) at the same time.

Maintaining annotations externally to Godot comes with the additional problem of bit rot -- changes in the API will likely not be reflected in the annotations, and it's difficult to keep versions synchronized.

StatisMike commented 8 months ago

Not knowing if the type is nullable makes the gdextension bindings in strict-typed languages very lacking, in some cases it even halts the whole development process. Any update on this would be appreciated.