Const, invariant, strings and "constant data need never be copied"

Janice Caron caron800 at googlemail.com
Fri Nov 2 15:32:10 PDT 2007


On 11/1/07, Stewart Gordon <smjg_1998 at yahoo.com> wrote:
> In DMD 2.006, the definition of string was changed from const(char)[] to
> invariant(char)[] (and similarly wstring and dstring).  This change has no
> doubt broken a fair amount of D 2.x code.

All of my D2 code compiled without change.


> Declaring these with invariant parameters therefore means that it
> is often necessary to .idup a string just to pass it to one of these
> functions.

That's not true. If /all/ strings are invariant, throughout, then
everything works.


> Moreover, if a piece of code manipulates strings with a mixture
> of direct modification and calls to std.string functions, it necessitates
> quite a bit of copying of strings.

No it doesn't, it merely means ensuring that the reference is unique
and then calling assumeUnique().


> void main(string[] a) {
>     char[] text = cast(char[]) read(a[1]);

Well that line's wrong for a start. It should be
    string text = cast(string)read(a[1]);

There's your problem right there.


>     foreach (ref char c; text) {
>         if ((c >= 'A' && c <= 'M') || (c >= 'a' && c <= 'm')) {
>             c += 13;
>         } else if ((c >= 'N' && c <= 'Z') || (c >= 'n' && c <= 'z')) {
>             c -= 13;
>         }
>     }

I believe that should be

    bool willChange = false;
    foreach(char c;text) if (inPattern["A-Za-z"]) { willChange = true; break }
    if (willChange)
    {
        char[] s = text.dup;
        foreach (ref char c; s) {
           if ((c >= 'A' && c <= 'M') || (c >= 'a' && c <= 'm')) {
               c += 13;
           } else if ((c >= 'N' && c <= 'Z') || (c >= 'n' && c <= 'z')) {
               c -= 13;
        }
        text = assumeUnique(s);
    }


(Before this release, I would have written
    text = cast(string)s;
That still compiles without complaint, but assumeUnique() is better).

The test to see if the string will change is good copy-on-write
behavior. The rest is your code, adapted to how you're supposed to do
things in D2.006. First you dup text, because that string /might/ be
in ROM. Then you make your changes. When you've got what you want, you
use assumeUnique() to turn it back into a string. This does /not/ make
a copy.



> In D 2.x, it's
> necessary to change one line, to something like
>
>     text = text.idup.replace("\r\n", "\n").replace("\r", "\n").dup;

I don't think that's right. You just declare text to be string instead
of char[] and those dups become unnecessary.


> There are a few caveats to this example:
> - the .idup is only because std.file.read currently returns a mutable
> void[] - we could actually cast it to an invariant as nothing else is going
> to use it

Not could. Should.


> There are probably plenty
> of more involved examples in which there's more difference than this between
> the 1.x and 2.x code.

If every string function you write obeys the copy-(only)-on-write
protocol, then I don't see that.


> 3. Some concept of const-transparent functions.

I believe that's in the planning stage.



More information about the Digitalmars-d mailing list