implement defining C variadic functions

This commit is contained in:
Veikka Tuominen
2022-12-13 00:14:54 +02:00
parent 728dd29f1a
commit 9bb1104e37
21 changed files with 598 additions and 12 deletions
+108 -1
View File
@@ -1,5 +1,6 @@
const builtin = @import("builtin");
const expect = @import("std").testing.expect;
const std = @import("std");
const expect = std.testing.expect;
fn add(args: anytype) i32 {
var sum = @as(i32, 0);
@@ -91,3 +92,109 @@ test "pass zero length array to var args param" {
fn doNothingWithFirstArg(args: anytype) void {
_ = args[0];
}
test "simple variadic function" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .windows and builtin.os.tag != .macos) return error.SkipZigTest; // TODO
if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO
const S = struct {
fn simple(...) callconv(.C) c_int {
var ap = @cVaStart();
defer @cVaEnd(&ap);
return @cVaArg(&ap, c_int);
}
fn add(count: c_int, ...) callconv(.C) c_int {
var ap = @cVaStart();
defer @cVaEnd(&ap);
var i: usize = 0;
var sum: c_int = 0;
while (i < count) : (i += 1) {
sum += @cVaArg(&ap, c_int);
}
return sum;
}
};
try std.testing.expectEqual(@as(c_int, 0), S.simple(@as(c_int, 0)));
try std.testing.expectEqual(@as(c_int, 1024), S.simple(@as(c_int, 1024)));
try std.testing.expectEqual(@as(c_int, 0), S.add(0));
try std.testing.expectEqual(@as(c_int, 1), S.add(1, @as(c_int, 1)));
try std.testing.expectEqual(@as(c_int, 3), S.add(2, @as(c_int, 1), @as(c_int, 2)));
}
test "variadic functions" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .windows and builtin.os.tag != .macos) return error.SkipZigTest; // TODO
if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO
const S = struct {
fn printf(list_ptr: *std.ArrayList(u8), format: [*:0]const u8, ...) callconv(.C) void {
var ap = @cVaStart();
defer @cVaEnd(&ap);
vprintf(list_ptr, format, &ap);
}
fn vprintf(
list: *std.ArrayList(u8),
format: [*:0]const u8,
ap: *std.builtin.VaList,
) callconv(.C) void {
for (std.mem.span(format)) |c| switch (c) {
's' => {
const arg = @cVaArg(ap, [*:0]const u8);
list.writer().print("{s}", .{arg}) catch return;
},
'd' => {
const arg = @cVaArg(ap, c_int);
list.writer().print("{d}", .{arg}) catch return;
},
else => unreachable,
};
}
};
var list = std.ArrayList(u8).init(std.testing.allocator);
defer list.deinit();
S.printf(&list, "dsd", @as(c_int, 1), @as([*:0]const u8, "hello"), @as(c_int, 5));
try std.testing.expectEqualStrings("1hello5", list.items);
}
test "copy VaList" {
if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; // TODO
if (builtin.zig_backend == .stage2_x86_64) return error.SkipZigTest; // TODO
if (builtin.cpu.arch == .aarch64 and builtin.os.tag != .windows and builtin.os.tag != .macos) return error.SkipZigTest; // TODO
if (builtin.cpu.arch == .x86_64 and builtin.os.tag == .windows) return error.SkipZigTest; // TODO
const S = struct {
fn add(count: c_int, ...) callconv(.C) c_int {
var ap = @cVaStart();
defer @cVaEnd(&ap);
var copy = @cVaCopy(&ap);
defer @cVaEnd(&copy);
var i: usize = 0;
var sum: c_int = 0;
while (i < count) : (i += 1) {
sum += @cVaArg(&ap, c_int);
sum += @cVaArg(&copy, c_int) * 2;
}
return sum;
}
};
try std.testing.expectEqual(@as(c_int, 0), S.add(0));
try std.testing.expectEqual(@as(c_int, 3), S.add(1, @as(c_int, 1)));
try std.testing.expectEqual(@as(c_int, 9), S.add(2, @as(c_int, 1), @as(c_int, 2)));
}
@@ -0,0 +1,12 @@
fn foo(...) void {}
fn bar(a: anytype, ...) callconv(a) void {}
comptime { _ = foo; }
comptime { _ = bar; }
// error
// backend=stage2
// target=native
//
// :1:1: error: variadic function must have 'C' calling convention
// :2:1: error: generic function cannot be variadic