# test.zig -rw-r--r-- 2.5 KiB View raw
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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);
}