为@call 构建可变长度参数数组

Build variable length arguments array for @call

我最近开始学习 Zig。 作为一个小项目,我想实现一个小型 QuickCheck [1] 风格的帮助程序库来编写随机测试。

但是,我不知道如何编写一种通用方法来调用具有任意数量参数的函数。

这是一个简化版本,可以测试带有两个参数的函数:

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);
}

我尝试了几种不同的方法,但我无法弄清楚如何让上述代码适用于具有任意数量参数的函数。

我尝试过的事情:

我正在使用 Zig 0.9.1。

如有任何见解,我们将不胜感激。

[1] https://hackage.haskell.org/package/QuickCheck

这可以通过 std.meta.ArgsTuple 完成(在 zig 标准库的 this file 中定义)

    const Args = std.meta.ArgsTuple(@TypeOf(property));

    var i: usize = 0;
    while (i < 1000) : (i += 1) {
        var args: Args = undefined;
        inline for (std.meta.fields(Args)) |field, index| {
            args[index] = gen(field.field_type, rnd.random());
        }

        try @call(.{}, property, args);
    }

它在内部工作的方式是用 @Type() 构造一个元组类型。然后我们可以用值填充它并用它来调用函数。