www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - const issues and more!

reply "Neil Vice" <psgdg swiftdsl.com.au> writes:
I've been experimenting with const et. al. in 2.009 and have encountered 
some snags which I'll log here. It is quite likely that some of these issues 
are unrelated to the recent const changes or are things I have misunderstood 
and I will appreciate any feedback.

Firstly the following simple initialiser fails to compile:

    char[] text = "text";

As far as I can tell there is no way to declare and initialise an array in a 
single line of code or even copy an array in a single line once declared. 
The only way I'm aware of to copy an array is as follows:

    char[] text;
    text.length = "text".length;
    text[] = "text";

Given this, is it possible to declare a const string? toString() cannot even 
be called on a const variable!

It would appear that constness can be implicitly cast away through the use 
of a foreach loop as in the following:

    const char[] test;
    foreach(char c; test)
    {
        c = 'x';
    }

Note that c is not even declared ref.

DMD crashes when a method is called which takes a "ref const" parameter 
e.g.:

    void test(ref const char c) { }
    void main()
    {
        char c;
        test(c);
    }

This of course begs the question: what exactly is a ref const?

Also, the reason I discovered all of this is that I was attempting to 
implement a collection type. I defined a regular opApply method which worked 
fine on non-const instances. Declaring opApply to be a const method allows 
it to curiously work for both const and non-const instances in order to gain 
access to const and non-const members of the collection as described above.

I've struggled to correctly implement a const opApply at all, and when 
opApply is overloaded to have a const and a non-const definition, it is then 
impossible to use the foreach syntax that detects the member type for you.

All-in-all, attempting to write a collection class that works (specifically 
foreach/opApply) for both const and non-const instances has been 
frustratingly awkward if not impossible with DMD 2.009.

Reassurances that my dreams of D being a great programming language have not 
been shattered are welcome =) 
Jan 14 2008
next sibling parent reply Matti Niemenmaa <see_signature for.real.address> writes:
Neil Vice wrote:
 I've been experimenting with const et. al. in 2.009 and have encountered 
 some snags which I'll log here. It is quite likely that some of these issues 
 are unrelated to the recent const changes or are things I have misunderstood 
 and I will appreciate any feedback.

I'm only proficient in 1.0 and thus not very up-to-date with the current const situation, but I'll answer what I do (think I) know.
 Firstly the following simple initialiser fails to compile:
 
     char[] text = "text";
 
 As far as I can tell there is no way to declare and initialise an array in a 
 single line of code or even copy an array in a single line once declared. 
 The only way I'm aware of to copy an array is as follows:
 
     char[] text;
     text.length = "text".length;
     text[] = "text";

How about: char[] text = "text".dup; .dup creates a mutable copy. There's also .idup, for an immutable copy, but I don't know its semantics. <snip>
 It would appear that constness can be implicitly cast away through the use 
 of a foreach loop as in the following:
 
     const char[] test;
     foreach(char c; test)
     {
         c = 'x';
     }
 
 Note that c is not even declared ref.

I guess this is the same thing as doing the following: const int y; foo(y); void foo(int n) { n = n+1; } I.e. since you don't have ref, it's just a copy of the value which is modified. You'll note that the original test array is unmodified even if it's not const: char[] foo = "foo"; foreach (c; foo) c = 'x'; assert (foo == "foo"); // passes
 DMD crashes when a method is called which takes a "ref const" parameter 
 e.g.:
 
     void test(ref const char c) { }
     void main()
     {
         char c;
         test(c);
     }
 
 This of course begs the question: what exactly is a ref const?

If DMD crashes and this isn't in the Bugzilla, file the bug. If 'ref const' does work it's probably one or the other. DMD has a habit of accepting contradictory attributes: public private protected int x; // quiz: what is the protection level of x? <snip>
 Reassurances that my dreams of D being a great programming language have not 
 been shattered are welcome =) 

Use 1.0 and live happily, waiting for the const stuff to be finalized. ;-) -- E-mail address: matti.niemenmaa+news, domain is iki (DOT) fi
Jan 14 2008
parent reply "Neil Vice" <psgdg swiftdsl.com.au> writes:
Thanks for the reply.

 How about:

 char[] text = "text".dup;

 .dup creates a mutable copy. There's also .idup, for an immutable copy, 
 but I don't know its semantics.

