-
Notifications
You must be signed in to change notification settings - Fork 136
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Help Wanted] Zig & Odin #3
Comments
For Odin:
instead of
an example from the source code how such a function is defined:
now, I see that these macros don't actually have return values, but you could certainly leverage this behavior anyway, to make fairly nice to use Odin bindings (which would be great, someone just posted this library in the Odin Discord, and it looks really useful!) |
Just for posterity the folks over at the Odin discord have been very helpful, and I'm going to have a crack at writing the Odin bindings today. |
I'm not an expert, but here's a Zig idea I toyed with: const std = @import("std");
const LayoutConfig = struct {};
// Would be the real imported Clay
const clay_ns = @This();
pub fn clay(layout: anytype) void {
const fields = std.meta.fields(@TypeOf(layout));
inline for (fields) |el| {
comptime var name: [el.name.len]u8 = undefined;
@memcpy(&name, el.name);
// Crudely capitalize the field name.
name[0] = comptime std.ascii.toUpper(name[0]);
const openFn = @field(clay_ns, "Clay__Open" ++ name ++ "Element");
const closeFn = @field(clay_ns, "Clay__Close" ++ name ++ "Element");
// The Clay element functions have different arities,
// so more code would be needed here.
const args = @field(layout, el.name);
const id_arg = args[0];
const config_arg = args[1];
openFn(id_arg, config_arg);
defer closeFn();
if (3 <= args.len) {
clay(args[2]);
}
}
}
const id = 123;
pub fn main() void {
clay(.{
.container = .{
id,
LayoutConfig{},
.{
.text = .{
id,
"text goes here",
},
.rectangle = .{
id,
LayoutConfig{},
},
},
},
});
}
fn Clay__OpenContainerElement(id_: usize, layout_config: LayoutConfig) void {
std.log.debug("open container element {d} {any}", .{ id_, layout_config });
}
fn Clay__CloseContainerElement() void {
std.log.debug("close container element", .{});
}
fn Clay__OpenTextElement(id_: usize, text: []const u8) void {
std.log.debug("open text element {d} {s}", .{ id_, text });
}
fn Clay__CloseTextElement() void {
std.log.debug("close text element", .{});
}
fn Clay__OpenRectangleElement(id_: usize, layout_config: LayoutConfig) void {
std.log.debug("open rectangle element {d} {any}", .{ id_, layout_config });
}
fn Clay__CloseRectangleElement() void {
std.log.debug("close rectangle element", .{});
} Running this program prints:
So you get the sandwiching, but it would need more work, and I don't know if it's a good direction. |
@illfygli That is super cool, thanks so much for the code samples! I will do some investigation when I have time, would be great to get it working from Zig. |
I've made some great progress with the Odin bindings today: #5 |
I'd love to see Nim bindings too, the macro system will allow for the functional form :) Edit: Missed the part where you explain how the macros work, should be simple to map to other DSL forms! I might take a crack if there are no takers |
@crystalthoughts Please feel free, I've never used nim myself but it looks cool 😁 |
Now that the odin bindings are out the door and I've got a reasonable idea of how long it takes to write them for another language, I'm probably going to delay writing the zig bindings until after I've finished some feature work. Still high on the priority list, though! |
Hey @nicbarker, I just watched your YT video and clay looks great! I have a Zig project that would benefit from a nice UI and I'd love to pitch in if you need any help with the bindings when you come around to it |
I would love for clay to be easily usable from both Zig and Odin.
However, there's a major sticking point with both that I don't know how to solve.
The core Element Macros rely on a particular feature of the C preprocessor, specifically "function-like macros" and the ability to pass arbitrary text as an argument to these macros. If you're not familiar with this in C, you can take a look at the definition of any of these macros in clay.h (example) and you'll see that they all follow a general form:
You can see that in order to correctly construct the layout hierarchy, child declarations need to preceded by a
Clay__Open...
and then followed by aClay__Close...
.In clay's use case, these macros allows you to automatically sandwhich child layout elements in the required open / close, by "passing a block as a macro argument" - creating (imo) a very nice developer experience:
As a result it's not actually possible to "forget" to close these containers and end up with a mismatch or with elements incorrectly parented - this macro syntax functions almost like a form of RAII.
Neither Zig nor Odin support this type of "function-like macro" where arbitrary text can be pasted in.
In Odin's case, it might be possible through some combination of
defer
and non capturing lambdas to replicate this type of behaviour, but what I'm really looking for is something fool proof - where you don't have to spend time debugging a missing call toClay__Close...
, and I don't have to build debug tools to help you with that 😛In Zig's case, AFAIK there is even less official support for closures, and just glossing over the docs I can't really think of a way to implement it that wouldn't make layout definition a mess.
Any help or out of the box ideas would be greatly appreciated!
The text was updated successfully, but these errors were encountered: