-
-
Notifications
You must be signed in to change notification settings - Fork 23.9k
Description
Tested versions
Updated version from PR #113697.
Issue description
Not directly a bug, but several things in the new JSON format (#107845) for the GDExtension specification might need some clarification before we go stable. It's fine if we leave parts unaddressed, but I'd like to make sure those are conscious decisions and not oversights 🙂
1. Handles: normal, const, uninitialized
There is a structural relation between ConstPtr -> Ptr, but not between UninitializedPtr -> Ptr.
I wonder if we could model this directly?
{
"name": "GDExtensionVariantPtr",
"kind": "handle",
"relations": {
"const": "GDExtensionConstVariantPtr",
"uninitialized": "GDExtensionUninitializedVariantPtr"
}
},Or also the inverse way, if the const information needs to be self-contained within GDExtensionConstVariantPtr.
But important is that uninitialized is mapped somehow. This allows code generators to create dedicated conversion functions between those types.
2. Enum size
C and C++ standards do not strictly specify enum size. The compiler must choose an underlying type that is big enough to represent all values. While this is typically uint32_t, it doesn't have to be.
For an ABI, it's however crucial that this is clearly specified. If the representation is the same for all enums (int32_t or uint32_t), it can be defined externally. If it varies between enums, we can add an optional underlying_type field, falling back to the external default.
3. Integer size
Even though most APIs use strictly-sized types, some have int:
{
"name": "GDExtensionPtrUtilityFunction",
"kind": "function",
"return_value": {
"type": "void"
},
"arguments": [
// ...
{
"name": "p_argument_count",
"type": "int"
}
]
},Many others use GDExtensionInt which is int64_t:
{
"name": "GDExtensionClassMethodCall",
"kind": "function",
"return_value": {
"type": "void"
},
"arguments": [
// ...
{
"name": "p_argument_count",
"type": "GDExtensionInt"
},
]
},Then again others use int64_t:
{
"name": "variant_construct",
"return_value": {
"type": "void"
},
"arguments": [
// ...
{
"name": "p_argument_count",
"type": "int32_t",
"description": [
"The number of arguments to pass to the constructor."
]
},
// ...
],
},I'm not so much concerned about the 32/64-bit inconsistency, we anyway can't change that without breaking the ABI.
However, I do wonder whether:
-
Can we rely on
intbeingint32_tfor targets that Godot is built for? Or is there a case differentiation necessary? If the latter, how do we know that without having a C compiler? -
Does
GDExtensionInthave an actual purpose, or could we replace it withint64_tdirectly? UnlikeGDExtensionBoolorGDExtensionObjectID,GDExtensionIntdoesn't represent its own "class" of types, but is used interchangeably with 64-bit integers. Removing this type would decrease the cognitive load and extra indirections in generated code.
Yes, I'm aware these problems were already present in the .h version, but the important difference was that there is typically standard tooling for C header interop with other languages, which can take care of some of the details. Now, JSON parsers have to be hand-written, and the burden of interpreting the spec correctly is on the binding authors. Either way, I think it would be good to clarify these integers.
4. Handle kind underspecified?
Opaque pointers are represented with key-value pair "kind": "handle". The term "handle" is quite abstract and can include integers, too. I get that this is the whole point, but this abstraction is leaky already: we need to know the size of the type, and it's sometimes necessary to convert between pointers of the same group.
Maybe it should be called opaque_ptr, ptr_handle or so? This would imply that it corresponds to the native pointer size on the corresponding platform.
Or we clearly document that somewhere,,, 🤔
5. References to C++ code
There are see references:
{
"name": "variant_iter_next",
// ...
"see": [
"Variant::iter_next()"
]
},These all refer to C++ symbols at the moment. Is there a chance that we need cross-references between symbols in the GDExtension C API (i.e. the same JSON) in the future? If yes, can we differentiate them somehow (apart from :: 😓)?
6. Enum vs. flags
The header defines some flags like GDExtensionClassMethodFlags (NORMAL, CONST, STATIC, ...).
In the extension_api.json, there is a distinction between enums and flags: enums can only take exactly one of the finite states (+ future additions), while flags exist in a | superposition. I'm not sure if the distinction is relevant here? On a C level, both can be represented as integer constants...
So enum might be good enough, if we don't expect code generators to treat them any different (e.g. | operator overloading or so).
Metadata
Metadata
Assignees
Type
Projects
Status
{ "name": "GDExtensionVariantPtr", "kind": "handle", "description": [/* ... */] }, { "name": "GDExtensionConstVariantPtr", "kind": "handle", "parent": "GDExtensionVariantPtr", "const": true }, { "name": "GDExtensionUninitializedVariantPtr", "kind": "handle" },