www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Idea: A Better opCast

reply pragma <pragma_member pathlink.com> writes:
Quoth the manual: "Since functions cannot be overloaded based on return value,
there can be only one opCast per struct or class."

Since we only have one opCast to burn, why not change its signature to something
more general purpose?

 void* opCast(TypeInfo from);

I came across this idea after I saw how cast() is actually implemented in phobos. In this case, opCast() could take the place of both _d_interface_cast *and* _d_dynamic_cast as "this" already provides the needed from pointer. Example: class Tester{ void* opCast(TypeInfo from){ else if(from = typeid(int)){ int value = 28; return cast(void*)value; // mimic the example in the docs. } return null; } } The above method has a *lot* more power than the current use of opCast(). I"ll also add that we're already used to working with TypeInfo and typeid() thanks to _arguments (varargs), so this doesn't clash with what we have already. I would anticipate that opCast() only be called when a normal dynamic cast or interface cast fails. This way, the above defintion will interoperate with a class' normal casting behavior. - EricAnderton at yahoo
Mar 31 2005
next sibling parent reply "Jamboree" <jam bor.ee> writes:
"pragma" <pragma_member pathlink.com> wrote in message
news:d2i7a1$2ase$1 digitaldaemon.com...
 Quoth the manual: "Since functions cannot be overloaded based on return value,
 there can be only one opCast per struct or class."

 Since we only have one opCast to burn, why not change its signature to
something
 more general purpose?

 void* opCast(TypeInfo from);

I came across this idea after I saw how cast() is actually implemented in phobos. In this case, opCast() could take the place of both _d_interface_cast *and* _d_dynamic_cast as "this" already provides the needed from pointer. Example: class Tester{ void* opCast(TypeInfo from){ else if(from = typeid(int)){ int value = 28; return cast(void*)value; // mimic the example in the docs. } return null; } } The above method has a *lot* more power than the current use of opCast(). I"ll also add that we're already used to working with TypeInfo and typeid() thanks to _arguments (varargs), so this doesn't clash with what we have already. I would anticipate that opCast() only be called when a normal dynamic cast or interface cast fails. This way, the above defintion will interoperate with a class' normal casting behavior.

It's elegant, but it'd only result in runtime errors, which would loose a lot of very useful compile-time checking. D's not Python. :-)
Mar 31 2005
parent pragma <pragma_member pathlink.com> writes:
void* opCast(TypeInfo *to);

In article <d2i83d$2bh6$1 digitaldaemon.com>, Jamboree says...
It's elegant, but it'd only result in runtime errors, which would loose a lot
of very useful compile-time checking.

I agree that improper use of this kind of casting mechanism could lead to issues. However, this is done with already existing language constructs, so the power to screw things up as badly alrady exists for every other portion of the language. Also, keep in mind that we already do this with varargs: they're just about as type-unsafe when you get down to it.
D's not Python. :-)

Agreed. But my motivation for this goes outside making types more 'plastic' or 'dynamic' as they are in scripting languages. Believe me when I say that I want to better enhance D's type system so it can be more *consistent*. I think this can help. For example, casts fail when types are used between dlls and their host programs (different TypeInfo trees make vanilla cast() fail). If an opCast override like this were available, I could do this on every type exported from a dll:
 void* opCast(TypeInfo *to){
     return classname_cast(this,to); // cast based on name instead of typeid
 }

.. where 'classname_cast' performs the real magic of resolving parallel TypeInfo trees by using classnames instead of TypeInfo references. I'm sure there's other practical uses for an enhanced opCast() as well. - EricAnderton at yahoo
Apr 01 2005
prev sibling next sibling parent reply "Regan Heath" <regan netwin.co.nz> writes:
On Fri, 1 Apr 2005 01:14:41 +0000 (UTC), pragma  
<pragma_member pathlink.com> wrote:
 Quoth the manual: "Since functions cannot be overloaded based on return  
 value,
 there can be only one opCast per struct or class."

 Since we only have one opCast to burn, why not change its signature to  
 something
 more general purpose?

 void* opCast(TypeInfo from);

I came across this idea after I saw how cast() is actually implemented in phobos. In this case, opCast() could take the place of both _d_interface_cast *and* _d_dynamic_cast as "this" already provides the needed from pointer. Example: class Tester{ void* opCast(TypeInfo from){ else if(from = typeid(int)){ int value = 28; return cast(void*)value; // mimic the example in the docs. } return null; } } The above method has a *lot* more power than the current use of opCast(). I"ll also add that we're already used to working with TypeInfo and typeid() thanks to _arguments (varargs), so this doesn't clash with what we have already. I would anticipate that opCast() only be called when a normal dynamic cast or interface cast fails. This way, the above defintion will interoperate with a class' normal casting behavior.

Did you actually mean "void* opCast(TypeInfo from);" or should it be "void* opCast(TypeInfo to);". Your example seems to be casting "to" the type not "from" the type. Regan
Mar 31 2005
parent pragma <pragma_member pathlink.com> writes:
In article <opsoi75p0x23k2f5 nrage.netwin.co.nz>, Regan Heath says...
Did you actually mean "void* opCast(TypeInfo from);" or should it be  
"void* opCast(TypeInfo to);". Your example seems to be casting "to" the  
type not "from" the type.

You're right, it's 'to', not 'from'. That's what I get for not being more thourough before posting. - EricAnderton at yahoo
Apr 01 2005
prev sibling next sibling parent reply "Ben Hinkle" <ben.hinkle gmail.com> writes:
 Example:

 class Tester{
 void* opCast(TypeInfo from){
 else if(from = typeid(int)){
 int value = 28;
 return cast(void*)value; // mimic the example in the docs.
 }
 return null;
 }
 }

I think the return type will be a problem. The example in the doc illstrates casting to a long by going through an int. If opCast is given the type to cast to how can it know how big the output should be? For example it's not a good idea in your example to cast the int 28 to a pointer. Maybe &value but then it's a reference to a local variable which will be bogus after returning. The generality of passing in the type to cast to is cool. I hope the details can be worked out. It's kindof like dynamic casting but to anything - not just between class types.
 I would anticipate that opCast() only be called when a normal dynamic cast 
 or
 interface cast fails.  This way, the above defintion will interoperate 
 with a
 class' normal casting behavior.

I don't follow this part. How would opCast interact with normal casting?
Mar 31 2005
parent pragma <pragma_member pathlink.com> writes:
A correction that was mentioned earlier, the method signature should be:

void* opCast(TypeInfo to);

In article <d2ijpp$2pgb$1 digitaldaemon.com>, Ben Hinkle says...
I think the return type will be a problem. The example in the doc illstrates 
casting to a long by going through an int. If opCast is given the type to 
cast to how can it know how big the output should be? For example it's not a 
good idea in your example to cast the int 28 to a pointer. Maybe &value but 
then it's a reference to a local variable which will be bogus after 
returning. The generality of passing in the type to cast to is cool. I hope 
the details can be worked out. It's kindof like dynamic casting but to 
anything - not just between class types.

Thanks for thinking that it might be plausable for D. As for the size, the example above is (for now) a what-if. I'm not sure how primitives should be returned, but I would imagine that casting any value to it's closest size_t size representation (be it the actual value or a pointer) may be what's called-for. I can already forsee problems with types like cent. As you say, I hope these details can be worked out.
 I would anticipate that opCast() only be called when a normal dynamic cast 
 or
 interface cast fails.  This way, the above defintion will interoperate 
 with a
 class' normal casting behavior.

I don't follow this part. How would opCast interact with normal casting?

Given the following: A a = new A(); int x = cast(int)a; B b = cast(B)a; // A and B are not related The compiler would generate code that does this: A a = new A(); int x = a.opCast(typeid(int)); B temp = _d_interface_cast(a,typeid(B)); // use a standard cast b = temp ? temp : a.opCast(typeid(B)); // use the operator on failure For all I know, the current implementation opCast() already does this. I thought I'd include this bit as the order of operation between a class' normal casting rules and the opCast override. However, I'll add that it might actually make more sense to reverse the order of operation so that opCast behaves more like an override instead. - EricAnderton at yahoo
Apr 01 2005
prev sibling parent Stewart Gordon <smjg_1998 yahoo.com> writes:
pragma wrote:
 Quoth the manual: "Since functions cannot be overloaded based on return value,
 there can be only one opCast per struct or class."
 
 Since we only have one opCast to burn, why not change its signature to
something
 more general purpose?

 class Tester{
 void* opCast(TypeInfo from){
 else if(from = typeid(int)){
 int value = 28;
 return cast(void*)value; // mimic the example in the docs.
 }
 return null;
 }
 }

Problems: (a) It's rather down and dirty. (b) What sense does it make to have a pointer to the memory address denoted by the number 28? (c) I'm not sure how efficient it would be generally. (d) D has enough features for postponing compile-time errors to the runtime already. Of the ideas suggested already, my vote would go to void opCast(out T value) Though I've just another idea involving a special syntax for cast functions. How about this? T cast() { ... } I thought of it as it looks just like an ordinary function definition, the difference being that 'cast' is a keyword. As such, one cannot call the function directly, but only by using a CastExpression. You could see it as a special function name that allows return type overloading. Though really, the 'cast' keyword is a placeholder, rather like 'this' for constructors/destructors. Of course the functions would have different names internally, using the name mangling convention in some form to indicate the destination type. Stewart. -- My e-mail is valid but not my primary mailbox. Please keep replies on on the 'group where everyone may benefit.
Apr 03 2005