www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Assignment and down-casting

reply Yigal Chripun <yigal100 gmail.com> writes:
I just read this article by Dennis Richie posted on the NG [1], and I 
wanted to discuss a way to solve the const return issue.

here's some (general) code:

class A {}
class B : A {}

A func (A a) {
   .. do stuff ..
   return a;
}

void main() {
   B b = new B;
   A a1 = func(b);
   B a2 = cast(B) func(b); // will not compile with explicit cast
}

the explicit downcast could be removed if the compiler inserted the 
down-cast implicitly - assigning null or throwing if the down-cast fails.

this can be applied to constancy since const is super type of both 
immutable and mutable. I don't know how a const pointer/reference is 
implemented in D so assume that pointers to const objects have a certain 
bit set. (constancy runtime type)

// create a const object - bit is set
const T* cptr = cast(const T*) new Struct;
// create mutable object - bit unset
T* ptr = new Struct; // bit is unset

on assingment the bit is preserved and cannot be unset:
T* a = ptr; // bit unset
T* b = cptr; // fails - bit cannot be unset
const T* c = cptr; // bit is set
const T* d = ptr;  // bit remains unset

the second case fails by either assinging null to be or by throwing an 
exception.

invariant can use a different bit.

let's say we have:

const T foo(const T a, U b); // foo returns a

const T cptr = ...; //const object
       T ptr  = ...; // mutable object

T res = foo (cptr, something); // fails
T res = foo (ptr, something); // *works*
const T res = foo (cptr, something); // bit set for res
const T res = foo (ptr, something); // bit unset for res

just as before, the first foo fails by evaluating to null or by 
exception and the second foo works since the bit was never set so 
there's no problem "downcasting" to a regular, mutable, pointer/ref.

What do you think?

[1] http://www.lysator.liu.se/c/dmr-on-noalias.html
Mar 07 2009
parent reply "Tim M" <a b.com> writes:
On Sun, 08 Mar 2009 06:59:04 +1300, Yigal Chripun <yigal100 gmail.com>  
wrote:

 I just read this article by Dennis Richie posted on the NG [1], and I  
 wanted to discuss a way to solve the const return issue.

 here's some (general) code:

 class A {}
 class B : A {}

 A func (A a) {
    .. do stuff ..
    return a;
 }

 void main() {
    B b = new B;
    A a1 = func(b);
    B a2 = cast(B) func(b); // will not compile with explicit cast
 }

 the explicit downcast could be removed if the compiler inserted the  
 down-cast implicitly - assigning null or throwing if the down-cast fails.

 this can be applied to constancy since const is super type of both  
 immutable and mutable. I don't know how a const pointer/reference is  
 implemented in D so assume that pointers to const objects have a certain  
 bit set. (constancy runtime type)

 // create a const object - bit is set
 const T* cptr = cast(const T*) new Struct;
 // create mutable object - bit unset
 T* ptr = new Struct; // bit is unset

 on assingment the bit is preserved and cannot be unset:
 T* a = ptr; // bit unset
 T* b = cptr; // fails - bit cannot be unset
 const T* c = cptr; // bit is set
 const T* d = ptr;  // bit remains unset

 the second case fails by either assinging null to be or by throwing an  
 exception.

 invariant can use a different bit.

 let's say we have:

 const T foo(const T a, U b); // foo returns a
Thats bcos it's not a down cast. It's an up cast.
 const T cptr = ...; //const object
        T ptr  = ...; // mutable object

 T res = foo (cptr, something); // fails
 T res = foo (ptr, something); // *works*
 const T res = foo (cptr, something); // bit set for res
 const T res = foo (ptr, something); // bit unset for res

 just as before, the first foo fails by evaluating to null or by  
 exception and the second foo works since the bit was never set so  
 there's no problem "downcasting" to a regular, mutable, pointer/ref.

 What do you think?

 [1] http://www.lysator.liu.se/c/dmr-on-noalias.html
Mar 07 2009
parent reply "Tim M" <a b.com> writes:
What I was trying to say is that func needs to be called with an "A" but  
calling it with a "B" works because a "B" is a kind of "A". The function  
looses an information that it is a "B" returns it as an "A" which should  
be cast back to a "B" explicitly. This is corrct behavior as not all "A"s  
can be of kind "B".

One way to get the kind of behaviour you are looking for is to have the  
caller infer the type from the function and also have the function  
templated to work with any type that inherits from A. I have shown this in  
the following example with the extra step of not explicitly instantiating  
the template as that can also be inferred:

module temp;

import std.stdio;

class A
{
       void foo()
       {
             writefln("A.foo()");
       }
}

class B : A
{
       override void foo()
       {
             writefln("B.foo()");
       }
       void bar()
       {
             writefln("B.bar()");
       }
}

T func(T : A)(T a)
{
       //.. do stuff ..
       return a;
}

void main()
{
       auto thing = func(new B()); //same as auto thing = func!(B)(new B());
       thing.foo();
       thing.bar();

}


I'm not completly sure of how const/invariant work with object references  
so I won't attempt to answer that.
Mar 07 2009
parent Yigal Chripun <yigal100 gmail.com> writes:
Tim M wrote:
 What I was trying to say is that func needs to be called with an "A" but
 calling it with a "B" works because a "B" is a kind of "A". The function
 looses an information that it is a "B" returns it as an "A" which should
 be cast back to a "B" explicitly. This is corrct behavior as not all
 "A"s can be of kind "B".

 One way to get the kind of behaviour you are looking for is to have the
 caller infer the type from the function and also have the function
 templated to work with any type that inherits from A. I have shown this
 in the following example with the extra step of not explicitly
 instantiating the template as that can also be inferred:

 module temp;

 import std.stdio;

 class A
 {
 void foo()
 {
 writefln("A.foo()");
 }
 }

 class B : A
 {
 override void foo()
 {
 writefln("B.foo()");
 }
 void bar()
 {
 writefln("B.bar()");
 }
 }

 T func(T : A)(T a)
 {
 //.. do stuff ..
 return a;
 }

 void main()
 {
 auto thing = func(new B()); //same as auto thing = func!(B)(new B());
 thing.foo();
 thing.bar();

 }


 I'm not completly sure of how const/invariant work with object
 references so I won't attempt to answer that.
I think you missed my point. I used an example with classes as a generalization. remember that in D we have: const(T) /\ / \ / \ T invariant(T) basically I want to do: ref const(T) max(T)(ref const(T) a, ref const(T) b){ return (a>b)?a:b; } int a = 4; int b = 5; int res = max(a, b); //works due to "downcasting" from const(int) to int this can be limited to constancy only if it doesn't make sense for classes.
Mar 08 2009