digitalmars.D - C++ mutable in D
- Michael Galuza (4/4) Jul 30 2021 Is there any analogue of C++ `mutable` in D? As far as I
- Tejas (5/10) Jul 30 2021 Nope. No logical const, head const, or tail const in D; just pure
- IGotD- (7/11) Jul 30 2021 Thank you D for not having "mutable" as in C++. I don't see
- Tejas (3/16) Jul 30 2021 Well, Jonathan Davies disagrees:
- Gollan (7/11) Jul 31 2021 I'm really surprised you think it doesn't make much sense. What
- IGotD- (14/20) Jul 31 2021 They way I look at it if you don't want the code alter the
- Jack Applegame (5/7) Jul 31 2021 No.
- Paulo Pinto (6/14) Aug 01 2021 Not in C++, when the const object modification is taking place
- Paul Backus (13/21) Aug 01 2021 In D, casting away const from a pointer and mutating the
- Dukc (18/23) Aug 01 2021 Not any good analogue. There are some situation-dependant
- Michael Galuza (16/21) Aug 01 2021 Let's slightly reformulate my question. Can we write in D smth
- Paul Backus (2/6) Aug 01 2021 No. Mutating an object typed as `const` is always UB in D.
- Steven Schveighoffer (20/44) Aug 02 2021 Yes. You need to use global space to do it.
- Alexandru Ermicioi (8/13) Aug 01 2021 Not possible as other people mentioned. In this case the best
- Dukc (32/33) Aug 03 2021 We are saying that there is no direct analogue for `mutable` in
- Paul Backus (3/7) Aug 03 2021 It's a known bug, since 2008:
- Dukc (15/23) Aug 03 2021 I don't think it's that one. I'm not abusing the delegates
- Paul Backus (23/42) Aug 03 2021 This example is abusing the fact that an `immutable` delegate can
Is there any analogue of C++ `mutable` in D? As far as I understand, this is impossible due to transitive constness, but maybe some variation of `Rebindable` will be useful? P.S. Of course I mean 'fair' mutable, not qualifier-away `cast()`.
Jul 30 2021
On Friday, 30 July 2021 at 19:17:55 UTC, Michael Galuza wrote:Is there any analogue of C++ `mutable` in D? As far as I understand, this is impossible due to transitive constness, but maybe some variation of `Rebindable` will be useful? P.S. Of course I mean 'fair' mutable, not qualifier-away `cast()`.Nope. No logical const, head const, or tail const in D; just pure transitive const. Also remember that casting away const is undefined behaviour in D (unlike being defined and supported in C++).
Jul 30 2021
On Friday, 30 July 2021 at 20:09:19 UTC, Tejas wrote:Nope. No logical const, head const, or tail const in D; just pure transitive const. Also remember that casting away const is undefined behaviour in D (unlike being defined and supported in C++).Thank you D for not having "mutable" as in C++. I don't see mutable often in C++ and is one of those corners in C++ that I don't think make much sense. const is const, easy. In C++ const might not be ROMable. In D immutable is ROMable and transistive const helps this (unless there is some escape hatch I don't know about).
Jul 30 2021
On Friday, 30 July 2021 at 21:53:01 UTC, IGotD- wrote:On Friday, 30 July 2021 at 20:09:19 UTC, Tejas wrote:Well, Jonathan Davies disagrees: http://jmdavisprog.com/articles/why-const-sucks.htmlNope. No logical const, head const, or tail const in D; just pure transitive const. Also remember that casting away const is undefined behaviour in D (unlike being defined and supported in C++).Thank you D for not having "mutable" as in C++. I don't see mutable often in C++ and is one of those corners in C++ that I don't think make much sense. const is const, easy. In C++ const might not be ROMable. In D immutable is ROMable and transistive const helps this (unless there is some escape hatch I don't know about).
Jul 30 2021
On Friday, 30 July 2021 at 21:53:01 UTC, IGotD- wrote:On Friday, 30 July 2021 at 20:09:19 UTC, Tejas wrote: Thank you D for not having "mutable" as in C++. I don't see mutable often in C++ and is one of those corners in C++ that I don't think make much sense.I'm really surprised you think it doesn't make much sense. What about: - Locking a mutex to avoid data races when returning a class member from a const getter function - Lazy resource allocation (caching) on first call of a const member function
Jul 31 2021
On Saturday, 31 July 2021 at 12:27:28 UTC, Gollan wrote:I'm really surprised you think it doesn't make much sense. What about: - Locking a mutex to avoid data races when returning a class member from a const getter function - Lazy resource allocation (caching) on first call of a const member functionThey way I look at it if you don't want the code alter the *memory location* then it must be immutable/const. The way you describe it, it is based on what the API expect to do with the object even if there might be internal memory writes, like the semaphore example. With structures where members are expanded into the structure, then they share the memory location and partial const in my opinion should be prohibited. Now this can be different with classes as members are allocated independently but I'm sure about the practical benefit. It boils down to what we expect const to represent. I used the word ROMable for a reason meaning the allocated memory should not alter.
Jul 31 2021
On Friday, 30 July 2021 at 20:09:19 UTC, Tejas wrote:Also remember that casting away const is undefined behaviour in D (unlike being defined and supported in C++).No. Casting const away IS NOT undefined behavior in both D and C++. Modifying a const object IS undefined behavior in both D and C++. There is not much difference between D and C++ in that way.
Jul 31 2021
On Sunday, 1 August 2021 at 06:15:30 UTC, Jack Applegame wrote:On Friday, 30 July 2021 at 20:09:19 UTC, Tejas wrote:Not in C++, when the const object modification is taking place via member variables declared as mutable accessed from a const member function. This is also a thing in Swift and Rust, by the way, they just use other mechanisms to do the same.Also remember that casting away const is undefined behaviour in D (unlike being defined and supported in C++).No. Casting const away IS NOT undefined behavior in both D and C++. Modifying a const object IS undefined behavior in both D and C++. There is not much difference between D and C++ in that way.
Aug 01 2021
On Sunday, 1 August 2021 at 06:15:30 UTC, Jack Applegame wrote:On Friday, 30 July 2021 at 20:09:19 UTC, Tejas wrote:In D, casting away const from a pointer and mutating the pointed-to object is UB even if the object it points to was not originally declared as `const` or `immutable`: ```d int n 123; // mutable object const(int)* pc = &n; int* pm = cast(int*) pc; *pm = 456; // undefined behavior ``` In C++, the above is allowed. Source: https://dlang.org/spec/cpp_interface.html#comparing-d-immutable-and-const-with-cpp-constAlso remember that casting away const is undefined behaviour in D (unlike being defined and supported in C++).No. Casting const away IS NOT undefined behavior in both D and C++. Modifying a const object IS undefined behavior in both D and C++. There is not much difference between D and C++ in that way.
Aug 01 2021
On Friday, 30 July 2021 at 19:17:55 UTC, Michael Galuza wrote:Is there any analogue of C++ `mutable` in D? As far as I understand, this is impossible due to transitive constness, but maybe some variation of `Rebindable` will be useful? P.S. Of course I mean 'fair' mutable, not qualifier-away `cast()`.Not any good analogue. There are some situation-dependant possibilities though. First one, you could do some template magic. Something like: ```d template constIf(bool cond, T) { static if(cond) alias constIf = const(T); else alias constIf = T; } struct AType(bool isConst) { constIf!(isConst, int[]) a; constIf!(isConst, int[]) b; int[] iPretendToBeMutable; } ``` Second possibility is that you store the mutable "element" in some external data structure, and store it's key or index in the `const` `struct`/`class`. This is what I'd probably do.
Aug 01 2021
On Friday, 30 July 2021 at 19:17:55 UTC, Michael Galuza wrote:Is there any analogue of C++ `mutable` in D? As far as I understand, this is impossible due to transitive constness, but maybe some variation of `Rebindable` will be useful? P.S. Of course I mean 'fair' mutable, not qualifier-away `cast()`.Let's slightly reformulate my question. Can we write in D smth like this: ``` struct Mutable(T) { /* magic */ } struct Shape { private Mutable!double area; double getArea() const { if (area == 0) { area = computeArea(); } return area; } } ``` In other words, can we implement method `void S.opAssign(T value) const` of some struct `S` which change internal state of `S` and this method doesn't have UB and doesn't break D's type system.
Aug 01 2021
On Sunday, 1 August 2021 at 18:21:32 UTC, Michael Galuza wrote:In other words, can we implement method `void S.opAssign(T value) const` of some struct `S` which change internal state of `S` and this method doesn't have UB and doesn't break D's type system.No. Mutating an object typed as `const` is always UB in D.
Aug 01 2021
On 8/1/21 2:21 PM, Michael Galuza wrote:On Friday, 30 July 2021 at 19:17:55 UTC, Michael Galuza wrote:Yes. You need to use global space to do it. e.g.: ```d struct Shape { private static double[size_t] areaLookup; private const size_t magicNumber; // set on constructor double getArea() const { return areaLookup.require(magicNumber, computeArea()); } } ``` I've argued in the past that since this is possible, putting the memory inside (or along-side) the instance isn't all that different, and there are surely other typesystem-allowable ways (I think there was a plan at some point to use affixAllocator to do something like this). But "possible" here is not exactly equivalent to "desirable". What you need is some way to clarify that the "mutable" data is not actually part of the instance, but some nebulous region that is carried along with it. -SteveIs there any analogue of C++ `mutable` in D? As far as I understand, this is impossible due to transitive constness, but maybe some variation of `Rebindable` will be useful? P.S. Of course I mean 'fair' mutable, not qualifier-away `cast()`.Let's slightly reformulate my question. Can we write in D smth like this: ``` struct Mutable(T) { /* magic */ } struct Shape { private Mutable!double area; double getArea() const { if (area == 0) { area = computeArea(); } return area; } } ``` In other words, can we implement method `void S.opAssign(T value) const` of some struct `S` which change internal state of `S` and this method doesn't have UB and doesn't break D's type system.
Aug 02 2021
On Friday, 30 July 2021 at 19:17:55 UTC, Michael Galuza wrote:Is there any analogue of C++ `mutable` in D? As far as I understand, this is impossible due to transitive constness, but maybe some variation of `Rebindable` will be useful? P.S. Of course I mean 'fair' mutable, not qualifier-away `cast()`.Not possible as other people mentioned. In this case the best approach would be to either design your struct to contain const and mutable fields, and design it's interface accordingly, or you could make a mutable decorator that would be mutable and expose mutable aspects, while keeping the decorated instance const. Best regards, Alexandru.
Aug 01 2021
On Friday, 30 July 2021 at 19:17:55 UTC, Michael Galuza wrote:[snip]We are saying that there is no direct analogue for `mutable` in D. It turns out we were all wrong - there is, and it even works in ` safe`! Behold: ```d safe: alias MutableDel(T) = safe ref T delegate(); struct Foo { int normalMember; private MutableDel!int _mutableMember; ref mutableMember() const {return _mutableMember();} this(int a, int b) { normalMember = a; _mutableMember = makeMutable(b); } } MutableDel!T makeMutable(T)(T mem) safe { auto varArr = [mem]; return ref () => varArr[0]; } void main() { import std; const foo = Foo(5, 10); foo.mutableMember.writeln; //10 foo.mutableMember = 15; foo.mutableMember.writeln; } ``` Now, I definitely don't recommend using this. It might well end up being considered as a bug, and thus stop working in the future. Also the optimizer might not take this possibility into account, thus injecting bugs to your code.
Aug 03 2021
On Tuesday, 3 August 2021 at 13:02:38 UTC, Dukc wrote:Now, I definitely don't recommend using this. It might well end up being considered as a bug, and thus stop working in the future. Also the optimizer might not take this possibility into account, thus injecting bugs to your code.It's a known bug, since 2008: https://issues.dlang.org/show_bug.cgi?id=1983
Aug 03 2021
On Tuesday, 3 August 2021 at 14:01:20 UTC, Paul Backus wrote:On Tuesday, 3 August 2021 at 13:02:38 UTC, Dukc wrote:I don't think it's that one. I'm not abusing the delegates context pointer mutablity, I'm abusing the fact that a `const` or `immutable` `delegate` may have a mutable return type: ```d safe: void main() { import std; auto varArr = [5]; immutable del = () => varArr[0]; del().writeln; varArr[0] = 10; del().writeln; } ```Now, I definitely don't recommend using this. It might well end up being considered as a bug, and thus stop working in the future. Also the optimizer might not take this possibility into account, thus injecting bugs to your code.It's a known bug, since 2008: https://issues.dlang.org/show_bug.cgi?id=1983
Aug 03 2021
On Tuesday, 3 August 2021 at 15:33:34 UTC, Dukc wrote:On Tuesday, 3 August 2021 at 14:01:20 UTC, Paul Backus wrote:This example is abusing the fact that an `immutable` delegate can have a mutable context pointer--i.e., that immutability of delegate contexts is not transitive. If you try to replace the delegate with a user-defined type... ```d struct Delegate { int* context; this(int* p) { context = p; } int opCall() { return *context; } } void main() { int* p = new int(5); immutable dg = Delegate(p); } ``` The compiler correctly points out that the conversion to `immutable` is invalid:https://issues.dlang.org/show_bug.cgi?id=1983I don't think it's that one. I'm not abusing the delegates context pointer mutablity, I'm abusing the fact that a `const` or `immutable` `delegate` may have a mutable return type: ```d safe: void main() { import std; auto varArr = [5]; immutable del = () => varArr[0]; del().writeln; varArr[0] = 10; del().writeln; } ```Error: cannot implicitly convert expression `Delegate(null).this(p)` of type `Delegate` to `immutable(Delegate)`You're right, though, that it's not exactly issue 1983. I think the bugzilla issue for this specific case is https://issues.dlang.org/show_bug.cgi?id=16058.
Aug 03 2021