I'm creating a system that has "objects" which can have any amount of fields. The classes for these objects are created at runtime and can be arbitrarily made and destroyed (usually by code outside of the application that has access to its APIs). Since these objects can have any number of fields, I don't exactly know the size of the struct at compile-time.
Something similar to C flexible array members would be useful here, but I'm not sure if that's the right approach. If it is, then how do I allocate and index it properly, since Zig has no builtin support for them?
#Struct that I only know the size of at runtime
1 messages · Page 1 of 1 (latest)
indeed Zig does not have built-in support for extensible array members - or any other dynamically-sized types scheme for that matter.
I suggest you emulate the behaviour via a wrapper type and corresponding functions. for example, let us create a length-prefixed slice type: some memory location holds a len: usize and after that there are len u8s:
┌────────────┬──┬──┬──┬──┬──┬──┬──┬──┬──┬──┐
│ len: usize │u8│u8│u8│u8│u8│u8│u8│u8│u8│u8│
└────────────┴──┴──┴──┴──┴──┴──┴──┴──┴──┴──┘
├─────────────────────────────┤
↳ len
```obviously, since the type's length is unknown, we can only ever have a reference to it. this is quite a simple data schema, with only one reasonable access pattern: get the slice (from that we can get the length and pointer separately):
```rs
const LenPrefix = struct {
/// points to the header field
header: [*]u64,
fn slice(self: LenPrefix) []u8 {
const body: [*]u8 = @ptrCast(self.header + 1);
return body[0..self.header[0]];
}
};
```this is a simple example, but I hope it gets the point across :)
Could also make header a 0-bit field and then it'd actually be stored within the object's allocated space, more similar to a flexible array
const Foo = struct {
len: usize,
array_start: void,
pub fn create(allocator: Allocator, len: usize) *Foo {
const opaque = allocator.allocAligned(u8, @alignOf(Foo), @sizeOf(Foo) + len);
errdefer allocator.destroy(opaque);
const f: *Foo = @ptrCast(opaque);
f.* = .{ .len = len };
return f;
}
pub fn asSlice(f: anytype) switch @TypeOf(f) {
*const Foo => []const u8,
*Foo => []u8,
} {
const ptr: [*]const u8 = @ptrCast(&f.array_start);
return @constCast(ptr[0..f.len]);
}
};
I guess it'll also have to be an extern struct, to prevent field offset reordering
Hmm good point
Although ive seen this pattern before so I assume the compiler just won't move around 0-bit wide types
Although here you wouldn't lose anything by using an extern struct so its whatever
in any case, yeah, this is quite nice for static-prefix-dynamic-suffix constructs - I like it
Honestly first time constCast was ever useful for me lol