Is opCast need, we have to!

foobar foo at bar.com
Wed Dec 1 13:43:56 PST 2010


Jesse Phillips Wrote:

> I'm not going to say it should be removed, but I'm just wondering if this is really needed anymore, or maybe to! should instead make use of it.
> 
> opCast provides a means to convert an object/struct of one type to another through an explicit cast operation. This is good as it allows generic code to perform conversions through a standard call, and the result is a usable type.
> 
> I would like to advocate the use of to!() more often in D code than cast(). It is much more robust, though has slightly different behavior (throws an exception on class conversions error).
> 
> The main issue I see is that we can now convert classes in two forms. To will make use of both on opCast function and a to function that is found in the class. (opCast taking precedence in to!). The behavior for using these are quite different, and be a reason to keep them distinct. In which case this is just an informative message about their behavior and I'll add it to a wiki.
> 
> The example below shows 3 things:
> 
>  * order of precedence is: opCast, cast, to
>  * defining opCast can disable casting to any type (its existence prevents normal casting operations from happening)
>  * the declared to!() function is not used when an opCast exists but doesn't match.
> 
> Maybe the last one should be fixed.
> 
> Here is the code:
> 
> import std.conv;
> 
> void main() {
>     B b = new B;
>     b.i = 5;
>     A ab = b;
> 
>     B bcast = cast(B) ab;
>     assert(b.i == bcast.i); // Fail: used opCast function
> 
>     B bto   = to!B(ab);
>     assert(b.i == bto.i); // Fail: used opCast function
> 
>     C c = new C;
>     c.i = 5;
>     A ac = c;
>     //C ccast = cast(C) ac; // Doesn't compile: uses opCast
>     //C cto   = to!C(ac); // Doesn't compile: uses opCast
>     //
>     // test.d(13): Error: template instance opCast!(C) 
>     //             does not match template declaration opCast(T) if (is(T == B))
>     //
>     //assert(c.i == ccast.i);
>     //assert(c.i == cto.i);
> 
>     E e = new E;
>     e.i = 5;
>     D de = e;
> 
>     E ecast = cast(E) de;
>     assert(e.i == ecast.i); // Pass: uses cast
> 
>     E eto   = to!E(de);
>     assert(e.i == eto.i); // Pass: uses cast 
> }
> 
> class A {
>     B opCast(T)() if(is(T == B)) {
>         return new B;
>     }
> 
>     C to(T)() if(is(T == C)) {
>         auto b = new B;
>         b.i = 5;
>         return b;
>     }
> }
> 
> class B : A {
>     int i;
> }
> 
> class C : A {
>     int i;
> }
> 
> class D {
>     E to(T)() if(is(T == E)) {
>         return new E;
>     }
> }
> 
> class E : D {
>     int i;
> }
> 


IMHO, coercions in D should be redesigned. They are a tiny bit better than C but C is a weekly (and poorly) typed language. I personally prefer the properly strongly typed ML family of languages.

My preference is as follows:
1. static_cast: 
  a. Remove ALL implicit coercions inherited from c such as double -> int,
  b. I don't see the need for an operator for conversions since they can have different parameters, e.g.:
  - converting to string can have formatting specified
  - converting string to numeric types with optional base parameter
  - converting integer to floating point specifies round/floor/ceiling/etc..
2. const_cast: should be a _separate_ operator in order to prevent removing const by mistake.
  const Base obj1 = new Derived();
  auto obj2 = cast(Derived)(obj1); // oops: meant to only down cast
3. dynamic_cast: the language should provide a down cast operator for OO. 
4. reinterpret_cast: unsafe and should be restricted as much as possible (Not available in @safe code) maybe not provide it at all since it's implementable via union. A restricted library solution for when you really want to play with bits and bytes? 

the above means that:
double pi = 3.14; 
int p = pi; // compile-time error
int p = floor(pi); // ok
and also:
int x = ??; // some number
double y = x; //compile-time error
double z = double(x); // explicit

double r = 5 / 2; // compile error
choose either:
a.  double r1 = double(5/2); // 2.0
b. double r2 = double (5) / 2; // 2.5




More information about the Digitalmars-d mailing list