I'm trying to implement a simple interface (for learning purposes) named Shape1 which hopefully allow me to iterate over different shapes generically and execute their area() methods:
const Circle = struct {
radius: f32,
fn area(self: *anyopaque) f32 {
const s: *const Circle = @ptrCast(@alignCast(self));
return std.math.pi * s.radius * s.radius;
}
};
const Square = struct {
side: f32,
fn area(self: *anyopaque) f32 {
const s: *const Square = @ptrCast(@alignCast(self));
return s.side * s.side;
}
};
const Shape1 = struct {
ptr: *anyopaque,
vtab: *const VTab,
const VTab = struct {
areaFn: *const fn (ptr: *anyopaque) f32,
};
fn area(self: *Shape1) f32 {
return self.vtab.areaFn(self.ptr);
}
fn init(ptr: *anyopaque, f: *const fn (ptr: *anyopaque) f32) Shape1 {
return .{
.ptr = ptr,
.vtab = &VTab{ .areaFn = f },
};
}
};
pub fn main() !void {
var sq = Square{ .side = 2 };
var cr = Circle{ .radius = 2 };
var shapes = [_]Shape1{
Shape1.init(&sq, Square.area),
Shape1.init(&cr, Circle.area),
};
for (&shapes) |*shape| {
std.log.debug("{d}", .{shape.area()});
}
}
First, I was trying to use ptr: *const anyopaque and it succeeded. Then, I switched to non-const pointers and now, code simply segfaults. I'm not sure what's wrong with it. Maybe because I'm taking &Vtable {...} in init. Even if so, I don't know how to do the same without using an allocator (and it seems other people manage to do the same "fat pointer" interfaces without using any allocators). Please, help! 🥲