digitalmars.D - Workarounds for Lack of Mutable Keyword
- Craig Black (27/44) Apr 02 2008 Part of the uneasiness about D's const is that it is much more strict th...
- Craig Black (22/22) Apr 02 2008 I did it and it was very easy. I didn't know you could cast away const ...
- Robert Fraser (2/29) Apr 02 2008 ight, but be sure to synchronize for multi threaded access.
- Craig Black (5/34) Apr 02 2008 In the case that I'm thinking of using this for, I know multiple threads...
- Janice Caron (5/12) Apr 03 2008 Yikes! I think I must be going mad. That's too many typos for one day!
- Janice Caron (8/9) Apr 03 2008 One problem with this function is demonstrated by
- Craig Black (5/14) Apr 03 2008 If that compiles, I think it may be a bug. Invariant types shouldn't be...
- Janice Caron (4/6) Apr 03 2008 Yes they should.
- Craig Black (5/11) Apr 03 2008 Hmmm. Maybe you are right. I was just thinking that since invariant is...
- Janice Caron (5/7) Apr 03 2008 There is, of course, a /huge/ problem with promising not to modify
- Craig Black (24/24) Apr 03 2008 The previous solution that I proposed only works because of a bug in DMD...
- Craig Black (21/21) Apr 03 2008 The second solution that I posted didn't work if the second parameter wa...
Part of the uneasiness about D's const is that it is much more strict than C++'s, and so has the perception of being more difficult to work with. For example, D lacks C++ mutable keyword, so that there is no way to specify a mutable field. Janice suggested a solution to this that would probably work for most purposes:There are trivial workarounds. Instead of class C { int x; mutable int y; } void f(const(C) c); just do this: class C { int x; } class D { int y; } void f(const(C) c, D d);Although this may be good for most, this doesn't work for me, because to use this approach, I would have to make an extra heap allocation, and use an extra parameter. An extra parameter may not seem like a big deal to some. However, a compiler can optimize small recursive functions with small numbers of parameters using registers, so there could be a significant performance penalty for adding a single parameter to a recursive function. I have a more hackish solution that won't cost me any performance. I'm pretty sure it will work, but I haven't quite fleshed out the details. Maybe some of you D template gurus could help me with this one. My idea is to have a ConstAssign template that would subvert const somehow and assign a value to const data. Here's how it would work to replace a mutable field: class C { int x; int y; void setY(int ny) { ConstAssign(y, ny); } } Since D has pointers and unions, there's probably some ugly hackish trick we can employ to subvert const. The idea is to put this ugly hack in a template function. This would provide a not-so-messy workaround fo the lack of mutable that doesn't cost anyperformance or memory. -Craig
Apr 02 2008
I did it and it was very easy. I didn't know you could cast away const so easily in D. This works for const, but not invariant. Since we have the ability to subvert const so easily, there should be no problem living without mutable fields. import std.stdio; void ConstAssign(T)(ref const T x, T y) { *cast(T*)&x = y; } class A { public: int x; int y; void setY(int ny) const { ConstAssign(y, ny); } } void foo(const A a) { a.setY(2); } int main(char[][] args) { A a = new A; a.y = 1; foo(a); writefln(a.y); return 0; }
Apr 02 2008
Craig Black wrote:I did it and it was very easy. I didn't know you could cast away const so easily in D. This works for const, but not invariant. Since we have the ability to subvert const so easily, there should be no problem living without mutable fields. import std.stdio; void ConstAssign(T)(ref const T x, T y) { *cast(T*)&x = y; } class A { public: int x; int y; void setY(int ny) const { ConstAssign(y, ny); } } void foo(const A a) { a.setY(2); } int main(char[][] args) { A a = new A; a.y = 1; foo(a); writefln(a.y); return 0; }ight, but be sure to synchronize for multi threaded access.
Apr 02 2008
"Robert Fraser" <fraserofthenight gmail.com> wrote in message news:ft1r7d$1q0b$1 digitalmars.com...Craig Black wrote:In the case that I'm thinking of using this for, I know multiple threads won't be contending for the "mutable" fields. -CraigI did it and it was very easy. I didn't know you could cast away const so easily in D. This works for const, but not invariant. Since we have the ability to subvert const so easily, there should be no problem living without mutable fields. import std.stdio; void ConstAssign(T)(ref const T x, T y) { *cast(T*)&x = y; } class A { public: int x; int y; void setY(int ny) const { ConstAssign(y, ny); } } void foo(const A a) { a.setY(2); } int main(char[][] args) { A a = new A; a.y = 1; foo(a); writefln(a.y); return 0; }ight, but be sure to synchronize for multi threaded access.
Apr 02 2008
On 03/04/2008, Janice Caron <caron800 googlemail.com> wrote:One problem with this function is demonstrated by string s = "hello world"; ConstAssign(h[0],'j');Yikes! I think I must be going mad. That's too many typos for one day! Need more coffee. The h should have been an s. string s = "hello world"; ConstAssign(s[0],'j');Oops - it assigns invariants too! Even if you can be sure there are no other threads vying for access to s, still, those chars might be in a hardware-locked ROM segment. There's a reason why casting away const is not defined.
Apr 03 2008
On 03/04/2008, Craig Black <craigblack2 cox.net> wrote:void ConstAssign(T)(ref const T x, T y) { *cast(T*)&x = y; }One problem with this function is demonstrated by string s = "hello world"; ConstAssign(h[0],'j'); Oops - it assigns invariants too! Even if you can be sure there are no other threads vying for access to s, still, those chars might be in a hardware-locked ROM segment. There's a reason why casting away const is not defined.
Apr 03 2008
"Janice Caron" <caron800 googlemail.com> wrote in message news:mailman.301.1207206839.2351.digitalmars-d puremagic.com...On 03/04/2008, Craig Black <craigblack2 cox.net> wrote:If that compiles, I think it may be a bug. Invariant types shouldn't be implicitly convertible to const. -Craigvoid ConstAssign(T)(ref const T x, T y) { *cast(T*)&x = y; }One problem with this function is demonstrated by string s = "hello world"; ConstAssign(h[0],'j'); Oops - it assigns invariants too! Even if you can be sure there are no other threads vying for access to s, still, those chars might be in a hardware-locked ROM segment. There's a reason why casting away const is not defined.
Apr 03 2008
On 03/04/2008, Craig Black <craigblack2 cox.net> wrote:If that compiles, I think it may be a bug. Invariant types shouldn't be implicitly convertible to const.Yes they should. const means "I promise not to modify this". There is absolutely no problem with promising not to modify something which is invariant.
Apr 03 2008
"Janice Caron" <caron800 googlemail.com> wrote in message news:mailman.304.1207217058.2351.digitalmars-d puremagic.com...On 03/04/2008, Craig Black <craigblack2 cox.net> wrote:Hmmm. Maybe you are right. I was just thinking that since invariant is a stronger guarantee than const, it shouldn't be implicitly convertible. But I suppose I agree with you after giving it a little more though.If that compiles, I think it may be a bug. Invariant types shouldn't be implicitly convertible to const.Yes they should. const means "I promise not to modify this". There is absolutely no problem with promising not to modify something which is invariant.
Apr 03 2008
On 03/04/2008, Janice Caron <caron800 googlemail.com> wrote:const means "I promise not to modify this". There is absolutely no problem with promising not to modify something which is invariant.There is, of course, a /huge/ problem with promising not to modify something while keeping your fingers crossed behind your back. Promising not to modify something (which is what accepting a const parameter means), and then modifying it anyway, is called lying.
Apr 03 2008
The previous solution that I proposed only works because of a bug in DMD. See the "const/invariant bug" post for more info on the bug. But I found another solution. Const doesn't seem to cooperate well with templates, so void ConstAssign(T)(ref const T a, T b) { *cast(T*)cast(int)(&a) = b; } doesn't work but void ConstAssign(T, S)(ref T a, S b) { *cast(S*)cast(int)(&a) = b; } does work, as long as the second parameter not const. Here's the full example: import std.stdio; void ConstAssign(T, S)(ref T a, S b) { *cast(S*)cast(int)(&a) = b; } class A { public: int x = 0; const void setX(int nx) { ConstAssign(x, nx); } } void foo(const A a) { a.setX(1); } int main(char[][] args) { A a = new A; foo(a); writefln(a.x); return 0; }
Apr 03 2008
The second solution that I posted didn't work if the second parameter was const. This solution will. It uses a ForceCast template. BTW, this template will work for more than just const. Since D doesn't allow returning a reference, we must pass a pointer to ForceCast and dereference the result. import std.stdio; T ForceCast(T, S)(S a) { return *cast(T*)cast(int)(&a); } class A { public: int x = 0; const void setX(const int nx) { *ForceCast!(int*)(&x) = nx; } } void foo(const A a) { a.setX(1); } int main(char[][] args) { A a = new A; foo(a); writefln(a.x); return 0; }
Apr 03 2008