5 ms·
I haven't worked with GTK, but what you are describing here sounds reminiscent of what we have been dealing with trying to build Godot bindings in Zig with a ni
by rvrb 1y ago
I haven't worked with GTK, but what you are describing here sounds reminiscent of what we have been dealing with trying to build Godot bindings in Zig with a nice API. the project is in mid-flight, but Godot:
- has tons of OOP concepts: classes, virtual methods, properties, signals, etc
- a C API to work with all of those concepts, define your own objects, properties, and so on
- manages the lifetimes of any engine objects (you can attach userdata to any of them)
- a whole tree of reference counted objects
it's a huge headache trying to figure out how to tie it into Zig idioms in a way that is an optimal API (specifically, dealing with lifetimes). we've come pretty far, but I am wondering if you have any additional insights or code snippets I should look at.
working on this problem produced this library, which I am not proud of: https://github.com/gdzig/oopz https://github.com/gdzig/oopz
here's a snippet that kind of demonstrates the state of the API at the moment: https://github.com/gdzig/gdzig/blob/master/example/src/SignalNode.zig https://github.com/gdzig/gdzig/blob/master/example/src/Signa...
also.. now I want to attempt to write a Ghostty frontend as a Godot extension
- aidenn0 1y agoHopefully it's improved, but the last time I wrote a GTK binding for a language, it was miserable. 98% of it was sane, but the remaining 2% had things like "whether or not this function takes a reference to this object depends on the other parameters passed" which made liveness analysis "interesting."
- DanielHB 1y agoThis is what people mean when they say "make invalid states unrepresentable", languages with union types can easily avoid this problem.
- biorach 1y agoIt doesn't sound like they are talking about invalid states, more like they are taking about the kind of thing that in Rust would be represented by `Option<Box<dyn SomeTrait>>` or suchlike. Maybe your point is that in Rust much less ceremony is necessary to avoid hitting a null pointer when doing this. But still, in either language it's easy to end up with hard to follow logic when doing this.
- DanielHB 1y agonot super familiar with Rust but isn't Option<T> just an union type of null and T? I get the language has special semantics for this compared to a union type but it is conceptionally just an union. For example this is something you can do with typescript. function(args: Arguments) { ... } type Arguments = { a: number, b: number } | { a: number, b: string, c: number } the Arguments { a: 1, b: 1, c: 1 } is not representable.
- qzzi 1y agoI'm pretty sure they're talking about reference counting that depends on the arguments, not about optional arguments or invalid argument combinations.
- 0x457 1y ago> not super familiar with Rust but isn't Option<T> just an union type of null and T? Only if there is a niche optimization happens if T is never null, otherwise it's a tagged union. That's not what you're replying to is about.
- poetril 1y agoI did not know this was a project that was in progress, and its quite exciting. I love Godot, and am quite fond a Zig as well. I'll be keeping my eye on this.
- Fraterkes 1y agoThx for the work you're doing! Just out of curiosity, I sometimes struggle to write performant C# Godot code because it's hard to interface with the engine without doing a lot of back and forth conversions to engine types. You end up doing a lot of allocations. Did you run into that kind of stuff while creating your bindings?
- rvrb 1y agoMy understanding may be out of date, but the C# support was created before GDExtension existed. The team has been working hard on porting it over to GDExtension. Once they are done, it should be much more performant, and they will finally be able to ship only one version of the editor. I believe the original C# bindings do a lot of unnecessary marshaling at the ABI. With GDExtension, the core builtin types like `Vector3` are passed by value. Avoid unnecessarily wrapping them in Variant, a specialized tagged union, where you can. You can see the documentation here; you have direct access to the float fields: https://gdzig.github.io/gdzig/#gdzig.builtin.vector3.Vector3 https://gdzig.github.io/gdzig/#gdzig.builtin.vector3.Vector3 Engine objects are passed around as opaque pointers into engine managed memory. The memory for your custom types is managed by you. You allocate the engine object and essentially attach userdata to it, tagged with a unique token provided to your extension. You can see the header functions that manage this: https://github.com/godotengine/godot/blob/e67074d0abe9b69f3d1944293342f0664e34c946/core/extension/gdextension_interface.h#L2535-L2585 https://github.com/godotengine/godot/blob/e67074d0abe9b69f3d... But, this is how the lifetimes for the objects gets slightly hairy (for us, the people creating the language bindings). Our goal with the Zig bindings is to make the allocations and lifetimes extremely obvious, a la Zig's philosophy of "No hidden memory allocations". It is proving somewhat challenging, but I think we can get there. There's still a lot of surprising or unintuitive allocations that can happen when calling into Godot, but we hope to expose those. My current idea is to accept a `GodotAllocator` on those functions (and do nothing with it; just use it to signal the allocation). You can read the code for the `GodotAllocator` implementation: https://github.com/gdzig/gdzig/blob/master/gdzig/heap.zig#L8-L132 https://github.com/gdzig/gdzig/blob/master/gdzig/heap.zig#L8... If we succeed, I think Zig can become the best language to write highly performant Godot extensions in.
- 1y ago