Safer casts

Janice Caron caron800 at googlemail.com
Sun May 11 09:50:44 PDT 2008


On 11/05/2008, Yigal Chripun <yigal100 at gmail.com> wrote:
>  > So maybe that brings us to this:
>  >
>  >     cast(T)x // dynamic_cast
>  >     cast!(T)x // reinterpret_cast
>  >     static cast(T)x // static_cast
>  >     const cast(T)x // const_cast
>  >
>  > That does have a certain appeal to it.
>
>  didn't you yourself complain that cast(T) can remove
>  constancy by mistake and create bugs?

Yes, which is why I now suggest

    void foo(in C c)
    {
        C c = cast(C)c; /* ERROR - cast cannot remove constancy */
        C d = const cast(C)c /* OK */
    }


>  why would you allow cast!(T) to do just that?

As in...

    void foo(in C c)
    {
        C c = cast!(C)c; /* OK */
    }

Because the exclamation mark means "allow anything" (but you're only
ever reinterpreting bits, not assigning new ones).

Reinterpret cast would most often be used for casting void* to T*, for
some T, but it could just as easily be used to interpret T* as U*, or
(for classes) C as D. Since the source type may have a completely
different memory layout from the destination type, there is, in
general, no way for the compiler to determine whether or not constancy
has changed. (e.g. you could be casting from a pointer to an intptr_t,
or vice versa). In short, this is the most dangerous form of cast of
all, and should only ever be used when nothing else will work, and
even then only if you're damn sure you need to circumvent the normal
rules.

(But since D is a systems programming language, this kind of
super-dangerous casting must be allowed. It just shouldn't be the
default. To my mind, the exclamation mark says that).

Bear in mind that even without reinterpret cast, you will always be
able to achieve the same thing with unions. Watch:

    DstType reinterpret_cast(DstType, SrcType)(SrcType x)
    {
        union U
        {
            SrcType src;
            DstType dst;
        }

        U u;
        u.src = x;
        return u.dst;
    }

In other words, if you ban reinterpret cast, someone's only going to
do it anyway.

Ummm ... wait a minute ... !

I think I just found a reason /not/ to have a reinterpret cast in the
language - the reason being, it can be done in a library function, as
I just showed. Hey, I think that means we don't need that one at all.
Hmm...



>  Also, My definition was that cast() can change the memory while cast!()
>  doesn't. it seems you define them in reverse of that.

Well, not quite. In C++, only static_cast<T>(x) can change the memory
(but it might not), wheras I suggest that in D, cast(T)x (i.e. the
default cast) should just "do the sensible thing" - sort of like it
does now, except with the dangerous cases removed - so it would act
like dynamic_cast<T>(x) where appropriate, and static_cast<T>(x) where
dynamic_cast<T> would be inappropriate. So, sometimes it will change
memory, sometimes not. But const cast and cast! would never change
memory.

Examples:

    double d;
    int x = cast(int)d; // creates new int

    class A {}
    class B : A {}
    A a = new B;

    B b = cast(B)a;
        // dynamic cast. a and b point to same object

    C b = static cast(B)a;
        // as above, but without the runtime check that a is really a B

    class D {}
    const d = new D;

    D d2 = cast(D)d; // ERROR - cannot remove constancy
    D d2 = const cast(D)d; // OK

    D d3 = cast(D)a; // ERROR - can't convert A to D
    D d3 = static cast(D)a; // ERROR - can't convert A to D
    D d3 = const cast(D)a; // ERROR - can't convert A to D
    D d3 = cast!(D)a; // OK - but you'd better know what you're doing!



More information about the Digitalmars-d mailing list