Safer casts
Janice Caron
caron800 at googlemail.com
Sun May 11 17:28:46 PDT 2008
On 12/05/2008, Yigal Chripun <yigal100 at gmail.com> wrote:
> if [re]interpret_cast can be implemented by the user and it is quite
> dangerous, than i think people who need that should implement that
> themselves with union as you showed. no need for D to provide that
> functionality by default.
Agreed. As a general principle, if it can be implemented in a library,
then there's no need for new syntax. /However/ - it /should/ be
implemented in a library, because we'd want to outlaw
void* p;
char* q = cast(char*) p; /* ERROR -- cannot cast from void* to char* */
reinterpret_cast should be the only kind of cast to allow this:
char* q = reinterpret_cast!(char*)(p); /* OK */
[EDIT - see below]
> I'm still not sure about constancy casts. but in order to provide the
> minimal solution, I'd prefer cast!(T) to do constancy casts over adding
> another construct for that.
OK.
> also, I don't like the static cast(), and it
> might cause ambiguity problems. what is this good for?
There can't be any ambiguity problems (yet), because you can't (yet)
call functions which take a "this" parameter at compile time. If you
could, then "static cast(int)x", might invoke typeof(x)'s opCast()
function, so arguably there might be ambiguity problems in the future
-- if you're entering an obfuscated D contest! Let's ignore that for
now.
What's it good for? Performance. If B is a subclass of A, and I *know*
that my A is actualy a B, static_cast<B> allows me avoid the runtime
penalty that I'd get with dynamic_cast<B>. There would also be no need
to check the return value, because static_cast always succeeds.
Obviously, reinterpret_cast<B> will work everywhere that
static_cast<B> works, but static_cast<B>(a) gets me a compile-time
check that typeof(a) is a subclass or superclass of B, wheras with
reinterpret_cast<B>(a) there's no compile-time checking at all.
static_cast<T> can also do plain conversion, e.g.
double d;
int n = static_cast<int>(d);
So, if the old-fashioned default cast were ever outlawed in C++,
static_cast is the one you'd use to replace it for "plain" casts.
However - given that we choose to define cast(T) to mean "if T is a
class then use RTTI, otherwise don't", then, for non-classes, our
cast(T) achieves what static_cast<T> does for C++. For classes ...
well, who needs static cast anyway? Let's ditch it.
So that leaves us with:
cast(T)x // (T is a class) ? dynamic_cast : static_cast
cast!(T)x // const_cast
reinterpret_cast!(T)(x) // reinterpret_cast, implemented in library
That pretty much covers it.
> open question: if this proposal does not provide a reinterpret_cast, can
> we split the cast!(T) to perform either a type change /or/ a constancy
> change?
I don't see how that could work in general.
We could allow it for specific cases though - e.g casting from void*
to T*, or from void[] to T[], etc. I think in those special cases, it
could work.
So, to turn an S* into a T* (where both are structs), you would do
S* s = cast!(S*)cast(void*)t
> // below case uses the same bit pattern, exception on invalid double
> // value
> auto d2 = cast!(double)n;
I don't like that, and I see no need for it. reinterpret_cast!(double)
can do that job. And there's no need for exception throwing - that
would entail a runtime check, and at this level, we must assume the
programmer knows what they're doing.
> same goes for classes with user defined cast and cast! operators.
There should never be any such thing as a user-defined cast! operator.
It's meaningless. Look at it like this - C++ has four different kinds
of cast, but only one kind of cast operator. It's all you need.
> downcast can be treated as a compiler pre-defined conversion from a
> class to its derived classes. uses the cast(Derived) form and throws
> exception on error.
It's not an error to fail an RTTI cast! e.g.
void f(A a)
{
B1 b1 = cast(B1)a;
if (b1 !is null) { ... }
B2 b2 = cast(B2)a;
if (b2 !is null) { ... }
B3 b3 = cast(B3)a;
if (b3 !is null) { ... }
}
So no need for throwing exceptions.
> constancy is performed via cast!(T).
OK.
> pointers are cast with cast!(T)
Only pointers to void.
> so to convert const(int)* to const(long*) you'll need something like:
> auto res = cast!(const(long*))cast(const(long)*)x;
I would have said
cast!(const(long*))cast(const(void)*)x;
or
reinterpret_cast!(const(long*))x;
More information about the Digitalmars-d
mailing list