#VFTable vs tagged union

1 messages · Page 1 of 1 (latest)

wooden sentinel
#

I wanted to know whether it is better to use a VFTable (like allocators) or tagged union in this case.


pub const MemoryAccessorType = enum {
    DirectMemoryAccessor,
};

pub const MemoryAccessor = union(MemoryAccessorType) {
    DirectMemoryAccessor: DirectMemoryAccessor,
    const Self = @This();
    pub fn read(this: Self, T: anytype, address: *anyopaque) if(@typeInfo(T) == .Optional) T else ?T {
        return switch (this) {
            .DirectMemoryAccessor => |dma| dma.read(T, address),
        };
    }
    pub fn readSlice(this: Self, T: anytype, buffer: []T, address: *anyopaque) bool {
        return switch (this) {
            .DirectMemoryAccessor => |dma| dma.readSlice(T, buffer, address),
        };
    }
};
pub const DirectMemoryAccessor = struct {
    processHandle: win32.HANDLE,
    const Self = @This();
    pub fn read(this: Self, T: anytype, address: *anyopaque) if(@typeInfo(T) == .Optional) T else ?T {
        var buf: T  = undefined;
        var bytesRead: u32 = undefined;
        if(NtReadVirtualMemory(this.processHandle, address, @ptrCast(&buf), @sizeOf(T), &bytesRead) != win.NTSTATUS.SUCCESS or bytesRead != @sizeOf(T))
            return null;
        return buf;
    }
    pub fn readSlice(this: Self, T: anytype, buffer: []T, address: *anyopaque) bool {
        var bytesRead: u32 = undefined;
        if(NtReadVirtualMemory(this.processHandle, address, @ptrCast(buffer.ptr), @truncate(buffer.len*@sizeOf(T)), &bytesRead) != win.NTSTATUS.SUCCESS or bytesRead != buffer.len*@sizeOf(T))
            return true;
        return false;
    }
};

I'll add more Memory Accessors in the future but I still want to be able to call different accessors using the same MemoryAccessor type.

fluid mason
#

you can use inline else for your switch statements btw which avoids having to list each enum variant:

return switch (this) {
  inline else => |ma| ma.read(T, address);
};

also i wouldnt say either is better or worse in general, the downside of a union is that all the variants will be the size of the largest variant, and the downside of a vtable pointer is having to dereference a pointer to get to your data. if one of your MemoryAccessor variants is going to have a lot more data associated with it than the other variants that could be an issue for a union.

A benefit of a union is not having to worry about the lifetime of the implementation, ie you can store an array of MemoryAccessor unions no problem, but if they're vtable+ptrs you have to make sure whatever theyre pointing to lives long enough.

maiden echo
#

The big downside for unions is that they all need to be defined upfront. If you want 3rd parties to build implementations (like Allocator), there's only one choice.

The size issue with unions can be solved by using a pointer.