www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - const(X) member of Y

reply "Dan" <dbdavidson yahoo.com> writes:
Start with:

struct X {
   char c[];
}

Assume you want value semantics - postblit provides this 
capability. It appears with 2.061 'this(this) const' is now 
supported. Previously, only 'this(this)' was recognized (i.e. 
called) when expected. To get value semantics a developer must 
choose between these two and for this case the goal is to ensure 
'c = c.dup;' so value semantics are preserved.

Case 1: Go with 'this(this) { c = c.dup; }'. This works just fine 
except for the case where you want a const(X) as a member of some 
other class. For example: struct Y { const(X) x; }.

Case 2: Go with 'this(this) const { c = c.dup; }'. This is not 
possible because you can not change 'c' since the function is 
const. Maxim Fomin pointed out a workaround.

struct X {
   char c[];
   void _postblit_() { c = c.dup; }
   this(this) const {
     void delegate() dg = &_postblit_;
     dg();
   }
}

This workaround succeeds, but, based on this thread 
(http://forum.dlang.org/thread/ywispsasaylqscyuayae forum.dlang.org) 
I don't know if it is a bug or not. If it is not a bug, I would 
doubt it is the prescribed approach for doing this?

So, the question is:
What is the best *current* way to code X to achieve struct Y { 
const(X) x; } and what is the long run solution to this(this)?

Thanks,
Dan
Feb 06 2013
next sibling parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Wednesday, 6 February 2013 at 18:15:53 UTC, Dan wrote:
 This workaround succeeds, but, based on this thread 
 (http://forum.dlang.org/thread/ywispsasaylqscyuayae forum.dlang.org) 
 I don't know if it is a bug or not. If it is not a bug, I would 
 doubt it is the prescribed approach for doing this?

 Thanks,
 Dan
Huh, ability to call non-const postblit from const __postblit is actually a general problem of a major type system breakage: import std.stdio; struct S { int i; void bar() { ++i; } void foo() immutable { //bar(); //error //(&bar)(); //works, lang hole void delegate() dg1 = &bar; //works, lang hole //dg1(); // void delegate() dg2; //dg.ptr = this; //error dg2.ptr = cast(void*)&this; //dg.funcptr = &S.bar; //error dg2.funcptr = cast(void function())&S.bar; dg2(); //works due to cast } } void main() { immutable S s; writeln(s.i); s.foo(); writeln(s.i); } The fact that bar() does not work and (&bar)() works is terrific. Also, if dg1 call is uncommented, dmd wrongly generates instruction which leads to segfault.
Feb 06 2013
parent reply "Dan" <dbdavidson yahoo.com> writes:
On Wednesday, 6 February 2013 at 20:30:40 UTC, Maxim Fomin wrote:
 The fact that bar() does not work and (&bar)() works is 
 terrific.
Sorry - but I don't understand what is terrific? Is this sarcasm? struct X { this(this) { c = c.dup; } char c[]; } (1) What is special about 'struct Y { const(X) x; }' versus 'void goo(const(X) x) {}'? Both require a copy of X, but for some reason the 'struct Y { const(X) x; }' requires the postblit to have signature 'this(this) const {...}'. Why is this? (2) Do structs have, or is it planned that structs will have, true copy constructors (as opposed to postblits)? (3) I know there have been threads on postblits and const types... Here is one: http://forum.dlang.org/thread/CAFDvkcvvL8GxHQB=Rw9pTm-uxOKzNGVQNDv9w5Os3SkQCc=DLQ mail.gmail.com and from it: > Qualified postblits make no sense, given that if > the object is const or immutable, you _can't_ > alter it without violating the type system. But > without them, you can't copy const or immutable > objects which require a postblit. Walter and > Andrei have stated in the past that they had a > plan for solving this, but AFAIK, they've never > actually revealed what it is. My best guess would > be to introduce copy constructors for const and > immutable objects, but I don't know. Regardless, > this is a situation that definitely needs to be > clarified and properly sorted out. > > - Jonathan M Davis Can someone summarize where this stands? Is the general thinking that everything is fine now?
Feb 06 2013
parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Wednesday, 6 February 2013 at 21:47:35 UTC, Dan wrote:
 On Wednesday, 6 February 2013 at 20:30:40 UTC, Maxim Fomin 
 wrote:
 The fact that bar() does not work and (&bar)() works is 
 terrific.
Sorry - but I don't understand what is terrific? Is this sarcasm?
This means it is a huge hole in a type system. Calling non-immutable method from immutable one is not supported, yet is possible not through explicit delegate and even not through casting, but through implicit method call which on low level happens even without delegate creation. Or, in another way, why block bar() if (&bar)() works?
 struct X {
   this(this) { c = c.dup; }
   char c[];
 }

 (1) What is special about 'struct Y { const(X) x; }' versus 
 'void goo(const(X) x) {}'?
     Both require a copy of X, but for some reason the 'struct Y 
 { const(X) x; }' requires the postblit to have signature 
 'this(this) const {...}'.
     Why is this?
Current implementation generates __fieldPostBlit method for Y struct which calls X postblit. Since Y struct has a const X and X.__postblit is not marked as const method, there is conflict between constness and postblits. goo function does no have such problem
 (2) Do structs have, or is it planned that structs will have, 
 true copy constructors (as opposed to postblits)?
Who knows, but current implementation has implicit copy constructor __cpctor. It is supposed to be hidden from users but is actually accessible. I don't know why Walter has chosen such way of copy construction.
Feb 06 2013
parent FG <home fgda.pl> writes:
On 2013-02-06 23:14, Maxim Fomin wrote:
 On Wednesday, 6 February 2013 at 21:47:35 UTC, Dan wrote:
 On Wednesday, 6 February 2013 at 20:30:40 UTC, Maxim Fomin wrote:
 The fact that bar() does not work and (&bar)() works is terrific.
Sorry - but I don't understand what is terrific? Is this sarcasm?
This means it is a huge hole in a type system.
Ah, so you meant: terrifying or horrific. :)
Feb 07 2013
prev sibling next sibling parent reply "Era Scarecrow" <rtcvb32 yahoo.com> writes:
On Wednesday, 6 February 2013 at 18:15:53 UTC, Dan wrote:
 Assume you want value semantics - postblit provides this 
 capability. It appears with 2.061 'this(this) const' is now 
 supported. Previously, only 'this(this)' was recognized (I.e. 
 called) when expected. To get value semantics a developer must 
 choose between these two and for this case the goal is to 
 ensure 'c = c.dup;' so value semantics are preserved.
Hmmm... in my mind 'this(this) const' would both referring prior to postblit, so mainly to pointers and arrays (not POD post-copy). That would only be to ensure the original data doesn't change in some unexpected way. In that way also postblit acts as an initializer so 'const' doesn't come into play until the field changes for the first time. I considered that perhaps the writing was wrong and it would be 'this(const this)', however since this is a variable it's referring to the hidden first field who's type is automatically deduced. This means it can't be that way otherwise all the data is const and thereby the definition is disallowed (pointless), unless you'd wanted to only change something from the original array... which seems off. So an example to consider: struct S { int[] x; const(int)[] y; const z; this(this) { x[0] = 1; //edits previous array y[0] = 1; //y's contents are const this.x = this.x.dup; //new array for x & y this.y = this.y.dup; x[0] = 2; //after new array // y[0] = 2; //y's contents are const z = 100; //allowed one change } this(this) const { x[0] = 1; //Error: Previous 'this' is const and cannot change y[0] = 1; //y's contents are const this.x = this.x.dup; //new array for x & y this.y = this.y.dup; x[0] = 2; //allowed // y[0] = 2; //y's contents are const z = 100; //allowed one change } }
Feb 06 2013
parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Wednesday, 6 February 2013 at 21:28:33 UTC, Era Scarecrow 
wrote:
  I considered that perhaps the writing was wrong and it would 
 be 'this(const this)', however since this is a variable it's 
 referring to the hidden first field who's type is automatically 
 deduced. This means it can't be that way otherwise all the data 
 is const and thereby the definition is disallowed (pointless), 
 unless you'd wanted to only change something from the original 
 array... which seems off.
but this(const this) is supported and means to be a constructor
Feb 06 2013
parent reply "Dan" <dbdavidson yahoo.com> writes:
On Wednesday, 6 February 2013 at 21:52:01 UTC, Maxim Fomin wrote:
 but this(const this) is supported and means to be a constructor
Maxim, please review this thread: http://forum.dlang.org/post/lrnxsvtwqwqtydggjphs forum.dlang.org In this example the 'this(const this)' is not called, just as pointed out in the previous thread. That is, things have not changed. So, in following, "Hi" is not printed. Have you had any luck with 'this(const this)'? Thanks Dan import std.stdio; struct X { char[] c; this(char[] c_) { c = c_.dup; } this(const this) { writeln("Hi"); } } void main() { auto const x = X(['a']); const(X) x2 = x; writeln("Done"); }
Feb 06 2013
parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Wednesday, 6 February 2013 at 22:00:52 UTC, Dan wrote:
 On Wednesday, 6 February 2013 at 21:52:01 UTC, Maxim Fomin 
 wrote:
 but this(const this) is supported and means to be a constructor
Maxim, please review this thread: http://forum.dlang.org/post/lrnxsvtwqwqtydggjphs forum.dlang.org In this example the 'this(const this)' is not called, just as pointed out in the previous thread. That is, things have not changed. So, in following, "Hi" is not printed. Have you had any luck with 'this(const this)'? Thanks Dan import std.stdio; struct X { char[] c; this(char[] c_) { c = c_.dup; } this(const this) { writeln("Hi"); } } void main() { auto const x = X(['a']); const(X) x2 = x; writeln("Done"); }
Yes, because this(const this) is not a copy constructor or a postblit, it is a simple constructor. X(x) will print "Hi". This is unintuitive, but parameter names can be omitted and this leads to confusion between struct postblit and struct constructor which has first argument of its own type.
Feb 06 2013
parent reply "Dan" <dbdavidson yahoo.com> writes:
On Wednesday, 6 February 2013 at 22:29:49 UTC, Maxim Fomin wrote:
 Yes, because this(const this) is not a copy constructor or a 
 postblit, it is a simple constructor. X(x) will print "Hi". 
 This is unintuitive, but parameter names can be omitted and 
 this leads to confusion between struct postblit and struct 
 constructor which has first argument of its own type.
Thanks, I did not know that. I think I see it, now: auto x3 = X(x); auto x4 = x; The first prints "Hi", the second not. So it is not a "copy constructor", but rather a "simple constructor" that would allow for performing a copy. I assume the syntax "auto x3 = X(x)" is the only way this would be called, and by-value parameter passing would never invoke 'this(const this)'. This begs the question: Which of these do you choose and for what reasons: - this(this){} - this(this)const{} - this(const this){} Also, has any of this detailed information made it into the language spec? Thanks Dan
Feb 06 2013
parent reply "Maxim Fomin" <maxim maxim-fomin.ru> writes:
On Wednesday, 6 February 2013 at 22:54:40 UTC, Dan wrote:
 This begs the question:

 Which of these do you choose and for what reasons:
 - this(this){}
This is natural way to define struct postblit. This fully complies with current D spec. This should be used until postblit constness problem is faced.
 - this(this)const{}
This is not normal way to define struct postblit but which happens to work. Spec is silent about this. If you encounter "postblit is not callable using const" message, you can workaround the problem by making postblit const and forwarding call to some other non-const method which actually does postblitting. This is type system breakage which can be useful until option 1 is fixed. Or you may use other workarounds.
 - this(const this){}
This is a tricky and unintuitive way to define simple struct constructor which may be confused with postblit. It actually means struct S { this(const S s){...} } and can be written in this way to avoid confusion.
 Also, has any of this detailed information made it into the 
 language spec?
particular example of omitting parameter names like void foo(int) { }. D unlike C supports omitting parameter names not only in function declaration, but in function definition too.
 Thanks
 Dan
Feb 06 2013
parent reply "Dan" <dbdavidson yahoo.com> writes:
On Thursday, 7 February 2013 at 05:30:27 UTC, Maxim Fomin wrote:
 On Wednesday, 6 February 2013 at 22:54:40 UTC, Dan wrote:
 This begs the question:

 Which of these do you choose and for what reasons:
 - this(this){}
This is natural way to define struct postblit. This fully complies with current D spec. This should be used until postblit constness problem is faced.
 - this(this)const{}
This is not normal way to define struct postblit but which happens to work. Spec is silent about this. If you encounter "postblit is not callable using const" message, you can workaround the problem by making postblit const and forwarding call to some other non-const method which actually does postblitting. This is type system breakage which can be useful until option 1 is fixed. Or you may use other workarounds.
 - this(const this){}
This is a tricky and unintuitive way to define simple struct constructor which may be confused with postblit. It actually means struct S { this(const S s){...} } and can be written in this way to avoid confusion.
 Also, has any of this detailed information made it into the 
 language spec?
a particular example of omitting parameter names like void foo(int) { }. D unlike C supports omitting parameter names not only in function declaration, but in function definition too.
Immensely helpful answer, thanks! will never be a requirement to have 'this(this) const' and you to set members in 'this(this) const'. required. I know of 2 cases: struct S { this(this) { c=c.dup; } char[] c; } struct X { const(S) s; } So my guess is something about the "lowering" or autogenerated postblit of X requires the postblit to be const. I think this is overkill and maybe the 'const'ness of a member of S should be relaxed in the creation of it. Another is the following fails to compile: import std.stdio; struct S { this(this) { c=c.dup; } char[] c; } void main() { const(S)[int] s1; const(S)[int] s2; writeln(s1==s2); } Something about opEquals is requiring the signature on S's this(this) to be 'this(this) const'. This really highlights two issues: First, the implementation of opEquals for a hash should *not* need to make copies of the values. There is probably an iteration in the implementation that should be changed to ref. The second is you should probably be allowed to construct a const(S) from another const(S) without requiring signature 'this(this) const' in all cases. This sort of issue comes up in the weirdest of situations. http://dpaste.dzfl.pl/f8ddc666 This code compiles. But interestingly making the 'static if(1)' causes it to fail to compile. Somehow, adding a 'opEquals const' to T triggers the generation of a opEquals in X that requires S[int] to have 'opEquals const', which in turn requires S to have 'this(this) const'. Why so much focus on new features like properties when the fundamentals of const for structs are not hammered out sufficiently? Thanks Dan
Feb 07 2013
parent reply Timon Gehr <timon.gehr gmx.ch> writes:
On 02/07/2013 02:49 PM, Dan wrote:
 ...

 Why so much focus on new features like properties
I guess because it is not a new feature, but another part where some things have not been hammered out sufficiently. (Also, it tends to generate larger threads. :o) )
 when the fundamentals of const for structs are not hammered out sufficiently?
 ...
const/immutable type checking currently is unsound during object construction in general.
Feb 07 2013
parent reply "Dan" <dbdavidson yahoo.com> writes:
On Thursday, 7 February 2013 at 15:03:20 UTC, Timon Gehr wrote:
 const/immutable type checking currently is unsound during 
 object construction in general.
properties => candy const/immutable soundness => meat & potatoes Evidence of tremedous energy and intellect here: http://forum.dlang.org/thread/ririagrqecshjljcdubd forum.dlang.org#post-ririagrqecshjljcdubd:40forum.dlang.org Why can't we harness that energy to fix more of the meat & potatoes problems that exist and have existed for a while? It seems the focus of initiatives are skewed toward 'wow' features and truly neat enhancements to D. But the original claims and promise of D on a certain level is within reach if the focus is on the more mundane. Thanks Dan
Feb 07 2013
parent reply "deadalnix" <deadalnix gmail.com> writes:
On Thursday, 7 February 2013 at 15:28:39 UTC, Dan wrote:
 On Thursday, 7 February 2013 at 15:03:20 UTC, Timon Gehr wrote:
 const/immutable type checking currently is unsound during 
 object construction in general.
properties => candy const/immutable soundness => meat & potatoes Evidence of tremedous energy and intellect here: http://forum.dlang.org/thread/ririagrqecshjljcdubd forum.dlang.org#post-ririagrqecshjljcdubd:40forum.dlang.org Why can't we harness that energy to fix more of the meat & potatoes problems that exist and have existed for a while? It seems the focus of initiatives are skewed toward 'wow' features and truly neat enhancements to D. But the original claims and promise of D on a certain level is within reach if the focus is on the more mundane.
Well because it is known what have to be done for a while. It is "just" not implemented.
Feb 07 2013
parent "Dan" <dbdavidson yahoo.com> writes:
On Friday, 8 February 2013 at 04:41:42 UTC, deadalnix wrote:
 Well because it is known what have to be done for a while. It 
 is "just" not implemented.
If it is known then please tell the plan. I see many conflicting points and I don't see the clean solution that maybe you do. Just from this thread we have that 'this(this) const' is kind of a mistake and not defined in the docs. Yet in certain cases that is a required signature. Repeating this from: http://forum.dlang.org/thread/CAFDvkcvvL8GxHQB=Rw9pTm-uxOKzNGVQNDv9w5Os3SkQCc=DLQ mail.gmail.com > Qualified postblits make no sense, given that if > the object is const or immutable, you _can't_ > alter it without violating the type system. But > without them, you can't copy const or immutable > objects which require a postblit. Walter and > Andrei have stated in the past that they had a > plan for solving this, but AFAIK, they've never > actually revealed what it is. My best guess would > be to introduce copy constructors for const and > immutable objects, but I don't know. Regardless, > this is a situation that definitely needs to be > clarified and properly sorted out. > > - Jonathan M Davis Ultimately, though it would be great to know not just what can't work or what might work but what the long term solution is. Is there a solution ironed out between Andrei and Walter and if so what is it, roughly? What is the priority of this issue, where the issue is not necessarily the implementation but letting us know where it is going so we can not do something silly? Realistically, when const gets too much in the way one recourse is to drop it. On the other hand if a solution is in the works and delegate trickery is just a stop gap it's less worrisome. Thanks Dan
Feb 08 2013
prev sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Wednesday, 6 February 2013 at 18:15:53 UTC, Dan wrote:
 Start with:

 struct X {
   char c[];
 }

 Assume you want value semantics - postblit provides this 
 capability. It appears with 2.061 'this(this) const' is now 
 supported. Previously, only 'this(this)' was recognized (i.e. 
 called) when expected. To get value semantics a developer must 
 choose between these two and for this case the goal is to 
 ensure 'c = c.dup;' so value semantics are preserved.

 Case 1: Go with 'this(this) { c = c.dup; }'. This works just 
 fine except for the case where you want a const(X) as a member 
 of some other class. For example: struct Y { const(X) x; }.

 Case 2: Go with 'this(this) const { c = c.dup; }'. This is not 
 possible because you can not change 'c' since the function is 
 const. Maxim Fomin pointed out a workaround.
Both are bugs, as const constructor are supposed to be able to set const variable once. See TDPL for reference on that.
 struct X {
   char c[];
   void _postblit_() { c = c.dup; }
   this(this) const {
     void delegate() dg = &_postblit_;
     dg();
   }
 }
This is also a bug as transitivity of const isn't respected. Welcome in weirdland !
Feb 06 2013