You are of course correct, however it seems a tad unnecessarily verbose IMHO.
 I guess this is the same thing as doing the following:

 const int y;
 foo(y);

 void foo(int n) {
 n = n+1;
 }

 I.e. since you don't have ref, it's just a copy of the value which is 
 modified. You'll note that the original test array is unmodified even if 
 it's not const:

 char[] foo = "foo";
 foreach (c; foo)
 c = 'x';
 assert (foo == "foo"); // passes

I had overlooked that. I don't see the utility of having a modifiable local copy however and it strikes me as a litle confusing and error-prone. I would expect that the common case is to want a const reference, which ends up being the most verbose declaration unfortunately.
 If DMD crashes and this isn't in the Bugzilla, file the bug.

It would appear the bug has indeed been filed (several times =P).
 If 'ref const' does work it's probably one or the other. DMD has a habit 
 of accepting contradictory attributes:

From browsing other posts I get the impression that it is a way of passing structs as readonly refernces, however I would have expected just const to function the same.
 public private protected int x; // quiz: what is the protection level of 
 x?

My guess would be protected but the fact that it compiles seems ridiculous.
 Use 1.0 and live happily, waiting for the const stuff to be finalized. ;-)

I may have to do just that but I do love my const =)
Jan 14 2008
parent reply Matti Niemenmaa <see_signature for.real.address> writes:
Neil Vice wrote:
 How about:

 char[] text = "text".dup;

 .dup creates a mutable copy. There's also .idup, for an immutable copy, 
 but I don't know its semantics.

You are of course correct, however it seems a tad unnecessarily verbose IMHO.

The advantage lies in optimization, as it's (more?) common to not want to modify a string literal, but only to output it or a part of it. I don't find it that verbose, but I don't think I've ever used it in code other than such examples. :-P
 char[] foo = "foo";
 foreach (c; foo)
 c = 'x';
 assert (foo == "foo"); // passes

I had overlooked that. I don't see the utility of having a modifiable local copy however and it strikes me as a litle confusing and error-prone. I would expect that the common case is to want a const reference, which ends up being the most verbose declaration unfortunately.

Yep. A while back ( http://www.digitalmars.com/webnews/newsgroups.php?art_group=digitalmar .D&article_id=54361 ) practically everybody wanted to try const by default on function parameters (by extension, it should probably apply to foreach as well though I don't think it was discussed). We got no response from Walter though and it doesn't seem like it'll happen. Regarding 'most verbose', 'in' should still work for the parameter case, unless I've missed something: void foo(in int n) { n = n+1; // error, n is "const scope" } "in" used to mean "final const scope": IIRC "final" has been dropped but the other two remain. I may be wrong due to not having coded with the new const.
 If 'ref const' does work it's probably one or the other. DMD has a habit 
 of accepting contradictory attributes:

From browsing other posts I get the impression that it is a way of passing structs as readonly refernces, however I would have expected just const to function the same.

That would make sense, but I don't think it's implemented yet. Once again, I may be wrong, though.
 public private protected int x; /+ quiz: what is the protection level of 
 x? +/

My guess would be protected but the fact that it compiles seems ridiculous.

I agree, but not all do. Walter, for one, doesn't, and he's the one that counts. One thing where it admittedly makes sense is in something like: public: /+ lots of declarations +/ private int foo; The declaration of 'foo' wouldn't be allowed if the above were disallowed, since that's the equivalent of writing "public private int foo;". Although I don't see why writing attributes all together ("public private") can't be treated differently from the style of applying the attribute to all following declarations ("public: private"). -- E-mail address: matti.niemenmaa+news, domain is iki (DOT) fi
Jan 14 2008
parent reply Michiel Helvensteijn <nomail please.com> writes:
Matti Niemenmaa wrote:

 How about:

 char[] text = "text".dup;

 .dup creates a mutable copy. There's also .idup, for an immutable copy,
 but I don't know its semantics.

You are of course correct, however it seems a tad unnecessarily verbose IMHO.

The advantage lies in optimization, as it's (more?) common to not want to modify a string literal, but only to output it or a part of it. I don't find it that verbose, but I don't think I've ever used it in code other than such examples. :-P

But wouldn't the expected behavior be to implicitly duplicate the string literal? Or better yet, to never actually allocate memory for the string literal at all, but simply use it to set the initial value of the variable? -- Michiel
Jan 14 2008
parent reply Yigal Chripun <yigal100 gmail.com> writes:
Michiel Helvensteijn wrote:
 Matti Niemenmaa wrote:
 
 How about:

 char[] text = "text".dup;

 .dup creates a mutable copy. There's also .idup, for an immutable copy,
 but I don't know its semantics.

IMHO.

modify a string literal, but only to output it or a part of it. I don't find it that verbose, but I don't think I've ever used it in code other than such examples. :-P

But wouldn't the expected behavior be to implicitly duplicate the string literal? Or better yet, to never actually allocate memory for the string literal at all, but simply use it to set the initial value of the variable?

you're quite right, and on some OSes (Linux) no memory is allocated (on the heap/stack) but rather the value is store in ROM. however, char[] is an array which means you could have done the following: char[] a = "abc"; a[1] = 'd'; which would segfault because you're changing something in ROM! that's why the type of string literals is variant(char)[] or string for short, and the above behavior is disallowed.
Jan 14 2008
parent reply Michiel Helvensteijn <nomail please.com> writes:
Yigal Chripun wrote:

 But wouldn't the expected behavior be to implicitly duplicate the string
 literal? Or better yet, to never actually allocate memory for the string
 literal at all, but simply use it to set the initial value of the
 variable?

you're quite right, and on some OSes (Linux) no memory is allocated (on the heap/stack) but rather the value is store in ROM. however, char[] is an array which means you could have done the following: char[] a = "abc"; a[1] = 'd'; which would segfault because you're changing something in ROM! that's why the type of string literals is variant(char)[] or string for short, and the above behavior is disallowed.

But wouldn't you want that to work exactly as written? char[] str = "abc"; a[1] = 'd'; assert(str == "adc"); "abc" here is just the initial value of the mutable variable str. So it should be allocated in the memory pointed to by str. In other words, an implicit cast should take place, or an implicit duplication should be made. That it doesn't work like that is, in my opinion, a flaw of the language. D seems to continually get more difficult to use. -- Michiel
Jan 14 2008
parent Yigal Chripun <yigal100 gmail.com> writes:
Michiel Helvensteijn wrote:
 Yigal Chripun wrote:
 
 But wouldn't the expected behavior be to implicitly duplicate the string
 literal? Or better yet, to never actually allocate memory for the string
 literal at all, but simply use it to set the initial value of the
 variable?

the heap/stack) but rather the value is store in ROM. however, char[] is an array which means you could have done the following: char[] a = "abc"; a[1] = 'd'; which would segfault because you're changing something in ROM! that's why the type of string literals is variant(char)[] or string for short, and the above behavior is disallowed.

