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
use case: struct with embedded resizable last field #173
Comments
Don't overlook the case where you have two (n) arbitrarily sized buffers struct Model {
size_t len;
Vec3 *vertices;
Vec2 *uv;
} It would still be nice to allocate it all at once. This could be solved by passing the sizes in the type declaration just like we do for C arrays ( struct Model<static_len> {
size_t len;
Vec3 vertices[static_len];
Vec2 uv[static_len];
}
// You would store the data at the end of the struct and the two pointers to the data
sizeof(Model<10>) // sizeof(size_t) + 10 * sizeof(Vec3) + 10 * sizeof(Vec2) + sizeof(*Vec3) + sizeof(*Vec2) They would work like C arrays struct Model *model = malloc(sizeof(Model<10>)); // analogous to malloc(sizeof(int[10]))
// TODO: figure out how to set the pointers automatically
model->len = 10;
sizeof(*model); // = sizeof(size_t) + sizeof(*Vec3) + sizeof(*Vec2)
// It's not the size of the entire thing just like the size of *int_ptr_to_array
// is the size of a single int
// You could have them on the stack
Model<10> model;
And since you store the actual pointers in the struct, you can pass them to other functions
foo(&model);
void foo(Model *model) {
for (int i = 0; i < model->len; i++) {
// vertices and uv are pointers inside the struct
model->vertices[i];
model->uv[i];
}
} You could afford to store only one pointer here as you at least know where the first buffer starts. In general you would need n - 1 buffers to store n parameterized buffers in the struct. |
As Philix says, it is nice to allocate it all at once. However, I find that the extra gymnastics I do to figure out how big to allocate a "variable length" struct in one memory block are worth it for the other end of the lifespan: freeing the memory. I only have one free(). I do not need to know much of anything about the memory, or the type if it is all one block. Now, I can wrap allocation and deallocation in functions per struct type and call those specifically. That works too (and is probably not a bad thing). You can still do the variable length structure as one block even if you do not have the empty array at the end. Instead, have the last part be a pointer and point it just past the rest of the object as if it was a separate allocation. But the case with the end element being a zero length array is handy... |
I don't think this is correct: zig gives no assurances on struct layout, and may reorder fields. |
You are correct. I believe when I originally made that statement struct layout had defined order, but as you note that is no longer the case. |
this seems to be quite old, did zig ever give guarantees on struct layout? |
simply adding |
I'm currently using this: const Block = struct {
nbytes: usize,
bytep: *u8,
fn bytes(self: *Block) [*]u8 {
return @ptrCast([*]u8, self) + @sizeOf(self); // assumes @alignOf(u8) < @alignOf(self)
}
}; Allocating one is a bit weird... const blk = @ptrCast(*Block, try allocator.alignedAlloc(u8, @alignOf(Block), @sizeOf(Block) + len));
blk.* = Block{
.nbytes = nbytes,
.bytep = bytep,
}; (and it gets even weirder when I use the VLA as a linkedlist node type) The |
There is no plan to add an explicit language feature for this. |
Is there at least a plan for how to conventionally represent them? note that translate-c will need to handle them. |
One way for translate-c would be to represent a struct as a comptime function that forces one to set the explicit size of the array. A c struct like this:
Would translate to:
All references would get translated as calls with the array length parameter set to
This works well for cases when one accesses the array explicitly like in the above example. For cases when one has to rely on the length passed at runtime ( |
In C you can do something like this:
Here, instead of having
data
be a pointer in the struct, the struct itself is resized, along withdata
. The data is part of the struct; thedata
field points to the beginning.Zig does not currently have a convenient way to represent this concept. How you would do this in status quo land is:
I guess that's not so bad. We could leave status quo as is.
The text was updated successfully, but these errors were encountered: