digitalmars.D.learn - is my code to get CTFE instantiated object valid D ?
- chmike (21/21) May 27 2016 I need to create an app wide singleton instance for my class.
- Steven Schveighoffer (5/23) May 27 2016 You can cast away immutable. You just can't mutate. Hard to say without
- Kagamin (6/8) May 27 2016 A mutable object can be synchronized on:
- chmike (20/28) May 27 2016 That is a good argument. Thanks. It would indeed be a good idea
- Mike Parker (14/18) May 28 2016 The difference lies in the guarantees of const and immutable.
- Mike Parker (2/4) May 28 2016 And, of course, those should be const(Foo) and immutable(Foo).
- ag0aep6g (24/28) May 28 2016 I don't think that's true.
- Mike Parker (34/51) May 28 2016 Well then, this completely breaks my understanding of variable
- Mike Parker (11/13) May 28 2016 OK, I see now at [1] the following:
- Steven Schveighoffer (5/18) May 29 2016 It's reasonable to treat const variables like immutable when the
- Kagamin (5/9) May 28 2016 Sometimes compiler is able to figure out that const data is
- Era Scarecrow (7/9) May 27 2016 Then... why cast away immutable?
- chmike (47/69) May 28 2016 I answer to myself on this question because I finally found out.
I need to create an app wide singleton instance for my class. The singleton is immutable, but I want to allow mutable references to that singleton object so that I can do fast 'is' tests. I declared this class Category { protected static immutable Category instance_ = new Category; Category instance() { return cast(Category)instance_; } ... } It compiles and the instance should be instantiated at compile time. I couldn't check yet. The public interface of Category is designed so that the object's state can't be modified and thus remains immutable. Is this code valid D or is the behavior undefined due to the cast ? A variant implementation would have a method that modifies the object but only internally and in a very controlled way to store strings in a cache for faster access. Would it still be valid D code ?
May 27 2016
On 5/27/16 4:20 PM, chmike wrote:I need to create an app wide singleton instance for my class. The singleton is immutable, but I want to allow mutable references to that singleton object so that I can do fast 'is' tests. I declared this class Category { protected static immutable Category instance_ = new Category; Category instance() { return cast(Category)instance_; } ... } It compiles and the instance should be instantiated at compile time. I couldn't check yet. The public interface of Category is designed so that the object's state can't be modified and thus remains immutable. Is this code valid D or is the behavior undefined due to the cast ?You can cast away immutable. You just can't mutate. Hard to say without seeing what the ... is.A variant implementation would have a method that modifies the object but only internally and in a very controlled way to store strings in a cache for faster access. Would it still be valid D code ?No. Undefined behavior if you modify immutable data. -Steve
May 27 2016
On Friday, 27 May 2016 at 20:20:36 UTC, chmike wrote:Is this code valid D or is the behavior undefined due to the cast ?A mutable object can be synchronized on: synchronized(Category.instance){} This will create and store a mutex in the object (sad but true, design taken from java). If the immutable object is in readonly memory, it will crash on write.
May 27 2016
On Friday, 27 May 2016 at 21:41:02 UTC, Kagamin wrote:On Friday, 27 May 2016 at 20:20:36 UTC, chmike wrote:That is a good argument. Thanks. It would indeed be a good idea to put immutable objects in a read only portion of the code. Would it be different if the object was declared const instead of immutable ? I want compile time instantiation and mutable references to the object. Apparently only immutable and const objects can be instantiated at compile time. I also want an app wide singleton (not stored in TLS). If I use _gshared I apparently wouldn't get a compile time instantitation. It's to implement the flyweight pattern. I don't want to use Rebindable because the assembly code generated by dmd is currently inefficient. Rebindable cast away the const but the non constant reference is not exposed. What is the difference between a const and immutable object ? would a const object be allowed to modify itself by using a hash table or caching results inside ? Is a static const Category c variable a TLS variable ? This is a bit frustrating because it is trivial to implement in C and C++.Is this code valid D or is the behavior undefined due to the cast ?A mutable object can be synchronized on: synchronized(Category.instance){} This will create and store a mutex in the object (sad but true, design taken from java). If the immutable object is in readonly memory, it will crash on write.
May 27 2016
On Saturday, 28 May 2016 at 05:30:26 UTC, chmike wrote:What is the difference between a const and immutable object ? would a const object be allowed to modify itself by using a hash table or caching results inside ?The difference lies in the guarantees of const and immutable. Foo f = new Foo; const(F) cf = f; The above is legal. The single instance can modify itself in methods called via f, but not those called via cf. So the answer to the second question is no, not directly. Foo f = new Foo; immutable(f) if = f; This will not compile. The compiler expects that any reference assigned to if will not be modified anywhere in the program, ever. It does not have the same expectation of const references.Is a static const Category c variable a TLS variable ?Yes. All variables are TLS unless explicitly marked with __gshared or shared.
May 28 2016
On Saturday, 28 May 2016 at 08:34:17 UTC, Mike Parker wrote:const(F) cf = f; immutable(f) if = f;And, of course, those should be const(Foo) and immutable(Foo).
May 28 2016
On 05/28/2016 10:34 AM, Mike Parker wrote:On Saturday, 28 May 2016 at 05:30:26 UTC, chmike wrote:[...]I don't think that's true. ---- import core.thread; import std.stdio; int m; const int c; immutable int i; void main() { printAddresses(); new Thread(&printAddresses).start(); } void printAddresses() { writeln(&m, " ", &c, " ", &i); } ---- Prints (for example): ---- 7F554F9E1710 695FF0 695FF4 7F554EDDA5D0 695FF0 695FF4 ---- So there are two different `m`s, but the `c`s and `i`s have the same address on both threads. Seems to me that `m` is in TLS, but `c` and `i` are not.Is a static const Category c variable a TLS variable ?Yes. All variables are TLS unless explicitly marked with __gshared or shared.
May 28 2016
On Saturday, 28 May 2016 at 15:39:44 UTC, ag0aep6g wrote:On 05/28/2016 10:34 AM, Mike Parker wrote:On Saturday, 28 May 2016 at 05:30:26 UTC, chmike wrote:[...]I don't think that's true.Is a static const Category c variable a TLS variable ?Yes. All variables are TLS unless explicitly marked with __gshared or shared.Prints (for example): ---- 7F554F9E1710 695FF0 695FF4 7F554EDDA5D0 695FF0 695FF4 ---- So there are two different `m`s, but the `c`s and `i`s have the same address on both threads. Seems to me that `m` is in TLS, but `c` and `i` are not.Well then, this completely breaks my understanding of variable scope. Consider this: ------ class Foo { int x; this(int i) { x = i; } } class Bar { static const Foo f; static this() { __gshared static firstRun = true; f = new Foo(firstRun ? 10 : 20); firstRun = false; import std.stdio; writeln("static con"); } } void main() { import core.thread; print(); new Thread(&print).start(); Thread.sleep(1.seconds); print(); } void print() { import std.stdio; writefln("Bar.f.x = %s, &Bar.f = %s", Bar.f.x, &Bar.f); } ---- The static constructor is run per thread. The second iteration reinitializes the const reference 'f' with a new instance. Not at all what I would expect. IMO, either its a bug that there is only one instance of 'f', or it's a bug that it can be reinitialized.
May 28 2016
On Sunday, 29 May 2016 at 05:35:33 UTC, Mike Parker wrote:Well then, this completely breaks my understanding of variable scope.OK, I see now at [1] the following: " Immutable data doesn't have synchronization problems, so the compiler doesn't place it in TLS." I've read that page more than once, but I had forgotten this bit. Still, I don't see anything there about const. I would not expect const variables to behave the same way, given the weaker guarantee about modification. But if they are intended to behave that way, then, IMO, it should not be possible to reinitialize them in a static constructor. https://dlang.org/migrate-to-shared.html
May 28 2016
On Sunday, 29 May 2016 at 05:43:31 UTC, Mike Parker wrote:On Sunday, 29 May 2016 at 05:35:33 UTC, Mike Parker wrote:It's reasonable to treat const variables like immutable when the const variable has no indirections. However, it shouldn't allow rewriting the variable in each thread ctor. -SteveWell then, this completely breaks my understanding of variable scope.OK, I see now at [1] the following: " Immutable data doesn't have synchronization problems, so the compiler doesn't place it in TLS." I've read that page more than once, but I had forgotten this bit. Still, I don't see anything there about const. I would not expect const variables to behave the same way, given the weaker guarantee about modification. But if they are intended to behave that way, then, IMO, it should not be possible to reinitialize them in a static constructor. https://dlang.org/migrate-to-shared.html
May 29 2016
On Saturday, 28 May 2016 at 05:30:26 UTC, chmike wrote:Would it be different if the object was declared const instead of immutable ?Sometimes compiler is able to figure out that const data is immutable.This is a bit frustrating because it is trivial to implement in C and C++.For a trick of static mutable allocation see https://github.com/dlang/druntime/pull/1325
May 28 2016
On Saturday, 28 May 2016 at 08:47:48 UTC, Kagamin wrote:For a trick of static mutable allocation see https://github.com/dlang/druntime/pull/1325Thank you that looks promising. I'll study an experiment with the code. If I would like that the instances are not in TLS, can I use the following ? private __gshared void[...] store; I then need to be sure that the objects are instantiated at compile time. Will Emplace do the trick ?
May 28 2016
On Saturday, 28 May 2016 at 08:47:48 UTC, Kagamin wrote:For a trick of static mutable allocation see https://github.com/dlang/druntime/pull/1325In the following instruction of the above commit, what effect has the [] after init ? _store[0 .. __traits(classInstanceSize, T)] = typeid(T).init[]; T is a template argument that is a class derived from Error. I couldn't find an explanation here https://dlang.org/spec/property.html#init. I saw that this is a concise implementation of what is being done in emplace().
May 28 2016
On 05/28/2016 06:09 PM, chmike wrote:In the following instruction of the above commit, what effect has the [] after init ? _store[0 .. __traits(classInstanceSize, T)] = typeid(T).init[]; T is a template argument that is a class derived from Error. I couldn't find an explanation here https://dlang.org/spec/property.html#init. I saw that this is a concise implementation of what is being done in emplace().`typeid(T).init` is a different `init` than the one you linked. We have since renamed it to `initializer` to fix the name clash. Documentation is here: As you can see there, it's a `const(void)[]`. The statement is a form of "array copying": http://dlang.org/spec/arrays.html#array-copying As you can see there, it would also work without the `[]`. With them it's a bit clearer that array copying is going on, and not "array setting" (next section on the spec page).
May 28 2016
On Friday, 27 May 2016 at 20:20:36 UTC, chmike wrote:The public interface of Category is designed so that the object's state can't be modified and thus remains immutable.Then... why cast away immutable? I suppose there's always a core set of variables that are what the object actually considers for it's constness, while there's other extra ones for convenience (say lookup hash tables, memoize and the like) which assist but their presence or lack thereof only affects speed rather than function...
May 27 2016
On Friday, 27 May 2016 at 20:20:36 UTC, chmike wrote:I need to create an app wide singleton instance for my class. The singleton is immutable, but I want to allow mutable references to that singleton object so that I can do fast 'is' tests. I declared this class Category { protected static immutable Category instance_ = new Category; Category instance() { return cast(Category)instance_; } ... } It compiles and the instance should be instantiated at compile time. I couldn't check yet. The public interface of Category is designed so that the object's state can't be modified and thus remains immutable. Is this code valid D or is the behavior undefined due to the cast ? A variant implementation would have a method that modifies the object but only internally and in a very controlled way to store strings in a cache for faster access. Would it still be valid D code ?I answer to myself on this question because I finally found out. Creating an app wide singleton object is as simple as this class Category { this() { assert __ctfe); } // Just to make sure private __gshared instance_ = new Info; static Category instance() { return _instance; } } It works as long as the constructor doesn't reference other global or static variables. Unfortunately this is what I have in my use case. I had this final class Info { Info(Category category, string name) { category_ = category; name_ = name; assert(__ctfe); } private string name_; private Category category_; string name() { return name_; } Category category() { return category_; } } This class can't be instantiated as compile time because the constructor depends on the global variable Category.instance_. Category { ... __gshared Info a1 = new Info(Category.instance(), "I'm a1"); __gshared Info a2 = new Info(Category.instance(), "I'm a2"); ... } The problem is solved by changing Category into an immutable class and instance. It's Ok in my case because it is immutable. The only inconvenience left is that we can't have mutable references to immutable objects. But at least now I can write Info x1 = Cateqory.a1, x2 = Category.a2; Info x3 = x1, x4; assert(x3 is x1); assert(x3 !is x2); assert(x1.category is x2.category); assert(x4 is null); And of course we can also access all the properties of the Info values. Objects are identified by their unique address. Another problem left is that synchronization
May 28 2016
On 05/28/2016 09:54 PM, chmike wrote:The only inconvenience left is that we can't have mutable references to immutable objects.There is std.typecons.Rebindable for that.
May 28 2016
On Saturday, 28 May 2016 at 21:21:34 UTC, ag0aep6g wrote:On 05/28/2016 09:54 PM, chmike wrote:That would be a good news. What is the right way to use it ? I had a hard time to understand how to use Rebindable. Rebindable!(immutable Category) instance( return Rebindable!(immutable Category)(instance_); } Or should, I do it like that ? Rebindable!(const Category) instance( return Rebindable!(const Category)(instance_); } The immutable and const concepts are really different from C and C++. A problem with rebindable is that its current assembly translation for 'is' test does a byte per byte comparison which is much less efficient than a direct 64bit comparison of the struct. I checked that with dmd. I don't know about gdc and ldc. This is something that will,be optimized soon I hope.The only inconvenience left is that we can't have mutable references to immutable objects.There is std.typecons.Rebindable for that.
May 28 2016
On Sunday, 29 May 2016 at 06:49:42 UTC, chmike wrote:What is the right way to use it ?I answer to my self after testing so that people looking for that info can find it here. The right way would be immutable Category category_; Rebindable!(immutable Category) instance() { return rebindable(category_); } it is equivalent and shorter (for the lazy) to write auto instance() { return rebindable(category_); } The disadvantage of this form is that the reader of documentation generated automatically from the source file will only see this : auto instance() and he won't know what this instance method effectively returns. If we want a mutable reference that accept mutable and immutable Category objects, we should define this Rebindable!(const Category) x = Category.instance(); x = new Category; The concept of immutable and constness of D was the most difficult thing to learn because it is radically different from the const concept of C and C++. Now that I understood it and stopped getting hit with immutability and constness compiler errors, I start to like it. So the only problem I see with Rebindable is the inefficiency of reference comparison in the generated assembly code with DMD64 D Compiler v2.071.0. This will hopefully get fixed in next versions. Thank you very much to everybody who took the time to help me learn D and answer my so many questions.
May 29 2016