But wouldn't you want that to work exactly as written? char[] str = "abc"; a[1] = 'd'; assert(str == "adc"); "abc" here is just the initial value of the mutable variable str. So it should be allocated in the memory pointed to by str. In other words, an implicit cast should take place, or an implicit duplication should be made. That it doesn't work like that is, in my opinion, a flaw of the language. D seems to continually get more difficult to use.

an important principle of D is COW (copy on write). everywhere in D where you need a copy, you explicitly state that with a dup method or if you need to allocate memory for an object you use: --- auto variable = new Type(params); // for example.. --- an implicit cast is a semantic error: the D spec says that an invariant cannot be cast implicitly to mutable, one of the reasons is because the compiler uses invariant to optimize code, another is the fact the invariant mean "nobody will change that object" and if you cast to mutable undefined behavior follows. meaning that a cast to mutable breaks the meaning of invariant. an implicit duplication is also wrong: just read Steven's explanation in this thread.
Jan 14 2008
prev sibling next sibling parent reply "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Neil Vice" wrote
 I've been experimenting with const et. al. in 2.009 and have encountered 
 some snags which I'll log here. It is quite likely that some of these 
 issues are unrelated to the recent const changes or are things I have 
 misunderstood and I will appreciate any feedback.

 Firstly the following simple initialiser fails to compile:

    char[] text = "text";

This is because a string literal is typed as an invariant(char)[]. const(char)[] text = "text"; or invariant(char)[] text = "text"; or (with phobos) string text = "text"; should all work.
 As far as I can tell there is no way to declare and initialise an array in 
 a single line of code or even copy an array in a single line once 
 declared. The only way I'm aware of to copy an array is as follows:

    char[] text;
    text.length = "text".length;
    text[] = "text";

