My input is a Zig string, and I want to use it in a C function.
I saw quite a few variations on this basic question, but none seem to fit 0.14.1.
Hardcoded is ok. With:
const newPath: [:0]const u8 = "my-path.jpg"`
I can do:
m.MagickReadImage(wand, newPath.ptr)
But I want to use a runtime variable path: []const u8 and produce this [:0]const u8.
I can iterate over an allocated buf and copy path and add 0 at the end. But I have a []u8. I looked into a few std.fmt functions without success so far.
Going from non-sentinel to sentinel is the direction of friction because that’s one more byte that’s just not accounted for in the slice. It might not even exist for all we know. There may be more ways to solve this, but here are the 3 solutions I know:
Bite the bullet and allocate a new sentinel-terminated slice. The Allocator interface has a friendly dupeZ method.
Assume the zig string already ends in a null character and force it. This invokes a runtime panic if you’re wrong. Example usage:
I don’t know the exact context of your Zig string, but when in doubt, #1 is the safest choice. Also, if you use an arena for scratch space and reset it for this exact operation, it’s not a bad way to go in my opinion.
If you’re reading from stdin, you can perform some buffer trickery to swap out the byte of your delimiter (newline, presumably) with a null character. It’s a little hacky.
I’d be curious if you’re able to use a null-terminated slice, even with embedded Zig. Is it Elixir calling Zig? However, the only time I’ve called Zig from external code, it’s been from C#. I’ve had no issues passing [*:0]u8 (or [*:0]u16 for UTF16 encoding) from the .NET side with a byte* (or char*), but that’s a different tech stack. If Elixir uses the C ABI for interop, I wonder if you can do something like that too. I know next to nothing about Elixir.
This is, of course, the right approach. Unfortunately not all commonly used C libraries accept ptr+len. Postgres’ libpq is one big culprit, last time I looked.
I tried to concatenate ‘0’ to the string on the Elixir side but it does not produce a sentinel, just another string. So indeed std.mem.Allocator.dupeZ.
The C interop in Elixir/Erlang uses NIF. Then the library Zigler lets you consume directly Zig code as a “NIF”. Quite powerful and makes C become nice.
Yes, hence the question.
I was ensuring that this was the value they were using to null-terminate, as they stated that they used '0' (without slash) and it “does not produce a sentinel, just another string.”
Adding a '\0' should provide a proper sentinel value, unless Elixer is doing something odd that I am unaware of when marshaling the value, which seems unlikely.