Disallow arrays as pointers

bearophile bearophileHUGS at lycos.com
Tue Nov 1 17:30:05 PDT 2011


In the meantime Walter has added the patch to the latest DMD.

Timon Gehr:

> That works without the cast.
> That works without the ".ptr".

Right. There is an implict conversion between the 2-word struct of the string to the nude pointer.


> I was casting to char*, not const(char*). It is just that occasionally C 
> headers/bindings lack the const qualifier. Then passing a string literal 
> is achieved simplest by a cast to char*.

In some situations like that I think that using someString.dup.ptr looks safer, or better using toStringz:
http://d-programming-language.org/cutting-edge/phobos/std_string.html#toStringz

I'd like toStringz to be pure and return a char*, so its result is assignable to immutable despite being fit for C functions without const too.

To do this, it can't contain a ~ (string concat) because currently the ~ is not a pure function, it always returns a string, dstring, wstring. So I think this should compile:

char[] foo(string s1, string s2) pure {
    return s1 ~ s2;
}
void main() {}


And regarding toStringz(), this modified version seems to work as desired:


import core.stdc.string;

char* toStringz(immutable(char[]) s) pure nothrow
in
{
    // The assert below contradicts the unittests!
    //assert(memchr(s.ptr, 0, s.length) == null,
    //text(s.length, ": `", s, "'"));
}
out (result)
{
    if (result)
    {
        auto slen = s.length;
        while (slen > 0 && s[slen-1] == 0) --slen;
        assert(strlen(result) == slen);
        assert(memcmp(result, s.ptr, slen) == 0); // overkill?
    }
}
body
{
    /+ Unfortunately, this isn't reliable.
     We could make this work if string literals are put
     in read-only memory and we test if s[] is pointing into
     that.

     /* Peek past end of s[], if it's 0, no conversion necessary.
     * Note that the compiler will put a 0 past the end of static
     * strings, and the storage allocator will put a 0 past the end
     * of newly allocated char[]'s.
     */
     char* p = &s[0] + s.length;
     if (*p == 0)
     return s;
     +/

    // Need to make a copy
    auto copy = new char[s.length + 1];
    copy[0..s.length] = s;
    copy[s.length] = 0;

    return copy.ptr;
}

void main() {
    string t = "hello";
    char* s1 = toStringz(t);
    const(char*) s2 = toStringz(t);
    immutable(char*) s3 = toStringz(t);
    immutable(char)* s4 = toStringz(t);
}


> I don't think we really need that feature, I just think that removing it breaks
> existing code without a reason.
> 
> extern(C) void puts(char* str);
> void main() {
>       puts(cast(char*)"hello");
> }

I prefer something like:

extern(C) void puts(char* str);
void main() {
    puts(Deconst!("hello".ptr));
}

This avoids a cast, a Deconst template doesn't change the data, it never forces the string to be of a specific char type.

Thank you for your answers,
bearophile


More information about the Digitalmars-d mailing list