A Zig String literal is a single-item pointer to a null-terminated byte array, which is perfect to be used as a C's char *
String!
However, when I try to use this simple C function from Zig:
int count_bytes(const char *str) {
int count = 0;
while(str[count]) {
count ;
}
return count;
}
Zig caller:
const std = @import("std");
const c = @cImport({
@cInclude("add.c");
});
const testing = std.testing;
test "should be able to count string length" {
try testing.expectEqual(0, c.count_bytes(""));
}
I get this error:
./src/main.zig:16:46: error: expected type '[*c]u8', found '*const [0:0]u8'
try testing.expectEqual(0, c.count_bytes(""));
^
./src/main.zig:16:46: note: cast discards const qualifier
try testing.expectEqual(0, c.count_bytes(""));
^
This article explains about Zig String literals in a similar situation, but I was unable to use the trick to make the String non-const:
test "should be able to count string length" {
var cstr = "".*;
try testing.expectEqual(0, c.count_bytes(&cstr));
}
Result is an even stranger error:
./src/main.zig:17:45: error: expected type 'comptime_int', found 'c_int'
try testing.expectEqual(0, c.count_bytes(&cstr));
^
I also tried casting the String to a C Pointer as shown in the Zig Docs:
test "should be able to count string length" {
var cstr: [*c]u8 = &"".*;
try testing.expectEqual(0, c.count_bytes(cstr));
}
Which also gives an error:
./src/main.zig:16:27: error: expected type '[*c]u8', found '*const [0:0]u8'
var cstr: [*c]u8 = &"".*;
^
./src/main.zig:16:27: note: cast discards const qualifier
var cstr: [*c]u8 = &"".*;
^
How can I do this?
EDIT:
I am getting suggestions that do not work with Zig 0.9, which is the latest stable release as I am writing this.
Please try this out first if you think you know a solution... put the C file at src-c/add.c
, and the Zig file at src/main.zig
,
then run this to try:
zig test src/main.zig -I src-c
CodePudding user response:
error: expected type 'comptime_int', found 'c_int'
It's hard to see because of how zig currently prints the error location, but this error is actually for the second argument to expectEqual, not an error with the arguments to count_bytes
./src/main.zig:17:45: error: expected type 'comptime_int', found 'c_int'
try testing.expectEqual(0, c.count_bytes(&cstr));
^
Would be slightly clearer if it was like this:
./src/main.zig:17:45: error: expected type 'comptime_int', found 'c_int'
try testing.expectEqual(0, c.count_bytes(&cstr));
~~~~~~~~~~~~~~~~~~~~
The arrow is pointing to the (
, meaning it's intended to cover the entire call expression
This error is due to the way std.testing.expectEqual
infers parameter types.
expectEqual is defined as fn expectEqual(a: anytype, b: @TypeOf(a)) void
. This means that the type of b
must be the same as the type of a
and does not attempt peer type resolution.
In this case, the type of a
is comptime_int
because that is the default type for integer literals in zig, and then it errors when the second parameter is a runtime integer that cannot be cast to comptime_int
To solve this, you often need to explicitly cast whenever using expectEqual
try testing.expectEqual(@as(c_int, 0), c.count_bytes(""));
Issue #4437 proposes one possible solution to solve this, but no changes have been made yet.
error: expected type '[*c]u8', found '*const [0:0]u8'
I was not able to replicate this error with your provided code - is it possible that a header file incorrectly defines count_bytes
as int count_bytes(char *str)
instead of int count_bytes(const char *str)
?
CodePudding user response:
regarding to the docs you're passing a pointer to N items (*[N]T
) or pointer to runtime-known ([]T
) instead pass a many-item pointer ([*]T
) or a single-item pointer (*T
).
so using cstr.ptr
or &cstr[0]
should work.
test "should be able to count string length" {
var cstr = "";
try testing.expectEqual(0, c.count_bytes(cstr.ptr));
}