@compileError("Error: No overload for " ++ @typeName(args_type) ++ "\n" ++ "Candidates are:\n" ++ candidatesMessage());
// ...
fn candidatesMessage() []const u8 {
var msg: []const u8 = "";
inline for (functions) |f| {
msg = msg ++ " " ++ @typeName(@TypeOf(f)) ++ "\n";
}
return msg;
}
Yes this works out quite nicely:
const std = @import("std");
pub fn isTuple(comptime T: type) bool {
return switch (@typeInfo(T)) {
.Struct => |s| s.is_tuple,
else => false,
};
}
inline fn detectOverloadError(args: anytype) ?[]const u8 {
inline for (args) |arg| {
switch (@typeInfo(@TypeOf(arg))) {
.Fn => {},
else => {
return "Non-function argument in overload set.";
},
}
}
inline for (args) |arg| {
for (@typeInfo(@TypeOf(arg)).Fn.params) |param| {
if (param.type == null) {
return "Generic parameter types in overload set.";
}
}
}
inline for (0..args.len) |i| {
const params0 = @typeInfo(@TypeOf(args[i])).Fn.params;
inline for (i + 1..args.len) |j| {
const params1 = @typeInfo(@TypeOf(args[j])).Fn.params;
const signatures_are_identical = params0.len == params1.len and
for (params0, params1) |param0, param1|
{
if (param0.type != param1.type) {
break false;
}
} else true;
if (signatures_are_identical) {
return "Identical function signatures in overload set.";
}
}
}
return null;
}
pub fn OverloadSet(comptime functions: anytype) type {
if (comptime detectOverloadError(functions)) |error_message| {
@compileError("Error: " ++ error_message);
}
return struct {
const no_matching_overload_found = struct {};
fn findMatchingFunctionIndex(comptime args_type: type) comptime_int {
const args_fields = @typeInfo(args_type).Struct.fields;
inline for (functions, 0..) |function, i| {
const function_type_info = @typeInfo(@TypeOf(function)).Fn;
const params = function_type_info.params;
const match = params.len == args_fields.len and
inline for (params, args_fields) |param, field|
{
if (param.type.? != field.type) break false;
} else true;
if (match) return i;
}
return -1;
}
fn candidatesMessage() []const u8 {
var msg: []const u8 = "";
inline for (functions) |f| {
msg = msg ++ " " ++ @typeName(@TypeOf(f)) ++ "\n";
}
return msg;
}
fn OverloadSetReturnType(comptime args_type: type) type {
if (!isTuple(args_type)) {
@compileError("Error: OverloadSet's call argument must be a tuple.");
}
if (@typeInfo(args_type).Struct.fields.len == 0) {
return void;
}
const function_index = findMatchingFunctionIndex(args_type);
if (function_index < 0) {
return no_matching_overload_found;
}
const function = functions[function_index];
return @typeInfo(@TypeOf(function)).Fn.return_type.?;
}
pub fn call(args: anytype) OverloadSetReturnType(@TypeOf(args)) {
if (comptime args.len == 0) {
@compileError("Error: Cannot invoke call function on empty OverloadSet.");
}
const function_index = findMatchingFunctionIndex(@TypeOf(args));
if (function_index < 0) {
@compileError("Error: No overload for " ++ @typeName(@TypeOf(args)) ++ "\n" ++ "Candidates are:\n" ++ candidatesMessage());
}
const function = functions[function_index];
return @call(.always_inline, function, args);
}
};
}
fn nothing() void {}
fn add(a: i32, b: i32) i32 {
return a + b;
}
fn addMul(a: i32, b: i32, c: i32) i32 {
return (a + b) * c;
}
pub fn main() !void {
const set = OverloadSet(.{ nothing, add, addMul });
set.call(.{42});
}
overloadedfunctionsets.zig:117:13: error: value of type 'overloadedfunctionsets.OverloadSet(.{(function 'nothing'), (function 'add'), (function 'addMul')}).no_matching_overload_found' ignored
set.call(.{42});
~~~~~~~~^~~~~~~
overloadedfunctionsets.zig:117:13: note: all non-void values must be used
overloadedfunctionsets.zig:117:13: note: this error can be suppressed by assigning the value to '_'
referenced by:
...
overloadedfunctionsets.zig:100:17: error: Error: No overload for struct{comptime comptime_int = 42}
Candidates are:
fn () void
fn (i32, i32) i32
fn (i32, i32, i32) i32
@compileError("Error: No overload for " ++ @typeName(@TypeOf(args)) ++ "\n" ++ "Candidates are:\n" ++ candidatesMessage());
^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~