A use case for fromStringz

Andrej Mitrovic none at none.none
Thu Mar 31 11:25:33 PDT 2011


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.




More information about the Digitalmars-d-learn mailing list