const std = @import("std");
const std_dispatch = struct {
fn dispatch_type(comptime types: var, a: type, b: type) type {
for (@typeInfo(@TypeOf(types)).Struct.fields) |field| {
if (@field(types, field.name)(a, b)) |typ| {
return typ;
}
}
std.debug.panic("no type", .{});
}
fn dispatch_impl(comptime impls: var, comptime types: var, a: type, b: type) fn(a,b) types(a,b) {
for (@typeInfo(@TypeOf(impls)).Struct.fields) |field| {
if (@field(impls, field.name)(a, b)) |impl| {
return impl;
}
}
std.debug.panic("no impl", .{});
}
};
const std_math = struct {
fn add_type(comptime a: type, comptime b: type) type {
const typ = comptime std_dispatch.dispatch_type(@import("root").add_types, a, b);
return typ;
}
fn add(a: var, b: var) add_type(@TypeOf(a), @TypeOf(b)) {
const impl = comptime std_dispatch.dispatch_impl(@import("root").add_impls, add_type, @TypeOf(a), @TypeOf(b));
return impl(a,b);
}
};
const lib_foo = struct {
const Vec2 = [2]usize;
fn add_vec_vec(a: Vec2, b: Vec2) Vec2 {
return .{a[0]+b[0], a[1]+b[1]};
}
fn add_type(comptime a: type, comptime b: type) ?type {
if (a == Vec2 and b == Vec2) {
return Vec2;
}
return null;
}
fn add_impl(comptime a: type, comptime b: type) if (add_type(a,b)) |typ| ?fn(a,b) typ else @TypeOf(null) {
if (a == Vec2 and b == Vec2) {
return add_vec_vec;
}
return null;
}
};
const lib_bar = struct {
const Mat2 = [4]usize;
fn add_mat_mat(a: Mat2, b: Mat2) Mat2 {
return .{a[0]+b[0], a[1]+b[1], a[2]+b[2], a[3]+b[3]};
}
fn add_type(comptime a: type, comptime b: type) ?type {
if (a == Mat2 and b == Mat2) {
return Mat2;
}
return null;
}
fn add_impl(comptime a: type, comptime b: type) if (add_type(a,b)) |typ| ?fn(a,b) typ else @TypeOf(null) {
if (a == Mat2 and b == Mat2) {
return add_mat_mat;
}
return null;
}
};
// root file
const add_types = .{
lib_foo.add_type,
lib_bar.add_type,
};
const add_impls = .{
lib_foo.add_impl,
lib_bar.add_impl,
};
pub fn main() !void {
const c1 = std_math.add([2]usize{1,4243}, [2]usize{1,1});
const c2 = std_math.add([4]usize{1,2,3,4}, [4]usize{1,1,1,1});
std.debug.warn("{} {}\n{} {} {} {}\n", .{c1[0], c1[1], c2[0], c2[1], c2[2], c2[3]});
// _ = add(0,0);
}