www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - is my code to get CTFE instantiated object valid D ?

reply chmike <christophe meessen.net> writes:
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
next sibling parent Steven Schveighoffer <schveiguy yahoo.com> writes:
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
prev sibling next sibling parent reply Kagamin <spam here.lot> writes:
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
parent reply chmike <christophe meessen.net> writes:
On Friday, 27 May 2016 at 21:41:02 UTC, Kagamin wrote:
 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.
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++.
May 27 2016
next sibling parent reply Mike Parker <aldacron gmail.com> writes:
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
next sibling parent Mike Parker <aldacron gmail.com> writes:
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
prev sibling parent reply ag0aep6g <anonymous example.com> writes:
On 05/28/2016 10:34 AM, Mike Parker wrote:
 On Saturday, 28 May 2016 at 05:30:26 UTC, chmike wrote:
[...]
 Is a static const Category c variable a TLS variable ?
Yes. All variables are TLS unless explicitly marked with __gshared or shared.
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.
May 28 2016
parent reply Mike Parker <aldacron gmail.com> writes:
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:
[...]
 Is a static const Category c variable a TLS variable ?
Yes. All variables are TLS unless explicitly marked with __gshared or shared.
I don't think that's true.
 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
parent reply Mike Parker <aldacron gmail.com> writes:
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
parent Steven Schveighoffer <schveiguy yahoo.com> writes:
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:

 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
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. -Steve
May 29 2016
prev sibling parent reply Kagamin <spam here.lot> writes:
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
next sibling parent chmike <christophe meessen.net> writes:
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/1325
Thank 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
prev sibling parent reply chmike <christophe meessen.net> writes:
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/1325
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().
May 28 2016
parent ag0aep6g <anonymous example.com> writes:
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: http://dlang.org/phobos/object.html#.TypeInfo.initializer 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
prev sibling next sibling parent Era Scarecrow <rtcvb32 yahoo.com> writes:
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
prev sibling parent reply chmike <christophe meessen.net> writes:
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
parent reply ag0aep6g <anonymous example.com> writes:
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
parent reply chmike <christophe meessen.net> writes:
On Saturday, 28 May 2016 at 21:21:34 UTC, ag0aep6g wrote:
 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.
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.
May 28 2016
parent chmike <christophe meessen.net> writes:
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