www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Immutable objects and constructor ?

reply chmike <christophe meessen.net> writes:
I'm implementing the flyweight pattern. It means that I have a 
set of object instances representing all the possible values. 
This allows me to manipulate "values" by simply manipulating 
references to the instance. Testing "value" equality boils down 
to simply compare reference value. I hope I can use these 
immutable instances as case argument of a switch, but I'm not 
there yet.

I have declared an immutable class.

----
immutable interface SomeInfo { ... }

immutable class Info : SomeInfo {
     disable this();
    this(int codeValue, string codeName)
    {
        codeValue_ = (codeValue * 10) - 113; // some random 
example computation
        codeName_ = "Info."~codeName_;
    }
    private:
       int codeValue_;
       string codeName_;
}
----

But when I try to instantiate the class I get an dramatic 
compilation error:

"none of the overloads of '__ctor' are callable using a mutable 
object, candidates are: "

Adding immutable to the constructor doesn't help. How can I 
initialize the immutable instances of a class ?


I have seen it is possible to cast away the immutability. Does it 
also work for immutable classes ? Could I use an object factory ?

How should I do if I would like to use the lazy pattern for 
initializing some member variables of the instance ? Something 
like :

immutable class Info : SomeInfo
{
    ...
    string toString()
    {
       if (!toString_)
           synchronize { // make it thread safe
              if (!toString_)
                 toString_ = format("bla bla %s (%d)", codeName_, 
codeValue_);
           }
       return toString_;
    }
    ...
    private:
       string toString_;
}
May 20 2016
parent reply Kagamin <spam here.lot> writes:
On Friday, 20 May 2016 at 14:06:54 UTC, chmike wrote:
 But when I try to instantiate the class I get an dramatic 
 compilation error:

 "none of the overloads of '__ctor' are callable using a mutable 
 object, candidates are: "
auto a=new immutable Info(1,"1");
 How should I do if I would like to use the lazy pattern for 
 initializing some member variables of the instance ?
Use mutable class.
May 20 2016
parent reply chmike <christophe meessen.net> writes:
On Friday, 20 May 2016 at 14:40:23 UTC, Kagamin wrote:
 On Friday, 20 May 2016 at 14:06:54 UTC, chmike wrote:
 But when I try to instantiate the class I get an dramatic 
 compilation error:

 "none of the overloads of '__ctor' are callable using a 
 mutable object, candidates are: "
auto a=new immutable Info(1,"1");
 How should I do if I would like to use the lazy pattern for 
 initializing some member variables of the instance ?
Use mutable class.
Thank you very much for the fast and very helpful reply. I'll try that.
May 20 2016
parent reply chmike <christophe meessen.net> writes:
The error message is gone, but I now have another compilation 
error message I don't understand.

This is what I have in fact

interface Info { . . . }

class MyInfos {
    . . .
protected:
    class Obj : Info
    {
        . . .
    }

public:
    static immutable Obj one = new immutable Obj(...);
    static immutable Obj two = new immutable Obj(...);
}

I get a compiler error in the two assignments to the static Obj 
member variables:
'this' is only defined in non-static member functions, not MyInfos

Is it related to the fact that the Obj class is encapsulated ?


My goal is to be able to write things like this:

void main() {
    Info x1 = MyInfos.one, x2 = MyInfo.two, x3;
    assert(x3 is null);
    x3 = x1;
    assert(x3 is x1);
    assert(x3 is MyInfo.one);

    // Use static immutable instance references as case arg in 
switch
    switch(x1) {
    case MyInfo.one: ...;
    }
}
May 20 2016
next sibling parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
On Friday, 20 May 2016 at 15:07:53 UTC, chmike wrote:
 The error message is gone, but I now have another compilation 
 error message I don't understand.

 This is what I have in fact

 interface Info { . . . }

 class MyInfos {
    . . .
 protected:
    class Obj : Info
    {
        . . .
    }

 public:
    static immutable Obj one = new immutable Obj(...);
    static immutable Obj two = new immutable Obj(...);
 }

 I get a compiler error in the two assignments to the static Obj 
 member variables:
 'this' is only defined in non-static member functions, not 
 MyInfos

 Is it related to the fact that the Obj class is encapsulated ?
