Default parameters in Engine API + meta param/return types by Bromeon · Pull Request #322 · godot-rust/gdext (original) (raw)
Default parameters in Engine API
Example RenderingServer::environment_set_ambient_light with signature:
void environment_set_ambient_light( RID env, Color color, EnvironmentAmbientSource ambient=0, float energy=1.0, float sky_contibution=0.0, EnvironmentReflectionSource reflection_source=0 )
Previous gdext syntax:
server.environment_set_ambient_light(rid, color, 0, 1.0, 0.0, 0)
Now:
server.environment_set_ambient_light(rid, color)
With default arguments:
// select what you like, skip out-of-order: server.environment_set_ambient_light_ex(rid, color).energy(2.0).done()
// provide all: server.environment_set_ambient_light_ex(rid, color) .ambient(0) .energy(1.0) .sky_contribution(0.0) .reflection_source(0) .done()
See also this commit for the real-world impact this change has, within gdext alone.
Mechanics
Methods accepting default parameters come in two flavors: method
and method_ex
.
The _ex
overload returns an "extender", a builder object providing a fluent API to accept values overriding the defaults. The arguments can be provided in any order, and arbitrary ones can be skipped. This is different from C++ and GDScript, where you have to provide all default arguments in their positional order.
This effectively implements an API similar to the one discussed in godot-rust/gdnative#814 (comment), however in a non-generic way and without enforcing ordering. Theoretically it's possible to specify the same argument twice, but that's a risk I'm gladly taking if I can avoid type-state with its compile-time and complexity implications.
Other changes
This pull request comes with a few collateral features:
- Support for meta fields in the JSON file.
Parameter or return types can have an extra "meta" type which constrains the domain for non-GDScript languages. For example, a parameterint
can come with meta fielduint16
. These are translated to Rust, so that the correct type is passed (hereu16
). - GDScript expression parser.
Implementing default parameters means we have to understand syntax likeVector2(1, 2)
which is provided as thedefault_value
field in GDScript. This one is then translated to an equivalent Rust representation when populating the extender struct.
This turned out to be much more complex than anticipated, due to things like0
meaning "enum" rather than "int", or beautiful expressions likeArray[RDPipelineSpecializationConstant]([])
. - Refactor
class_generator.rs
.
Quite a lot of functionality in the code generator was touched. Already in Refactor small parts of class_generator.rs #315, I added a domain representation for function arguments and parameters, distinct from the JSON models. The function to generate method definitions now accepts consolidated structs instead of a dozen parameters. I cleaned up a few other things on-the-fly. Possibly I'll do a few follow-up cleanups, but they can be done independently of this functionality. - Implement
EngineEnum
forVector*Axis
enums.
This allows ordinal conversion from/to those types, just like other engine enums.