4

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 
Renato
  • 12,940
  • 3
  • 54
  • 85
  • Have you tried: `int count_bytes(const char *)`? If you're not modifying it on the C side, const is a good idea. – Max Jun 13 '22 at 18:33
  • I am assuming the C code is not mine (though this is just an exercise for me to see if Zig can be used effectively to test C code), but even after trying to add const to the C code I don't get this to work. – Renato Jun 13 '22 at 19:19

1 Answers1

4

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)?

pfg
  • 2,348
  • 1
  • 16
  • 37
  • 1
    I originally did not have the const in the C parameter... forgot to add it... then after adding it I ran into the c_int error which threw me off completely... now everything makes sense, thanks for your assistance. – Renato Jun 13 '22 at 21:13