Yeah, as Matti said, you want "text".dup. There is a good reason for this. A string is an array, which is nothing but a pointer and length. If you allow the line: char[] text = "text"; to compile, then you are free to modify the constant "text"! For example, in D 1.0 this works: char[] text = "text"; text[0] = 'n'; char[] text2 = "text"; assert(text2 == "next"); These kinds of errors are subtle and hard to find. This is why you are not allowed to have a non-const or invariant pointer to invariant data such as string literals. -Steve
Jan 14 2008
parent reply "Saaa" <empty needmail.com> writes:
Will this work as well?int[] def = [ 1, 2, 3 ];def[0]=0;int[] def2 = [ 1, 2, 
3 ];assert(def2[0..2] == [ 0, 2, 3 ]);
 to compile, then you are free to modify the constant "text"!  For example, 
 in D 1.0 this works:

 char[] text = "text";
 text[0] = 'n';
 char[] text2 = "text";
 assert(text2 == "next");

 These kinds of errors are subtle and hard to find.  This is why you are 
 not allowed to have a non-const or invariant pointer to invariant data 
 such as string literals.

 -Steve
 

Jan 14 2008
parent reply "Saaa" <empty needmail.com> writes:
:)> Will this work as well?
int[] def = [ 1, 2, 3 ];
def[0]=0;
int[] def2 = [ 1, 2, 3 ];
assert(def2[0..2] == [ 0, 2, 3 ]);

 to compile, then you are free to modify the constant "text"!  For 
 example, in D 1.0 this works:

 char[] text = "text";
 text[0] = 'n';
 char[] text2 = "text";
 assert(text2 == "next");

 These kinds of errors are subtle and hard to find.  This is why you are 
 not allowed to have a non-const or invariant pointer to invariant data 
 such as string literals.

 -Steve


Jan 14 2008
parent "Saaa" <empty needmail.com> writes:
My intuition tells me that a char[] is an array of chars, just like int[] is 
an array of ints.
If you want something special like a string literal you can use string or 
stringl or something alike.


void function(in out int i){}

 :)> Will this work as well?
 int[] def = [ 1, 2, 3 ];
 def[0]=0;
 int[] def2 = [ 1, 2, 3 ];
 assert(def2[0..2] == [ 0, 2, 3 ]);

 to compile, then you are free to modify the constant "text"!  For 
 example, in D 1.0 this works:

 char[] text = "text";
 text[0] = 'n';
 char[] text2 = "text";
 assert(text2 == "next");

 These kinds of errors are subtle and hard to find.  This is why you are 
 not allowed to have a non-const or invariant pointer to invariant data 
 such as string literals.

 -Steve



Jan 14 2008
prev sibling parent reply Walter Bright <newshound1 digitalmars.com> writes:
Neil Vice wrote:
 Firstly the following simple initialiser fails to compile:
 
     char[] text = "text";
 
 As far as I can tell there is no way to declare and initialise an array in a 
 single line of code

string text = "text";
Jan 14 2008
parent reply Bill Baxter <dnewsgroup billbaxter.com> writes:
Walter Bright wrote:
 Neil Vice wrote:
 Firstly the following simple initialiser fails to compile:

     char[] text = "text";

 As far as I can tell there is no way to declare and initialise an 
 array in a single line of code

string text = "text";

My intuition is that an implicit dup should happen. Why? Well, maybe it's just familiarity with C++? std::string text = "hello"; // makes a copy text[0] = 'b'; // ok because we have a copy --bb
Jan 14 2008
parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
"Bill Baxter" wrote
 Walter Bright wrote:
 Neil Vice wrote:
 Firstly the following simple initialiser fails to compile:

     char[] text = "text";

 As far as I can tell there is no way to declare and initialise an array 
 in a single line of code

string text = "text";

My intuition is that an implicit dup should happen. Why? Well, maybe it's just familiarity with C++? std::string text = "hello"; // makes a copy text[0] = 'b'; // ok because we have a copy --bb

If assigning one string to another made a copy, you lose all the performance benefits of slicing. If I make 20 "copies" of the same string, as long as the string is invariant, there is no reason that I need to keep 20 copies of it in memory. That being said, let's examine the following line: char[] text = "hello"; The compiler could interpret this 2 ways. One is "yes, compiler, I know that "hello" is invariant, I just want a mutable copy of it", The other is "oops, I really meant to get a read-only pointer to that string data, so give me an error". If the compiler interprets the first way, the coder who wants a read-only pointer isn't warned that he now has used inefficient heap memory. If the compiler inteprets the second way, the coder who wants a copy is told he cannot do it that way, he fixes it, and now everyone is happy. I like the way it works now. Err on the side of clarity and efficiency. -Steve
Jan 14 2008