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