There are situations where you have to call a C dispatch function, and pass it
a void* and a selector. The selector lets you choose what the C function does,
for example an enum constant selector `kGetProductName` could ask the C
function to fill a null-terminated string at the location of the void* you've
passed in.
One way of doing this is to pass the .ptr field of a static or dynamic char
array to the C function, letting it fill the array with a null-terminated
string.
But here's the problem: If you try to print out that array in D code with e.g.
writefln, it will print out the _entire length_ of the array.
This is a problem because the array could quite likely be filled with garbage
values after the null terminator. In fact I just had that case when interfacing
with C.
to!string can convert a null-terminated C string to a D string, with the length
matching the location of the null-terminator. But for char arrays, it won't do
any checks for null terminators. It only does this if you explicitly pass it a
char*.
So I've come up with a very simple solution:
module fromStringz2;
import std.stdio;
import std.conv;
import std.traits;
import std.string;
enum
{
kGetProductName = 1
}
// imagine this function is defined in a C DLL
extern(C) void cDispatch(void* payload, int selector)
{
if (selector == kGetProductName)
{
char* val = cast(char*)payload;
val[0] = 'a';
val[1] = 'b';
val[2] = 'c';
val[3] = '\0';
}
}
string fromStringz(T)(T value)
{
static if (isArray!T)
{
return to!string(cast(char*)value);
}
else
{
return to!string(value);
}
}
string getNameOld()
{
static char[256] name;
cDispatch(name.ptr, kGetProductName);
return to!string(name);
}
string getNameNew()
{
static char[256] name;
cDispatch(name.ptr, kGetProductName);
return fromStringz(name);
}
void main()
{
assert(getNameOld().length == 256); // values after [3] could quite
// likely be garbage
assert(getNameNew().length == 3);
}
I admit I didn't take Unicode into account, so its far from being perfect or
safe.
In any case I think its useful to have such a function, since you generally do
not want the part of a C string after the null terminator.