I have this packed struct, which is part of another packed struct.
That is because the size must be as small as possible.
pub const Letters = packed struct
{
l0: u6,
l1: u6,
l2: u6,
l3: u6,
l4: u6,
l5: u6,
l6: u6,
};
I was wondering if instead of this:
fn set(self: *Letters idx: u3, value: u6) void
{
switch (idx)
{
0 => self.l0 = value, // etc
}
}
I can use some supersmart @builtin function to directly set a value by index?
You can use @field
and comptimePrint
to achieve this:
@field(self, std.fmt.comptimePrint("l{}", .{idx})) = value;
Another alternative would be to use an enum for idx:
const Index = enum (u3) {
l0 = 0,
l1 = 1,
...
};
...
@field(self, @tagName(idx)) = value;
1 Like
Aha how advanced. I never saw comptimePrint, but for that idx
needs to be comptime!
You can make idx comptime using switch and inline else:
switch(idx) {
inline else => |comptime_idx| {
...
},
}
This basically auto-generates one case per possible value.
2 Likes
Sze
5
You can use std.meta.fieldNames
and a switch with inline else to convert the runtime index to a comptime index:
const std = @import("std");
pub const Letters = packed struct {
l0: u6,
l1: u6,
l2: u6,
l3: u6,
l4: u6,
l5: u6,
l6: u6,
fn set(self: *Letters, idx: u3, value: u6) void {
const fields = comptime std.meta.fieldNames(Letters);
switch (idx) {
7 => @panic("out of bounds"),
inline else => |i| @field(self, fields[i]) = value,
}
}
};
pub fn main() !void {
var letters: Letters = .{
.l0 = 0,
.l1 = 0,
.l2 = 0,
.l3 = 0,
.l4 = 0,
.l5 = 0,
.l6 = 0,
};
letters.set(2, 5);
}
3 Likes
Ok thanks! That works great. I was wondering if the “normal” switch generates the same code. Because this case is so small, would it matter?