I've recently started learning Zig. As a little project I wanted to implement a small QuickCheck [1] style helper library for writing randomized tests.
However, I can't figure out how to write a generic way to call a function with an arbitrary number of arguments.
Here's a simplified version that can test functions with two arguments:
const std = @import("std");
const Prng = std.rand.DefaultPrng;
const Random = std.rand.Random;
const expect = std.testing.expect;
// the thing we want to test
fn some_property(a: u64, b: u64) !void {
var tmp: u64 = undefined;
var c1 = @addWithOverflow(u64, a, b, &tmp);
var c2 = @addWithOverflow(u64, a, b, &tmp);
expect(c1 == c2);
}
// helper for generating random arguments for the function under test
fn gen(comptime T: ?type, rnd: Random) (T orelse undefined) {
switch (T orelse undefined) {
u64 => return rnd.int(u64),
f64 => return rnd.float(f64),
else => @compileError("unsupported type"),
}
}
/// tests if 'property' holds.
fn for_all(property: anytype) !void {
var rnd = Prng.init(0);
const arg_types = @typeInfo(@TypeOf(property)).Fn.args;
var i: usize = 0;
while (i < 100) {
var a = gen(arg_types[0].arg_type, rnd.random());
var b = gen(arg_types[1].arg_type, rnd.random());
var args = .{a, b}; // <-- how do I build args for functions with any number of arguments?
try @call(.{}, property, args);
i += 1;
}
}
test "test" {
try for_all(some_property);
}
I've tried a few different things, but I can't figure out how to get the above code to work for functions with any number of arguments.
Things I've tried:
- Make
argsan array and fill it with aninline forloop. Doesn't work since[]anytypeis not a valid type. - Use a bit of comptime magic to build a struct type whose fields hold the arguments for
@call. This hits a TODO in the compiler:error: TODO: struct args. - Write generic functions that return an appropriate argument tuple call. I don't really like this one, since you need one function for every arity you want to support. But it doesn't seem to work anyway since
antypeis not a valid return type.
I'm on Zig 0.9.1.
Any insight would be appreciated.