Yes, nested classes have an implicit reference to their parent object, which doesn't exist in your case.
 My goal is to be able to write things like this:

 void main() {
    Info x1 = MyInfos.one, x2 = MyInfo.two, x3;
    assert(x3 is null);
    x3 = x1;
    assert(x3 is x1);
    assert(x3 is MyInfo.one);

    // Use static immutable instance references as case arg in 
 switch
    switch(x1) {
    case MyInfo.one: ...;
    }
 }
It looks like your don't actually need `Obj` to be a real nested class. Try declaring it as `static Obj : Info { }`. This should work if `Obj`'s methods don't need access to `MyInfo`'s non-static members.
May 20 2016
parent chmike <christophe meessen.net> writes:
On Friday, 20 May 2016 at 15:43:28 UTC, Marc Schütz wrote:

 It looks like your don't actually need `Obj` to be a real 
 nested class. Try declaring it as `static Obj : Info { }`. This 
 should work if `Obj`'s methods don't need access to `MyInfo`'s 
 non-static members.
That worked great. Thank you. The only problem left is how I can get mutable references to immutable objects.
May 20 2016
prev sibling parent reply chmike <christophe meessen.net> writes:
I solved the problem by moving the class Obj definition out of 
the class MyInfo.
I still don't understand why I had to do that. In C++ this would 
work without problem.

I now have

interface Info {. . .}

class Obj : Info {. . .}

class MyInfos
{
     . . .
     static immutable Obj one = new immutable Obj(...);
     static immutable Obj two = new immutable Obj(...);
     . . .
}

This now compiles without a peep.

But I now met another error in my main(). I can't assign the 
immutable object to a mutable reference.

Info x1 = MyInfos.one;

Is it possible to define a mutable reference to an immutable 
instance ? 

This is confusing and frustrating. In C++ we can write
MyInfos {
    . . .
    // one is a constant pointer to a constant object of type Obj
    Obj const * const one;
    . . .
}

And in main()

Info const * x1 = MyInfos.one;

x1 i a modifiable pointer to a constant object of type Info.

Is this possible in D ? I couldn't find how to do that.
May 20 2016
next sibling parent reply Kagamin <spam here.lot> writes:
On Friday, 20 May 2016 at 16:09:54 UTC, chmike wrote:
 But I now met another error in my main(). I can't assign the 
 immutable object to a mutable reference.

 Info x1 = MyInfos.one;

 Is it possible to define a mutable reference to an immutable 
 instance ? 
Sort of possible with a library solution: import std.typecons; auto x1 = rebindable(MyInfos.one);
May 20 2016
parent reply chmike <christophe meessen.net> writes:
On Friday, 20 May 2016 at 17:35:01 UTC, Kagamin wrote:
 On Friday, 20 May 2016 at 16:09:54 UTC, chmike wrote:
 But I now met another error in my main(). I can't assign the 
 immutable object to a mutable reference.

 Info x1 = MyInfos.one;

 Is it possible to define a mutable reference to an immutable 
 instance ? 
Sort of possible with a library solution: import std.typecons; auto x1 = rebindable(MyInfos.one);
I'm a bit surprized that the language doesn't support this. We have immutable strings that can be assigned to different variables. Why couldn't we do the same with objects ? This rebindable is not user friendly. I really wish the user could write this Info x = MyInfos.one; I may achieve this if Info is defined as a struct with a single member defined as rebindable.
May 20 2016
next sibling parent reply Jonathan M Davis via Digitalmars-d-learn writes:
On Friday, May 20, 2016 20:30:22 chmike via Digitalmars-d-learn wrote:
 On Friday, 20 May 2016 at 17:35:01 UTC, Kagamin wrote:
 On Friday, 20 May 2016 at 16:09:54 UTC, chmike wrote:
 But I now met another error in my main(). I can't assign the
 immutable object to a mutable reference.

 Info x1 = MyInfos.one;

 Is it possible to define a mutable reference to an immutable
 instance ?
