digitalmars.D.learn - mutable constant?
- Namespace (33/33) Jun 25 2013 I want to ask if this code should compile or if it's a bug,
- Simen Kjaeraas (5/38) Jun 25 2013 This is perfectly fine.
- Jonathan M Davis (8/54) Jun 25 2013 I could certainly be missing something here, but I don't understand what
- Namespace (1/1) Jun 25 2013 If you change uint to size_t it works fine AFAIK.
- Jonathan M Davis (12/13) Jun 25 2013 Yes. It's easy enough to fix, but it _is_ arguably a bug in the code, an...
- =?UTF-8?B?QWxpIMOHZWhyZWxp?= (14/22) Jun 25 2013 I have seen similar designs in the past where constructors had
- Namespace (2/25) Jun 26 2013 This is no real code. Just a test example to check. ;)
- monarch_dodra (16/54) Jun 26 2013 It seems safe, however, your example seems to show how to indeed
- Jonathan M Davis (3/21) Jun 26 2013 It doesn't break anything. It just shows the need for pure.
- monarch_dodra (4/6) Jun 26 2013 OOhhhh.... I just got it :(
- anonymous (20/21) Jun 26 2013 Really? In the following simplified code I see mutation of an
- Jonathan M Davis (14/38) Jun 26 2013 It looks to me like your code is fundamentally different from the OP's e...
- anonymous (15/33) Jun 26 2013 I don't see the fundamental difference. In both versions:
- Jonathan M Davis (11/50) Jun 26 2013 You're right. I didn't read over the OP's example carefully enough. The
- Namespace (1/19) Jun 26 2013 That was exactly my question.
I want to ask if this code should compile or if it's a bug, because I circumvent the const system: ---- import std.stdio; struct Point { int x, y; } Point*[] points; struct TplPoint(T) { public: Point _point; T x, y; const uint id; this(T x, T y) { this.x = x; this.y = y; points ~= &this._point; id = points.length - 1; } property inout(Point)* ptr() inout { points[this.id].x = cast(int) this.x; points[this.id].y = cast(int) this.y; return cast(inout Point*) points[this.id]; } } void main() { const TplPoint!float my = TplPoint!float(42, 23); writeln(my._point, "::", &my._point); writeln(*my.ptr, "::", my.ptr); } ---- Or is the fact that it compiles ok and it's "only" unsafe?
Jun 25 2013
On Wed, 26 Jun 2013 00:07:38 +0200, Namespace <rswhite4 googlemail.com> wrote:I want to ask if this code should compile or if it's a bug, because I circumvent the const system: ---- import std.stdio; struct Point { int x, y; } Point*[] points; struct TplPoint(T) { public: Point _point; T x, y; const uint id; this(T x, T y) { this.x = x; this.y = y; points ~= &this._point; id = points.length - 1; } property inout(Point)* ptr() inout { points[this.id].x = cast(int) this.x; points[this.id].y = cast(int) this.y; return cast(inout Point*) points[this.id]; } } void main() { const TplPoint!float my = TplPoint!float(42, 23); writeln(my._point, "::", &my._point); writeln(*my.ptr, "::", my.ptr); } ---- Or is the fact that it compiles ok and it's "only" unsafe?This is perfectly fine. -- Simen
Jun 25 2013
On Wednesday, June 26, 2013 00:07:38 Namespace wrote:I want to ask if this code should compile or if it's a bug, because I circumvent the const system: ---- import std.stdio; struct Point { int x, y; } Point*[] points; struct TplPoint(T) { public: Point _point; T x, y; const uint id; this(T x, T y) { this.x = x; this.y = y; points ~= &this._point; id = points.length - 1; } property inout(Point)* ptr() inout { points[this.id].x = cast(int) this.x; points[this.id].y = cast(int) this.y; return cast(inout Point*) points[this.id]; } } void main() { const TplPoint!float my = TplPoint!float(42, 23); writeln(my._point, "::", &my._point); writeln(*my.ptr, "::", my.ptr); } ---- Or is the fact that it compiles ok and it's "only" unsafe?I could certainly be missing something here, but I don't understand what about the code you're even concerned about. Where in here would you be breaking the type system? I don't see any place in here where you're mutating a const variable or anything like that. The worst thing I see about the code is that it won't compile on 64-bit machines thanks to id = points.length - 1; - Jonathan M Davis
Jun 25 2013
If you change uint to size_t it works fine AFAIK.
Jun 25 2013
On Wednesday, June 26, 2013 00:52:58 Namespace wrote:If you change uint to size_t it works fine AFAIK.Yes. It's easy enough to fix, but it _is_ arguably a bug in the code, and it's the only one that's obvious to me. I don't understand what about the code makes you think that it might be violating the type system. - Jonathan M Davis P.S. Please always quote at least a portion of the post that you're replying to. Clients frequently do not manage to thread posts correctly, and it's not always clear which post a post is replying to if it doesn't quote any of its parent. In this particular case, based on what you said, I expect that you were replying to me, but it's threaded in my client as being a responsed to Simen. Also, not all posters even use a threaded view. So, relying on threading to make it clear which post you're replying to is not a good idea.
Jun 25 2013
With apologies, I have unrelated comments to make. On 06/25/2013 03:07 PM, Namespace wrote:this(T x, T y) { this.x = x; this.y = y; points ~= &this._point;I have seen similar designs in the past where constructors had side-effects such as registering the object in a global state. (Exactly what the last line is doing above.) It has almost always been cause of trouble. It is better to register an object from the outside after constructing it. Sometimes I had attempted to remove seemingly unused objects only to be reminded by a comment that it should not be: // Do not remove! Registers itself in the points array auto p = Point();property inout(Point)* ptr() inout { points[this.id].x = cast(int) this.x; points[this.id].y = cast(int) this.y;That looks questionable as well: ptr() looks like an accessor but it makes changes to a global state. Ali
Jun 25 2013
On Tuesday, 25 June 2013 at 23:39:45 UTC, Ali Çehreli wrote:With apologies, I have unrelated comments to make. On 06/25/2013 03:07 PM, Namespace wrote:This is no real code. Just a test example to check. ;)this(T x, T y) { this.x = x; this.y = y; points ~= &this._point;I have seen similar designs in the past where constructors had side-effects such as registering the object in a global state. (Exactly what the last line is doing above.) It has almost always been cause of trouble. It is better to register an object from the outside after constructing it. Sometimes I had attempted to remove seemingly unused objects only to be reminded by a comment that it should not be: // Do not remove! Registers itself in the points array auto p = Point();property inout(Point)* ptr() inout { points[this.id].x = cast(int) this.x; points[this.id].y = cast(int) this.y;That looks questionable as well: ptr() looks like an accessor but it makes changes to a global state. Ali
Jun 26 2013
On Wednesday, 26 June 2013 at 07:35:08 UTC, Namespace wrote:On Tuesday, 25 June 2013 at 23:39:45 UTC, Ali Çehreli wrote:It seems safe, however, your example seems to show how to indeed break the type system... without a cast (!): property Point* ptr() inout { points[this.id].x = cast(int) this.x; points[this.id].y = cast(int) this.y; return points[this.id]; } void main() { immutable TplPoint!float my = TplPoint!float(42, 23); Point* p = my.ptr; //Oops! mutable point! } Disturbing...With apologies, I have unrelated comments to make. On 06/25/2013 03:07 PM, Namespace wrote:This is no real code. Just a test example to check. ;)this(T x, T y) { this.x = x; this.y = y; points ~= &this._point;I have seen similar designs in the past where constructors had side-effects such as registering the object in a global state. (Exactly what the last line is doing above.) It has almost always been cause of trouble. It is better to register an object from the outside after constructing it. Sometimes I had attempted to remove seemingly unused objects only to be reminded by a comment that it should not be: // Do not remove! Registers itself in the points array auto p = Point();property inout(Point)* ptr() inout { points[this.id].x = cast(int) this.x; points[this.id].y = cast(int) this.y;That looks questionable as well: ptr() looks like an accessor but it makes changes to a global state. Alithis(T x, T y) { this.x = x; this.y = y; points ~= &this._point;I'd careful with this, you can easily end up with pointers to destroyed temporaries...
Jun 26 2013
On Wednesday, June 26, 2013 13:16:16 monarch_dodra wrote:It seems safe, however, your example seems to show how to indeed break the type system... without a cast (!): property Point* ptr() inout { points[this.id].x = cast(int) this.x; points[this.id].y = cast(int) this.y; return points[this.id]; } void main() { immutable TplPoint!float my = TplPoint!float(42, 23); Point* p = my.ptr; //Oops! mutable point! } Disturbing...It doesn't break anything. It just shows the need for pure. - Jonathan M Davis
Jun 26 2013
On Wednesday, 26 June 2013 at 15:48:42 UTC, Jonathan M Davis wrote:It doesn't break anything. It just shows the need for pure. - Jonathan M DavisOOhhhh.... I just got it :( nevermind then...
Jun 26 2013
On Wednesday, 26 June 2013 at 15:48:42 UTC, Jonathan M Davis wrote:It doesn't break anything. It just shows the need for pure.Really? In the following simplified code I see mutation of an immutable variable, which should not be possible, of course. That is breaking the type system, no? What am I missing? import std.stdio; int* point; struct TplPoint { int _point; this(int x) { _point = x; point = &_point; } } void main() { immutable TplPoint my = TplPoint(42); writeln(my._point); // 42 *point = 13; // uh-oh writeln(my._point); // 13 !!! }
Jun 26 2013
On Thursday, June 27, 2013 01:45:22 anonymous wrote:On Wednesday, 26 June 2013 at 15:48:42 UTC, Jonathan M Davis wrote:It looks to me like your code is fundamentally different from the OP's example rather than being a simplification of the original code. In the OP's example, the variable being mutated is a module-level variable, so the immutability of the object is irrelevant when its member function is called (since it's not the object itself which is being mutated). It also has nothing to do with construction. Your example, on the other hand, is showing a bug with regards to constructing immutable objects in that the object doesn't actually become immutable until it's fully constructed, and the compiler isn't catching something which then violates the impending immutability. I _think_ that that bug has already been reported, but I can't find it at the moment. But regardless, your example is quite different from the OP's example. - Jonathan M DavisIt doesn't break anything. It just shows the need for pure.Really? In the following simplified code I see mutation of an immutable variable, which should not be possible, of course. That is breaking the type system, no? What am I missing? import std.stdio; int* point; struct TplPoint { int _point; this(int x) { _point = x; point = &_point; } } void main() { immutable TplPoint my = TplPoint(42); writeln(my._point); // 42 *point = 13; // uh-oh writeln(my._point); // 13 !!! }
Jun 26 2013
On Thursday, 27 June 2013 at 00:53:48 UTC, Jonathan M Davis wrote:It looks to me like your code is fundamentally different from the OP's example rather than being a simplification of the original code. In the OP's example, the variable being mutated is a module-level variable, so the immutability of the object is irrelevant when its member function is called (since it's not the object itself which is being mutated). It also has nothing to do with construction. Your example, on the other hand, is showing a bug with regards to constructing immutable objects in that the object doesn't actually become immutable until it's fully constructed, and the compiler isn't catching something which then violates the impending immutability.I don't see the fundamental difference. In both versions: - An immutable struct instance is constructed. mine: immutable TplPoint my = TplPoint(42); OP: const TplPoint!float my = TplPoint!float(42, 23); Note that this does not set my._point to Point(42, 23). - The constructor stores a mutable pointer to its contents in module scope. mine: point = &_point; OP: points ~= &this._point; - Via that mutable pointer the data is altered. mine: *point = 13; OP: a bit more convoluted, TplPoint.ptr does the nasty work, but it could just as well be in main: points[someid].x = somevalue; => Immutability broken.
Jun 26 2013
On Thursday, June 27, 2013 03:31:01 anonymous wrote:On Thursday, 27 June 2013 at 00:53:48 UTC, Jonathan M Davis wrote:You're right. I didn't read over the OP's example carefully enough. The mutation is being done to a module-level variable in an inout function, which is completely legit. I thought that what the OP thought was wrong was mutating a module-level variable in a non-mutable function (and that's perfectly fine as long as it's not pure). What I missed (and you didn't) was the fact that that module-level variable was pointing to the contents of the object which was const. And that mutable pointer _is_ being obtained via the constructor just like in your example. And that is most definitely a compiler bug - the same one that your example shows. - Jonathan M DavisIt looks to me like your code is fundamentally different from the OP's example rather than being a simplification of the original code. In the OP's example, the variable being mutated is a module-level variable, so the immutability of the object is irrelevant when its member function is called (since it's not the object itself which is being mutated). It also has nothing to do with construction. Your example, on the other hand, is showing a bug with regards to constructing immutable objects in that the object doesn't actually become immutable until it's fully constructed, and the compiler isn't catching something which then violates the impending immutability.I don't see the fundamental difference. In both versions: - An immutable struct instance is constructed. mine: immutable TplPoint my = TplPoint(42); OP: const TplPoint!float my = TplPoint!float(42, 23); Note that this does not set my._point to Point(42, 23). - The constructor stores a mutable pointer to its contents in module scope. mine: point = &_point; OP: points ~= &this._point; - Via that mutable pointer the data is altered. mine: *point = 13; OP: a bit more convoluted, TplPoint.ptr does the nasty work, but it could just as well be in main: points[someid].x = somevalue; => Immutability broken.
Jun 26 2013
You're right. I didn't read over the OP's example carefully enough. The mutation is being done to a module-level variable in an inout function, which is completely legit. I thought that what the OP thought was wrong was mutating a module-level variable in a non-mutable function (and that's perfectly fine as long as it's not pure). What I missed (and you didn't) was the fact that that module-level variable was pointing to the contents of the object which was const. And that mutable pointer _is_ being obtained via the constructor just like in your example. And that is most definitely a compiler bug - the same one that your example shows. - Jonathan M DavisThat was exactly my question.
Jun 26 2013