I understood this, but I seem to have confused you again, for which I apologize.
Anyways, what structure did you think I was referring to?
These ones.
It’s not.
Reader:
vtable: *const VTable,
buffer: []u8,
/// Number of bytes which have been consumed from `buffer`.
seek: usize,
/// In `buffer` before this are buffered bytes, after this is `undefined`.
end: usize,
Writer:
vtable: *const VTable,
/// If this has length zero, the writer is unbuffered, and `flush` is a no-op.
buffer: []u8,
/// In `buffer` before this are buffered bytes, after this is `undefined`.
end: usize = 0,
As you can see from the source, vtable is a pointer.
The formulation is technically incorrect, but what is implied by the question is that the pointer to the VTable is intrusive. Can you confirm the reasoning behind putting the pointer into the interface, or do you just consider it common sense?
It must be there: I’d encourage you to try implementing polymorphism another way.
I can’t make sense of the question, implied or otherwise. “Intrusive” comes from “intrusive linked list” which is an alternative where, instead of putting a pointer to the node in the struct, you put the node data itself.
There’s no functional difference between accepting struct as a parameter, or the fields of the struct as separate parameters.
An interface is a vtable.
I’m not haggling over semantics here. There simply isn’t a coherent question being asked using any possible definition of these words.
The question (as I understood) is:
Why pick
vtable: *const VTable,
buffer: []u8,
/// Number of bytes which have been consumed from `buffer`.
seek: usize,
/// In `buffer` before this are buffered bytes, after this is `undefined`.
end: usize,
over
buffer: []u8,
/// Number of bytes which have been consumed from `buffer`.
seek: usize,
/// In `buffer` before this are buffered bytes, after this is `undefined`.
end: usize,
const State = @This();
pub fn interface(self: *State) Interface {
return .{ .ptr = self, .vtable = &vtable };
}
pub const Interface = struct {
ptr: *State,
vtable: *const VTable,
};
since Allocator uses
ptr: *anyopaque,
vtable: *const VTable,
?
The two @This() have different types.
I think a better way to illustrate that code would be (or alternatively assign the @This() a name):
pub fn interface(self: *@This()) struct {
ptr: @TypeOf(self),
vtable: *const VTable,
} {
return .{ .ptr = self, .vtable = &vtable };
}
oh, yeah ![]()
this is the eternal question - who owns the memory? ![]()
You are right I edited the response. Thanks.
My original question was just about why the vtable pointer is stored where it is
As in, in Allocator and Io you pass a “fat pointer” a pointer to implementation and pointer to the vtable
Even though for Reader/Writer we need to store the buffer “above the vtable” there are still different ways we could do this
We could still pass around “fat pointer” with a pointer to the buffer state, which is embedded within the implementation (this is analogous to the type erased pointer) and a pointer to the vtable
And Allocator could be passed as a pointer to a vtable pointer stored inside the implementation, the blog by pithlessly which was posted earlier in the thread mentions this was possible but notes it would require double indirection
My question was around why the current way was chosen for each interface
The answer I was satisfied with from Vulpesx was that the Reader/Writer already pay the cost of the indirection since all calls to the vtable have to load the buffer first anyway
Hope this is more clear
(I would include code examples but this is typed on mobile)
I understand now, thank you for the clarification