Sort of possible with a library solution: import std.typecons; auto x1 = rebindable(MyInfos.one);
I'm a bit surprized that the language doesn't support this. We have immutable strings that can be assigned to different variables. Why couldn't we do the same with objects ?
It has to do with the fact that the type system does not differentiate between classes and references to class objects. When you type Object o; that's a reference to a class, not actually a class object, and the same goes for every use of a class. So, while you can have const(int)* ptr; and the compiler understands that, the compiler doesn't have an understanding of the separation of an Object and a reference to an Object, so not only is there no way to represent it syntactically, the compiler can't currently represent it semantically either. This is at least partially a result of making it so that classes inherently live on the heap. It's certainly possible to change it so that we have a way to have tail-const references to classes and so that Rebindable is then unnecessary, but Walter and Andrei don't think that it's worth it, so it hasn't happened. And while Rebindable is kind of ugly, it works just fine. So, the lack of tail-const for classes in the language hasn't really been a blocker for anything, just kind of ugly.
 This rebindable is not user friendly.
 I really wish the user could write this

 Info x = MyInfos.one;

 I may achieve this if Info is defined as a struct with a single
 member defined as rebindable.
Well, as long as you're dealing with local variables, you can use auto rather than typing the type explicitly, and then you don't need to explicitly use Rebindable, though you generally would for function parameters and member variables. But while typing Rebindable is a bit ugly, it's basically just a bit more verbose than what you'd get if it were built in. Right now you have Rebindable!Info info; whereas if it were buit in, you'd probably get something like tail_const(Info) info; or I think that someone suggested something like const(ref) Info info; And both of those are about as verbose as Rebindable is. So, they wouldn't save us much. So, sure, it would be nice if tail-const classes were built into the language, but we don't seem to be losing much by not having them. If someone could come up with why the lack of tail-const classes were a major roadblocker somehow, then maybe Walter could be convinced to alter the language, but you'd need a very solid reason as to why Rebindable doesn't cut it, and thinking that it's a bit ugly isn't going to be enough. - Jonathan M Davis
May 20 2016
parent reply chmike <christophe meessen.net> writes:
Unfortunately it is not possible to write this

import std.typecons;
class Info{...}
rebindable!Info x;

I get the following error message

source/app.d(11,3): Error: template std.typecons.rebindable 
matches more than one template declaration:
/usr/include/dmd/phobos/std/typecons.d(1675,14):     
rebindable(T)(T obj) if (is(T == class) || is(T == interface) || 
isDynamicArray!T || isAssociativeArray!T)
and
/usr/include/dmd/phobos/std/typecons.d(1694,14):     
rebindable(T)(Rebindable!T obj)


This would have been equivalent to define a mutable reference to 
an object of type Info and allow me to write this

alias rebindable!Info InfoR;
InfoR x;

Unfortunately I didn't manage to get something compiling with 
rebindable. I don't understand how I'm supposed to use it.


Note that I'm designing a library that I would like intuitive to 
use. Hacking around the problem won't cut it.

I need a type defining a mutable reference to an immutable 
object. Rebindable doesn't give me that apparently.

I need something allowing me to write this

interface Info {...}
class MyInfos {
     static class Obj : Info {...}
     static immutable Obj one = new immutable Obj(...);
}

mutableObjectRef!Info x1, x2;
assert(x1 is null);
assert(x1 == null);
x1 = MyInfos.one;
assert(x1 is MyInfos.one);
assert(x1 == MyInfos.one);
x2 = x1;

switch(x1){
case MyInfos.one: ... ;
default:...;
}

x1 must have the semantic of an object reference, support 
polymorphism, allowing to down or up cast and of course access 
immutable members and methods.
MyInfos.one is an object reference that I shouldn't be allowed to 
modify. I can't use a function because it needs the value 
semantic so I can use it as a case argument in a switch.

It doesn't seam that this is what rebind provides. It looks like 
mutableObjectRef should be a templated struct. But I'm not 
experienced enough in D to implement it. I don't know if its only 
possible.
May 21 2016
next sibling parent chmike <christophe meessen.net> writes:
Since I'm trying to implement a flyweight pattern, the opEqual 
need only comparision of reference in my case.

By the way, what operation is the switch performing ? OpEqual or 
is ?
May 21 2016
prev sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 05/21/2016 01:07 AM, chmike wrote:
 Unfortunately it is not possible to write this

 import std.typecons;
 class Info{...}
 rebindable!Info x;
