Old style interfaces with and without wrappers

We can construct an interface without method calling wrappers.
I would call this “C-style” interfaces. Like this:

const log = @import("std").debug.print;

const IFace: type = struct {
    funcA: *const fn(*IFace) void,
    funcB: *const fn(*IFace) void,
};

const Thing: type = struct {
    data: u32,
    iface: IFace,

    fn init(d: u32) Thing {
        return .{
            .data = d,
            .iface = .{.funcA = myAImpl, .funcB = myBImpl},
        };
    }

    fn myAImpl(i: *IFace) void {
        const me: *Thing = @fieldParentPtr("iface", i);
        log("I can do A with {}\n", .{me.data});
    }

    fn myBImpl(i: *IFace) void {
        const me: *Thing = @fieldParentPtr("iface", i);
        log("Also I can do B with {}\n", .{me.data});
    }

};

pub fn main() void {
    var t = Thing.init(11);
    t.iface.funcA(&t.iface);
    t.iface.funcB(&t.iface);
}

Now add wrappers.

const IFace: type = struct {
    funcA: *const fn(*IFace) void,
    funcB: *const fn(*IFace) void,

    fn doA(i: *IFace) void {i.funcA(i);}
    fn doB(i: *IFace) void {i.funcB(i);}
};

// Thing is the same

pub fn main() void {
    var t = Thing.init(11);
    t.iface.doA();
    t.iface.doB();
}

So the question - do I understand it correctly that the only purpose of wrappers is to have the ability of using methods calling syntax sugar in main code?

Hoa! We can make function pointers optional and add some logic before invoking an implementation:

const log = @import("std").debug.print;

const IFace: type = struct {

    funcA: ?*const fn(*IFace) void = null,
    funcB: ?*const fn(*IFace) void = null,

    fn doA(i: *IFace) void {
        const f = i.funcA orelse {
            log("Defailt A implementation\n", .{});
            return;
            // or panic
        };
        f(i);
    }

    fn doB(i: *IFace) void {
        const f = i.funcB orelse {
            log("Defailt B implementation\n", .{});
            return;
            // or panic
        };
        f(i);
    }
};

const Thing: type = struct {
    data: u32,
    iface: IFace,

    fn init(d: u32) Thing {
        return .{
            .data = d,
            .iface = .{.funcA = myAImpl},
        };
    }

    fn myAImpl(i: *IFace) void {
        const me: *Thing = @fieldParentPtr("iface", i);
        log("I can do A with {}\n", .{me.data});
    }
};

pub fn main() void {
    var t = Thing.init(11);
    t.iface.doA();
    t.iface.doB();
}
2 Likes