digitalmars.D - user defined implicit casts
- Ender KaShae (186/186) Jul 27 2007 I have become aware that in D it is possible to create wrapper classes t...
- Tristam MacDonald (2/196) Jul 27 2007
- Jarrett Billingsley (8/12) Jul 28 2007 It was intentional. The problem with global or static (as in C#) operat...
- Daniel Keep (29/40) Jul 27 2007 Eww, ick. First of all, you're overloading on return type, *and* you've
- Ender KaShae (3/42) Jul 28 2007 That would be VERY useful
- Ender KaShae (2/15) Jul 30 2007 c++ has member, global, and static operator overloads it is up to the pr...
- Jarrett Billingsley (5/8) Jul 30 2007 Can you come up with something more solid than "this is how C++ does it?...
- Reiner Pope (13/24) Jul 30 2007 Non-member operator overloads are useful for the same reason that
- Tristam MacDonald (2/33) Jul 31 2007
- Jarrett Billingsley (8/16) Jul 31 2007 Can you generalize that to a List!(any integer type)? What if (I know t...
- Reiner Pope (49/58) Jul 31 2007 Not quite, but that's D's fault. The following very nearly works,
- Jarrett Billingsley (7/37) Jul 31 2007 Hm. I don't really like the idea of just allowing static/global operato...
- Ender KaShae (2/4) Aug 01 2007 it actually suprised me that D doesn't allow specialised templates the w...
- Tristam MacDonald (2/6) Aug 01 2007
- Jarrett Billingsley (6/14) Aug 01 2007 Ender,
- Tristam MacDonald (4/23) Aug 01 2007 The I should probably assume that I am causing the same problems?
- Jarrett Billingsley (5/9) Aug 01 2007 Sometimes it happens, sometimes it doesn't. Ender's reply to Tristam (t...
- Ender KaShae (5/10) Aug 02 2007 lets say you have an immutable fraction class, with D the way it is you ...
I have become aware that in D it is possible to create wrapper classes that can intermingle with primitave types for example: /** * wrappers for all the basic types (and derived types) of D * note: these wrappers are mutable */ module dragon.math.basictypes; import dragon.math.numbers; import std.math; import std.string; /// wrapper for byte class Byte: numeric!(Byte){ private byte value; /// value of instance public: this(byte b = 0){ value = b;} ///Constructor byte Value() {return value;} ///Returns: value of instance void Value(byte b) {value = b;} ///Params: b = new value byte opCast() {return value;} /// cast overload char[] toString() { return std.string.toString(value);} uint toHash() {return abs(value);} // binary operator TODO add documentation Byte opAdd(Byte b) { return new Byte(value + b.value);} Byte opAdd(byte b) { return new Byte(value + b);} Byte opSub(Byte b) { return new Byte(value - b.value);} Byte opSub(byte b) { return new Byte(value - b);} Byte opSub_r(byte b) { return new Byte(b - value);} Byte opMul(Byte b) { return new Byte(value * b.value);} Byte opMul(byte b) { return new Byte(value * b);} Byte opDiv(Byte b) { return new Byte(value / b.value);} Byte opDiv(byte b) { return new Byte(value / b);} Byte opDiv_r(byte b) {return new Byte(b / value);} Byte opMod(Byte b) {return new Byte(value % b.value);} Byte opMod(byte b) {return new Byte(value % b);} Byte opMod_r(byte b) { return new Byte(b % value);} Byte opAnd(Byte b) { return new Byte(value & b.value);} Byte opAnd(byte b) { return new Byte(value & b);} Byte opOr(Byte b) { return new Byte(value | b.value);} Byte opOr(byte b) { return new Byte(value | b);} Byte opXor(Byte b) { return new Byte(value ^ b.value);} Byte opXor(byte b) { return new Byte(value ^ b);} Byte opShl(int i) { return new Byte(value << i);} long opShl_r(long l) { return l << value;} Byte opShr(int i) {return new Byte(value >> i);} long opShr_r(long l) { return l >> value;} Byte opUShr(int i) {return new Byte(value >>> i);} long opUShr(long l) {return l >>> value;} //comparison operators int opEquals(Object o) { if(o is Byte) { Byte b = cast(Byte)o; return value == b.value; } return o == value; } int opEquals(long l) { return l = value; } int opCmp(Object o) { if(o is Byte) { Byte b = cast(Byte)o; if(value > b.value)return 1; if(value < b.value)return -1; return 0; } return o.opCmp(value); } int opCmp(long l) { if(value < l)return -1; if(value > l)return 1; return 0; } //assignment operators Byte opAssign(byte b) { value = b; return this; } Byte opAddAssign(Byte b) { value += b.value; return this; } Byte opAddAssign(byte b) { value += b; return this; } Byte opSubAssign(Byte b) { value -= b.value; return this; } Byte opSubAssign(byte b) { value -= b; return this; } Byte opMulAssign(Byte b) { value *= b.value; return this; } Byte opMulAssign(byte b) { value *= b; return this; } Byte opDivAssign(Byte b) { value /= b.value; return this; } Byte opDivAssign(byte b) { value /= b; return this; } Byte opModAssign(Byte b) { value %= b.value; return this; } Byte opModAssign(byte b) { value %= b; return this; } Byte opAndAssign(Byte b) { value &= b.value; return this; } Byte opAndAssign(byte b) { value &= b; return this; } Byte opOrAssign(Byte b) { value |= b.value; return this; } Byte opOrAssign(byte b) { value |= b; return this; } Byte opXorAssign(Byte b) { value ^= b.value; return this; } Byte opXorAssign(byte b) { value ^= b; return this; } Byte opShlAssign(Byte b) { value <<= b.value; return this; } Byte opShlAssign(byte b) { value <<= b; return this; } Byte opShrAssign(Byte b) { value >>= b.value; return this; } Byte opShrAssign(byte b) { value >>= b; return this; } Byte opUShrAssign(Byte b) { value >>>= b.value; return this; } Byte opUShrAssign(byte b) { value >>>= b; return this; } // unary operators TODO add documentation Byte opNeg() {return new Byte(-value);} Byte opPos() {return new Byte(+value);} Byte opCom() {return new Bye(~value);} Byte opPostInc() { scope temp = value; value++; return new Byte(temp); } Byte opPostDec() { scope temp = value; value--; return new Byte(temp); } } now the problem is a lack of implicit casting for user defined types the Byte cannot be implicitly cast to byte, int, long etc. even though such a cast would make sense. If implicit casting were allowed then a library could be fashioned with wrapper for all of the basic types and some less basic types such as cint, iint, fraction, radical, ifraction, cfraction, etc. my suggested syntax for overloading implicit casts is: implicit type1 cast(type2 var); where type1 is the type casting to and type2 is the type casting from this syntax would allow non-member functions to be used, so that implicit casts can be overloaded in specific contexts example: implicit int cast(long var){ return cast(int)var;} when you know that longs will never exceed the bound of ints
Jul 27 2007
While I would like to see this, I wopuld like to see a logical extension of the last part even more: non-member operator overloads. This is an incredibly neat feature from C++ that was lost somewhere along the way, but I don't know if it was intentional or not... Ender KaShae Wrote:I have become aware that in D it is possible to create wrapper classes that can intermingle with primitave types for example: /** * wrappers for all the basic types (and derived types) of D * note: these wrappers are mutable */ module dragon.math.basictypes; import dragon.math.numbers; import std.math; import std.string; /// wrapper for byte class Byte: numeric!(Byte){ private byte value; /// value of instance public: this(byte b = 0){ value = b;} ///Constructor byte Value() {return value;} ///Returns: value of instance void Value(byte b) {value = b;} ///Params: b = new value byte opCast() {return value;} /// cast overload char[] toString() { return std.string.toString(value);} uint toHash() {return abs(value);} // binary operator TODO add documentation Byte opAdd(Byte b) { return new Byte(value + b.value);} Byte opAdd(byte b) { return new Byte(value + b);} Byte opSub(Byte b) { return new Byte(value - b.value);} Byte opSub(byte b) { return new Byte(value - b);} Byte opSub_r(byte b) { return new Byte(b - value);} Byte opMul(Byte b) { return new Byte(value * b.value);} Byte opMul(byte b) { return new Byte(value * b);} Byte opDiv(Byte b) { return new Byte(value / b.value);} Byte opDiv(byte b) { return new Byte(value / b);} Byte opDiv_r(byte b) {return new Byte(b / value);} Byte opMod(Byte b) {return new Byte(value % b.value);} Byte opMod(byte b) {return new Byte(value % b);} Byte opMod_r(byte b) { return new Byte(b % value);} Byte opAnd(Byte b) { return new Byte(value & b.value);} Byte opAnd(byte b) { return new Byte(value & b);} Byte opOr(Byte b) { return new Byte(value | b.value);} Byte opOr(byte b) { return new Byte(value | b);} Byte opXor(Byte b) { return new Byte(value ^ b.value);} Byte opXor(byte b) { return new Byte(value ^ b);} Byte opShl(int i) { return new Byte(value << i);} long opShl_r(long l) { return l << value;} Byte opShr(int i) {return new Byte(value >> i);} long opShr_r(long l) { return l >> value;} Byte opUShr(int i) {return new Byte(value >>> i);} long opUShr(long l) {return l >>> value;} //comparison operators int opEquals(Object o) { if(o is Byte) { Byte b = cast(Byte)o; return value == b.value; } return o == value; } int opEquals(long l) { return l = value; } int opCmp(Object o) { if(o is Byte) { Byte b = cast(Byte)o; if(value > b.value)return 1; if(value < b.value)return -1; return 0; } return o.opCmp(value); } int opCmp(long l) { if(value < l)return -1; if(value > l)return 1; return 0; } //assignment operators Byte opAssign(byte b) { value = b; return this; } Byte opAddAssign(Byte b) { value += b.value; return this; } Byte opAddAssign(byte b) { value += b; return this; } Byte opSubAssign(Byte b) { value -= b.value; return this; } Byte opSubAssign(byte b) { value -= b; return this; } Byte opMulAssign(Byte b) { value *= b.value; return this; } Byte opMulAssign(byte b) { value *= b; return this; } Byte opDivAssign(Byte b) { value /= b.value; return this; } Byte opDivAssign(byte b) { value /= b; return this; } Byte opModAssign(Byte b) { value %= b.value; return this; } Byte opModAssign(byte b) { value %= b; return this; } Byte opAndAssign(Byte b) { value &= b.value; return this; } Byte opAndAssign(byte b) { value &= b; return this; } Byte opOrAssign(Byte b) { value |= b.value; return this; } Byte opOrAssign(byte b) { value |= b; return this; } Byte opXorAssign(Byte b) { value ^= b.value; return this; } Byte opXorAssign(byte b) { value ^= b; return this; } Byte opShlAssign(Byte b) { value <<= b.value; return this; } Byte opShlAssign(byte b) { value <<= b; return this; } Byte opShrAssign(Byte b) { value >>= b.value; return this; } Byte opShrAssign(byte b) { value >>= b; return this; } Byte opUShrAssign(Byte b) { value >>>= b.value; return this; } Byte opUShrAssign(byte b) { value >>>= b; return this; } // unary operators TODO add documentation Byte opNeg() {return new Byte(-value);} Byte opPos() {return new Byte(+value);} Byte opCom() {return new Bye(~value);} Byte opPostInc() { scope temp = value; value++; return new Byte(temp); } Byte opPostDec() { scope temp = value; value--; return new Byte(temp); } } now the problem is a lack of implicit casting for user defined types the Byte cannot be implicitly cast to byte, int, long etc. even though such a cast would make sense. If implicit casting were allowed then a library could be fashioned with wrapper for all of the basic types and some less basic types such as cint, iint, fraction, radical, ifraction, cfraction, etc. my suggested syntax for overloading implicit casts is: implicit type1 cast(type2 var); where type1 is the type casting to and type2 is the type casting from this syntax would allow non-member functions to be used, so that implicit casts can be overloaded in specific contexts example: implicit int cast(long var){ return cast(int)var;} when you know that longs will never exceed the bound of ints
Jul 27 2007
"Tristam MacDonald" <swiftcoder gmail.com> wrote in message news:f8dndh$1c8l$1 digitalmars.com...While I would like to see this, I wopuld like to see a logical extension of the last part even more: non-member operator overloads. This is an incredibly neat feature from C++ that was lost somewhere along the way, but I don't know if it was intentional or not...overloads is that they cannot be virtual, and so you end up writing the non-virtual operator overloads which simply call a virtual implementation method on the object(s), which is pointless -- just make the overload virtual in the first place, and it works fine. How would non-member operator overloads solve this problem anyway?
Jul 28 2007
Ender KaShae wrote:... now the problem is a lack of implicit casting for user defined types the Byte cannot be implicitly cast to byte, int, long etc. even though such a cast would make sense. If implicit casting were allowed then a library could be fashioned with wrapper for all of the basic types and some less basic types such as cint, iint, fraction, radical, ifraction, cfraction, etc. my suggested syntax for overloading implicit casts is: implicit type1 cast(type2 var); where type1 is the type casting to and type2 is the type casting fromEww, ick. First of all, you're overloading on return type, *and* you've introduced a new keyword. Sorry :) D already allows you to define an explicit casting overload like so: class Foo { type1 opCast() { ... } } What has been suggested a few times is to make a new operator overload that passes the result out using an out argument instead, which you *can* overload on: class Foo { void opImplicitCast(out type1) { ... } } On the issue of using a global function (as opposed to a member function), I'd rather get general type extensions, which would also provide this as a sort of side effect: bool isFoo(Object this) { return (cast(Foo)this) !is null; } auto bar = (new Foo).isFoo;this syntax would allow non-member functions to be used, so that implicit casts can be overloaded in specific contexts example: implicit int cast(long var){ return cast(int)var;} when you know that longs will never exceed the bound of intsOk, there's something very wrong about this. Altering how casts happen in particular contexts just strikes me as a very bad idea. The problem is that someone reading your code will need to go over it with a fine tooth comb to work out what casts are *actually* doing at any given line. That said, I *would* like to see implicit casts added to the language. -- Daniel
Jul 27 2007
Daniel Keep Wrote:uggested syntax for overloading implicit casts is:I like that much better than my suggestion, it's a lot cleanerimplicit type1 cast(type2 var); where type1 is the type casting to and type2 is the type casting fromEww, ick. First of all, you're overloading on return type, *and* you've introduced a new keyword. Sorry :) D already allows you to define an explicit casting overload like so: class Foo { type1 opCast() { ... } } What has been suggested a few times is to make a new operator overload that passes the result out using an out argument instead, which you *can* overload on: class Foo { void opImplicitCast(out type1) { ... } }On the issue of using a global function (as opposed to a member function), I'd rather get general type extensions, which would also provide this as a sort of side effect: bool isFoo(Object this) { return (cast(Foo)this) !is null; } auto bar = (new Foo).isFoo;That would be VERY usefulThat said, I *would* like to see implicit casts added to the language. -- Daniel
Jul 28 2007
Jarrett Billingsley Wrote:"Tristam MacDonald" <swiftcoder gmail.com> wrote in message news:f8dndh$1c8l$1 digitalmars.com...c++ has member, global, and static operator overloads it is up to the programmer to choose how to implement it, I think it would be nice if D had a similar mechanizm.While I would like to see this, I wopuld like to see a logical extension of the last part even more: non-member operator overloads. This is an incredibly neat feature from C++ that was lost somewhere along the way, but I don't know if it was intentional or not...overloads is that they cannot be virtual, and so you end up writing the non-virtual operator overloads which simply call a virtual implementation method on the object(s), which is pointless -- just make the overload virtual in the first place, and it works fine.
Jul 30 2007
"Ender KaShae" <astrothayne gmail.ckom> wrote in message news:f8m0k6$2oo9$1 digitalmars.com...c++ has member, global, and static operator overloads it is up to the programmer to choose how to implement it, I think it would be nice if D had a similar mechanizm.Can you come up with something more solid than "this is how C++ does it?" Can you explain what functionality global and static operator overloads provide that member operator overloads cannot?
Jul 30 2007
Jarrett Billingsley wrote:"Ender KaShae" <astrothayne gmail.ckom> wrote in message news:f8m0k6$2oo9$1 digitalmars.com...Non-member operator overloads are useful for the same reason that non-member functions are useful: writing functions that operate on a type, but don't belong within that type. I can think of one example where global operator overloads make sense: // stores a list of T, with opIndex, opCat, etc class List(T) { ... } In this case it might be reasonable to define a function which does a elementwise add of two List!(int)s to produce a third: List!(int) opAdd(List!(int) a, List!(int) b) { ... } But this operator clearly doesn't belong in the List class, as in the general case, you can't add two lists together. -- Reinerc++ has member, global, and static operator overloads it is up to the programmer to choose how to implement it, I think it would be nice if D had a similar mechanizm.Can you come up with something more solid than "this is how C++ does it?" Can you explain what functionality global and static operator overloads provide that member operator overloads cannot?
Jul 30 2007
It would be nice if this could be generalised to functions, rather than just operator overloads. Then we could define member functions for specific template instantiations without having to specialise the whole template, or clog the body with static ifs. Reiner Pope Wrote:Jarrett Billingsley wrote:"Ender KaShae" <astrothayne gmail.ckom> wrote in message news:f8m0k6$2oo9$1 digitalmars.com...Non-member operator overloads are useful for the same reason that non-member functions are useful: writing functions that operate on a type, but don't belong within that type. I can think of one example where global operator overloads make sense: // stores a list of T, with opIndex, opCat, etc class List(T) { ... } In this case it might be reasonable to define a function which does a elementwise add of two List!(int)s to produce a third: List!(int) opAdd(List!(int) a, List!(int) b) { ... } But this operator clearly doesn't belong in the List class, as in the general case, you can't add two lists together. -- Reinerc++ has member, global, and static operator overloads it is up to the programmer to choose how to implement it, I think it would be nice if D had a similar mechanizm.Can you come up with something more solid than "this is how C++ does it?" Can you explain what functionality global and static operator overloads provide that member operator overloads cannot?
Jul 31 2007
"Reiner Pope" <some address.com> wrote in message news:f8m84m$30eq$1 digitalmars.com...I can think of one example where global operator overloads make sense: // stores a list of T, with opIndex, opCat, etc class List(T) { ... } In this case it might be reasonable to define a function which does a elementwise add of two List!(int)s to produce a third: List!(int) opAdd(List!(int) a, List!(int) b) { ... } But this operator clearly doesn't belong in the List class, as in the general case, you can't add two lists together.Can you generalize that to a List!(any integer type)? What if (I know the chances are slim but bear with me) you created a class derived from List which wanted to change the behavior of opAdd? And is there anything that you absolutely cannot do with member operator overloads, or problems which static/global operator overloads solve more elegantly than member operator overloads?
Jul 31 2007
Jarrett Billingsley wrote:Can you generalize that to a List!(any integer type)?Not quite, but that's D's fault. The following very nearly works, though, but it doesn't have a specialization on T. If it did, IFTI would break (which, as far as I see, is D's fault, and not something fundamental). List!(T) opAdd(T)(List!(T) a, List!(T) b) { ... } But anyway, the same problem holds if you want to write a templated accumulate function. The problem is in template specialization, not in the global operator overloading.What if (I know the chances are slim but bear with me) you created a class derived from List which wanted to change the behavior of opAdd?I hadn't thought of this situation. But I don't see anything wrong with the most obvious solution: if a class C has an operator overloaded, for instance opAdd, then only the members are considered in overloading for C.opAdd. If not, then consider globals. I think this situation is very similar to the pseudo-member syntax that many people (including me) are keen on, to generalize the implicit properties for arrays to all types. The same problem arises there, and I would suggest the same solution.And is there anything that you absolutely cannot do with member operator overloads, or problems which static/global operator overloads solve more elegantly than member operator overloads?I think the example I gave is one such. Supposing that there is a way to generalise my example to any iteratable list of any numeric type, then I would say that making it a global is the best design. It makes the algorithm composable, independent of the underlying type (like STL). The alternative is to write: template OpAddImpl(T, ThisType) { static if (IsAddable!(T)) { ThisType opAdd(ThisType other) { ... } } } class List(T) { ... mixin OpAddImpl!(T, typeof(this)); } class Vector(T) { ... mixin OpAddImpl!(T, typeof(this)); } // and the same for all the other list-looking classes You could also try to use inheritance, but then you lose information about the return type, and it's also not currently possible with interfaces since they don't allow implementing even final member functions. -- Reiner
Jul 31 2007
"Reiner Pope" <some address.com> wrote in message news:f8oct2$7le$1 digitalmars.com...I think this situation is very similar to the pseudo-member syntax that many people (including me) are keen on, to generalize the implicit properties for arrays to all types. The same problem arises there, and I would suggest the same solution.Hm. I don't really like the idea of just allowing static/global operator overloads in isolation, but if they were allowed as another aspect of this generalized pseudo-member syntax, it would make much more sense.I think the example I gave is one such. Supposing that there is a way to generalise my example to any iteratable list of any numeric type, then I would say that making it a global is the best design. It makes the algorithm composable, independent of the underlying type (like STL). The alternative is to write: template OpAddImpl(T, ThisType) { static if (IsAddable!(T)) { ThisType opAdd(ThisType other) { ... } } } class List(T) { ... mixin OpAddImpl!(T, typeof(this)); } class Vector(T) { ... mixin OpAddImpl!(T, typeof(this)); } // and the same for all the other list-looking classesI see what you're getting at now, and I agree that that would be very useful.
Jul 31 2007
Tristam MacDonald Wrote:It would be nice if this could be generalised to functions, rather than just operator overloads. Then we could define member functions for specific template instantiations without having to specialise the whole template, or clog the body with static ifs.it actually suprised me that D doesn't allow specialised templates the way c++ does
Aug 01 2007
The problem being that I can figure otu quite *what* C++ will do in a specialisation. Sometimes the specialisation will correctly inherit all the original members and sometimes not, sometimes I can replace members and sometimes not, so most of the time I must completely re-write the entire template for each specialisation (as stdc++ does in most cases). Ender KaShae Wrote:Tristam MacDonald Wrote:It would be nice if this could be generalised to functions, rather than just operator overloads. Then we could define member functions for specific template instantiations without having to specialise the whole template, or clog the body with static ifs.it actually suprised me that D doesn't allow specialised templates the way c++ does
Aug 01 2007
"Ender KaShae" <astrothayne gmail.com> wrote in message news:f8qidi$kq1$1 digitalmars.com...Tristam MacDonald Wrote:Ender, Please use a newsreader to read and reply to topics on these newsgroups. There is a bug in the web interface that messes up threading, and it makes all your topics very hard to follow.It would be nice if this could be generalised to functions, rather than just operator overloads. Then we could define member functions for specific template instantiations without having to specialise the whole template, or clog the body with static ifs.it actually suprised me that D doesn't allow specialised templates the way c++ does
Aug 01 2007
The I should probably assume that I am causing the same problems? (I also use the web interface) I don't *see* anything wrong with the threading, but I may not know what to look for. Jarrett Billingsley Wrote:"Ender KaShae" <astrothayne gmail.com> wrote in message news:f8qidi$kq1$1 digitalmars.com...Tristam MacDonald Wrote:Ender, Please use a newsreader to read and reply to topics on these newsgroups. There is a bug in the web interface that messes up threading, and it makes all your topics very hard to follow.It would be nice if this could be generalised to functions, rather than just operator overloads. Then we could define member functions for specific template instantiations without having to specialise the whole template, or clog the body with static ifs.it actually suprised me that D doesn't allow specialised templates the way c++ does
Aug 01 2007
"Tristam MacDonald" <swiftcoder gmail.com> wrote in message news:f8qn7h$sf7$1 digitalmars.com...The I should probably assume that I am causing the same problems? (I also use the web interface) I don't *see* anything wrong with the threading, but I may not know what to look for.Sometimes it happens, sometimes it doesn't. Ender's reply to Tristam (this reply) ended up not as a reply to Tristam but as a reply to the OP. Your reply, though, showed up in the right place.
Aug 01 2007
Jarrett Billingsley Wrote:Can you come up with something more solid than "this is how C++ does it?" Can you explain what functionality global and static operator overloads provide that member operator overloads cannot?lets say you have an immutable fraction class, with D the way it is you can't overload any op=, =, ++, or --, and maintain immutability with nonmember/static operator overloads you can as such: static Fraction opAddAssign(ref Fraction first, Fraction second){...} where first is assigned a new instance of Fraction
Aug 02 2007