You have a capitalization typo. Rebindable is a type template, rebindable is a function template. import std.typecons; class Info{} void main() { auto x = rebindable(new immutable(Info)()); pragma(msg, typeof(x)); auto y = Rebindable!(immutable(Info))(new Info()); pragma(msg, typeof(y)); } Ali
May 21 2016
parent chmike <christophe meessen.net> writes:
On Saturday, 21 May 2016 at 08:24:19 UTC, Ali Çehreli wrote:
 On 05/21/2016 01:07 AM, chmike wrote:
 Unfortunately it is not possible to write this

 import std.typecons;
 class Info{...}
 rebindable!Info x;
You have a capitalization typo. Rebindable is a type template, rebindable is a function template. import std.typecons; class Info{} void main() { auto x = rebindable(new immutable(Info)()); pragma(msg, typeof(x)); auto y = Rebindable!(immutable(Info))(new Info()); pragma(msg, typeof(y)); } Ali
Thank you. I'll start a new thread with the subject "problems with Rebindable". See https://forum.dlang.org/post/bprfdptcvzzkfzxlhflb forum.dlang.org
May 21 2016
prev sibling parent Mike Parker <aldacron gmail.com> writes:
On Friday, 20 May 2016 at 20:30:22 UTC, chmike wrote:

 I'm a bit surprized that the language doesn't support this. We 
 have immutable strings that can be assigned to different 
 variables. Why couldn't we do the same with objects ?
Consider this: immutable(char)[] str; Here, the array elements of str are immutable, i.e. you cannot do this: str[0] = 's' However, the array reference itself is mutable, so you can freely assign array references around. This is what string is aliased to. To prevent the array reference from being assigned: immutable(char[]) str; Now trying to assign another string to str will produce a compiler error. With arrays and pointers, we distinguish between the data and the reference. With classes, we do not; there is only the reference.
May 20 2016
prev sibling next sibling parent Mike Parker <aldacron gmail.com> writes:
On Friday, 20 May 2016 at 16:09:54 UTC, chmike wrote:

 But I now met another error in my main(). I can't assign the 
 immutable object to a mutable reference.

 Info x1 = MyInfos.one;

 Is it possible to define a mutable reference to an immutable 
 instance ? 

 This is confusing and frustrating. In C++ we can write
 MyInfos {
    . . .
    // one is a constant pointer to a constant object of type Obj
    Obj const * const one;
    . . .
 }
I strongly advise you put C++ out of your head when programming D and try to come at it as if you have never seen C++. Otherwise, you will just keep getting frustrated. D is a different language and, while some C++ idioms may work just fine, others do not and cannot. Consider this: immutable(int*) ifoo; Because the * is inside the parens in the declaration of ifoo, we are saying that we never want the pointer to be reassigned. The compiler is free to behave as if that is true and that ifoo will always point to the same address. It might make certain optimizations based on that guarantee that might not otherwise be possible. The same is true if you replace immutable with const in this particular declaration. If you somehow manage to circumvent that guarantee, then you are breaking the compiler's expectations and entering the realm of undefined behavior. You can think of an immutable class reference as being an immutable pointer like ifoo. C++ has no such guarantees. The compiler is not free to make the same assumptions D can make. If D allowed you to do what C++ does, then D compilers would be in the same boat. This is one of the reasons why you can't expect D code to behave like C++ code in every case, no matter how similar things look on the surface.
May 20 2016
prev sibling parent Mike Parker <aldacron gmail.com> writes:
On Friday, 20 May 2016 at 16:09:54 UTC, chmike wrote:

 This is confusing and frustrating. In C++ we can write
 MyInfos {
    . . .
    // one is a constant pointer to a constant object of type Obj
    Obj const * const one;
    . . .
 }

 And in main()

 Info const * x1 = MyInfos.one;

 x1 i a modifiable pointer to a constant object of type Info.

 Is this possible in D ? I couldn't find how to do that.
I should have addressed this above. This is another of the those things that will bite you if you are thinking in C++ when writing D. Classes in D are references types, so right out of the gate they can not be treated as C++ classes. You absolutely can have modifiable pointers to const and immutable data with built in types and structs, but not with classes. Always think of const(classref) as const(class*). There is no such thing as const(class)* in D.
May 20 2016