digitalmars.D - typedefs are useless
- Steven Schveighoffer (28/28) Dec 03 2007 This may have arisen somewhere before, but...
- Kirk McDonald (7/52) Dec 03 2007 You can typedef things other than integral types.
- Steven Schveighoffer (18/19) Dec 03 2007 OK, so that is one difference between enum and typedef (I forgot about t...
-
Matti Niemenmaa
(6/14)
Dec 03 2007
- BCS (19/23) Dec 03 2007 [...]
- Bill Baxter (7/34) Dec 03 2007 Why not just make that behavior triggered when you do
- Steven Schveighoffer (12/46) Dec 04 2007 FWIW, you can do stuff like this today by using a struct with a single
- Simen Kjaeraas (227/256) Dec 04 2007 Have a template, it's on the house.
- Steven Schveighoffer (11/12) Dec 04 2007 Thank you for the code.
- Peter C. Chapin (19/30) Dec 03 2007 FWIW, Ada solves this problem by considering literals in a special type
- John Demme (6/45) Dec 03 2007 Behavior like this would come about as a result of polysemous values bei...
- BLS (9/48) Dec 04 2007 IMO, in this case ADA is slicker than D.
- Peter C. Chapin (10/14) Dec 04 2007 Actually that's
- BLS (5/16) Dec 04 2007 Thanks Peter, that's it.
This may have arisen somewhere before, but... Let's say I want a way to create a type that's like a long, but is not implicitly convertable from a long. I can do: typedef long mytype; However, I can't create literals of this type. so if I want to initialize a mytype value to 6, I have to do: mytype x = cast(mytype)6L; And if I want to scale a mytype value, I can do: x *= 6; but if I wanted to create a scaled value of another mytype value, this is illegal: x = y * 6; So in order to get this to work, I have to do: x = y * cast(mytype)6; which is really cumersome, when what I want is a integral type that is semantically different from the integral type it's based on. But I want literals of the base type to implicitly convert to my type so I don't have these ugly casts. Note that an enum is EXACTLY the same as a typedef, with the added feature that an enum can have const values of itself accessible as members. In that regard, why would anyone use a typedef when it is a less-functional version of an enum? in short, I would like the compiler to auto promote a literal to a typedef that was based on that literal's type. Not sure if this is feasible, but without something like this, there is no reason to have a typedef keyword. I can use enum instead. -Steve
Dec 03 2007
Steven Schveighoffer wrote:This may have arisen somewhere before, but... Let's say I want a way to create a type that's like a long, but is not implicitly convertable from a long. I can do: typedef long mytype; However, I can't create literals of this type. so if I want to initialize a mytype value to 6, I have to do: mytype x = cast(mytype)6L; And if I want to scale a mytype value, I can do: x *= 6; but if I wanted to create a scaled value of another mytype value, this is illegal: x = y * 6; So in order to get this to work, I have to do: x = y * cast(mytype)6; which is really cumersome, when what I want is a integral type that is semantically different from the integral type it's based on. But I want literals of the base type to implicitly convert to my type so I don't have these ugly casts. Note that an enum is EXACTLY the same as a typedef, with the added feature that an enum can have const values of itself accessible as members. In that regard, why would anyone use a typedef when it is a less-functional version of an enum? in short, I would like the compiler to auto promote a literal to a typedef that was based on that literal's type. Not sure if this is feasible, but without something like this, there is no reason to have a typedef keyword. I can use enum instead. -SteveYou can typedef things other than integral types. -- Kirk McDonald http://kirkmcdonald.blogspot.com Pyd: Connecting D and Python http://pyd.dsource.org
Dec 03 2007
"Kirk McDonald" wroteYou can typedef things other than integral types.OK, so that is one difference between enum and typedef (I forgot about that, because I was so narrowly focused on my problem at hand). but the same thing can be said for non-integral types: typedef mytype float; mytype x = 1.0 // does not work. This typedef is just as useless as an integral typedef. The fact that enum does not exist for a floating point type does not justify the existance of typedef. You can use alias to make a more useful type, but you then do not have the restrictions I am looking for. I look at a typedef as a useful way to create a derived type from a builtin type such that it is implicitly convertable to the base type, but not in reverse (similar to deriving from a base class). However, without the ability to specify literals, or even extend the syntax to be able to specify them (i.e. some way to make 1mt mean cast(mytype)1), the type is only useful as an enumeration, as any mathematical manipulation will require lots of casting statements, just like an enum. -Steve
Dec 03 2007
Steven Schveighoffer wrote:I can do: typedef long mytype; However, I can't create literals of this type. so if I want to initialize a mytype value to 6, I have to do: mytype x = cast(mytype)6L;<snip> Complained about in Issue 1335 and earlier. This is why I use aliases instead of typedefs in all but the shortest of programs. -- E-mail address: matti.niemenmaa+news, domain is iki (DOT) fi
Dec 03 2007
Steven Schveighoffer wrote:This may have arisen somewhere before, but... Let's say I want a way to create a type that's like a long, but is not implicitly convertable from a long.[...] this is getting really close to something I have wanted for some time: typedef real MyReal { // stuff } it would be like a struct that inherits from a primitive type. The this would be of the base type and you wouldn't be allowed to add any members. However this would allow you to do things like overload the operators. The one things in particular I would like to do would be to overload just the typing of the operators. This would result in the implementation of + (for instance) being the same as + on the underlying type, but the type of the result would be redefined. The point of this would be to allow a program to restrict the type that could be used. A concrete use case would be a SIUinits type that would, at compile time, verify unit correctness, but in the end would result in exactly the same code as if bare FP values were used. Thoughts?
Dec 03 2007
BCS wrote:Steven Schveighoffer wrote:Why not just make that behavior triggered when you do struct MyReal : real { ... } ?This may have arisen somewhere before, but... Let's say I want a way to create a type that's like a long, but is not implicitly convertable from a long.[...] this is getting really close to something I have wanted for some time: typedef real MyReal { // stuff } it would be like a struct that inherits from a primitive type. The this would be of the base type and you wouldn't be allowed to add any members. However this would allow you to do things like overload the operators. The one things in particular I would like to do would be to overload just the typing of the operators. This would result in the implementation of + (for instance) being the same as + on the underlying type, but the type of the result would be redefined. The point of this would be to allow a program to restrict the type that could be used. A concrete use case would be a SIUinits type that would, at compile time, verify unit correctness, but in the end would result in exactly the same code as if bare FP values were used. Thoughts?
Dec 03 2007
"Bill Baxter" wroteBCS wrote:FWIW, you can do stuff like this today by using a struct with a single member of the base type you want, but you must define all the operators, but there is no implicit cast back to the base type. A struct is POD, so it occupies the same space as the single member, so you get no loss of efficiency there. The other problem is that operators can't be used in constant expressions (which is another pet peeve I have about this). Using a typedef would allow operators to be used. I want to say I really like BCS's idea as it really allows you to redefine all aspects of a typedef. This would be perfect for what I am trying to do. -SteveSteven Schveighoffer wrote:Why not just make that behavior triggered when you do struct MyReal : real { ... } ?This may have arisen somewhere before, but... Let's say I want a way to create a type that's like a long, but is not implicitly convertable from a long.[...] this is getting really close to something I have wanted for some time: typedef real MyReal { // stuff } it would be like a struct that inherits from a primitive type. The this would be of the base type and you wouldn't be allowed to add any members. However this would allow you to do things like overload the operators. The one things in particular I would like to do would be to overload just the typing of the operators. This would result in the implementation of + (for instance) being the same as + on the underlying type, but the type of the result would be redefined. The point of this would be to allow a program to restrict the type that could be used. A concrete use case would be a SIUinits type that would, at compile time, verify unit correctness, but in the end would result in exactly the same code as if bare FP values were used. Thoughts?
Dec 04 2007
Steven Schveighoffer Wrote:FWIW, you can do stuff like this today by using a struct with a single member of the base type you want, but you must define all the operators, but there is no implicit cast back to the base type. A struct is POD, so it occupies the same space as the single member, so you get no loss of efficiency there. The other problem is that operators can't be used in constant expressions (which is another pet peeve I have about this). Using a typedef would allow operators to be used. I want to say I really like BCS's idea as it really allows you to redefine all aspects of a typedef. This would be perfect for what I am trying to do. -SteveOn Tue, 04 Dec 2007 15:38:40 +0100, Steven Schveighoffer <schveiguy yahoo.com> wrote:FWIW, you can do stuff like this today by using a struct with a single member of the base type you want, but you must define all the operators, but there is no implicit cast back to the base type. A struct is POD, so it occupies the same space as the single member, so you get no loss of efficiency there. The other problem is that operators can't be used in constant expressions (which is another pet peeve I have about this). Using a typedef would allow operators to be used. I want to say I really like BCS's idea as it really allows you to redefine all aspects of a typedef. This would be perfect for what I am trying to do. -SteveHave a template, it's on the house. // start code template TypeDef(T) { T value; typeof(this) opNeg() { typeof(this) tmp; tmp.value = -this.value; return tmp; } typeof(this) opPostInc() { typeof(this) tmp; tmp.value = value++; return tmp; } typeof(this) opPostDec() { typeof(this) tmp; tmp.value = value--; return tmp; } T opCast() { return value; } static typeof(this) opCall(T rhs) { typeof(this) tmp; tmp.value = rhs; return tmp; } T opCall() // ugly hack to simulate implicit cast { return value; } typeof(this) opAdd(T rhs) { typeof(this) tmp; tmp.value = value + rhs; return tmp; } typeof(this) opSub(T rhs) { typeof(this) tmp; tmp.value = value + rhs; return tmp; } typeof(this) opMul(T rhs) { typeof(this) tmp; tmp.value = value * rhs; return tmp; } typeof(this) opDiv(T rhs) { typeof(this) tmp; tmp.value = value / rhs; return tmp; } typeof(this) opMod(T rhs) { typeof(this) tmp; tmp.value = value % rhs; return tmp; } typeof(this) opAssign(T rhs) { value = rhs; return this; } typeof(this) opAddAssign(T rhs) { value += rhs; return this; } typeof(this) opSubAssign(T rhs) { value -= rhs; return this; } typeof(this) opMulAssign(T rhs) { value *= rhs; return this; } typeof(this) opDivAssign(T rhs) { value /= rhs; return this; } typeof(this) opModAssign(T rhs) { value %= rhs; return this; } typeof(this) opAdd(typeof(this) rhs) { typeof(this) tmp; tmp.value = value + rhs.value; return tmp; } typeof(this) opSub(typeof(this) rhs) { typeof(this) tmp; tmp.value = value + rhs.value; return tmp; } typeof(this) opMul(typeof(this) rhs) { typeof(this) tmp; tmp.value = value * rhs.value; return tmp; } typeof(this) opDiv(typeof(this) rhs) { typeof(this) tmp; tmp.value = value / rhs.value; return tmp; } typeof(this) opMod(typeof(this) rhs) { typeof(this) tmp; tmp.value = value % rhs.value; return tmp; } typeof(this) opAddAssign(typeof(this) rhs) { value += rhs.value; return this; } typeof(this) opSubAssign(typeof(this) rhs) { value -= rhs.value; return this; } typeof(this) opMulAssign(typeof(this) rhs) { value *= rhs.value; return this; } typeof(this) opDivAssign(typeof(this) rhs) { value /= rhs.value; return this; } typeof(this) opModAssign(typeof(this) rhs) { value %= rhs.value; return this; } string toString() { return std.stdio.toString(value); } } // end code Used it for a quick 'n dirty test like this: // start code struct ImaginaryNumber { mixin TypeDef!(int); typeof(this) opAddAssign(int rhs){return this;} typeof(this) opSubAssign(int rhs){return this;} int opMulAssign(typeof(*this) rhs) { int result = value * rhs.value; value = 0; return -result; } typeof(this) opMulAssign(int rhs) { value *= rhs; return this; } int opMul(typeof(*this) rhs) { return -value * rhs.value; } typeof(this) opMul(int rhs) { typeof(this) tmp; tmp.value = value * rhs; return tmp; } } // end code I'd like to have a T opImplicitCastTo() {return value;} in there as well, but that will have to wait.
Dec 04 2007
"Simen Kjaeraas" wroteHave a template, it's on the house.Thank you for the code. However, there are still 2 problems with it: 1. Every time I define a typedef of the same base type, the operators are reinstantiated. This makes for tremendous code bloat. 2. I cannot create const versions with operators. i.e. how do I do this: typedef long myType; const myType x1 = cast(myType)5; const myType x2 = x1 * cast(myType)5; -Steve
Dec 04 2007
Steven Schveighoffer wrote:Let's say I want a way to create a type that's like a long, but is not implicitly convertable from a long. I can do: typedef long mytype; However, I can't create literals of this type. so if I want to initialize a mytype value to 6, I have to do: mytype x = cast(mytype)6L;FWIW, Ada solves this problem by considering literals in a special type called "universal integer." It's special because you can't actually declare any variables of that type. However, universal integers can be implicitly converted to other types derived from Integer. So, in Ada it looks like this type My_Type is range 0..10 -- Or whatever range you need. X : My_Type; Y : Integer; ... X := Y; -- Type mismatch. Compile error. X := 1; -- Fine. Universal integer converts to My_Type. This sounds like what you want for D. Note, by the way, that the range constraint on a type definition in Ada must be static. Thus the compiler can always tell if the value of the universal integer (which can only be a literal) is in the right range. Ada also has a concept of universal float to deal with float point literals in a similar way. Peter
Dec 03 2007
Peter C. Chapin wrote:Steven Schveighoffer wrote:Behavior like this would come about as a result of polysemous values being added to D, yes? -- ~John Demme me teqdruid.comLet's say I want a way to create a type that's like a long, but is not implicitly convertable from a long. I can do: typedef long mytype; However, I can't create literals of this type. so if I want to initialize a mytype value to 6, I have to do: mytype x = cast(mytype)6L;FWIW, Ada solves this problem by considering literals in a special type called "universal integer." It's special because you can't actually declare any variables of that type. However, universal integers can be implicitly converted to other types derived from Integer. So, in Ada it looks like this type My_Type is range 0..10 -- Or whatever range you need. X : My_Type; Y : Integer; ... X := Y; -- Type mismatch. Compile error. X := 1; -- Fine. Universal integer converts to My_Type. This sounds like what you want for D. Note, by the way, that the range constraint on a type definition in Ada must be static. Thus the compiler can always tell if the value of the universal integer (which can only be a literal) is in the right range. Ada also has a concept of universal float to deal with float point literals in a similar way. Peter
Dec 03 2007
IMO, in this case ADA is slicker than D. I remember that f.i. type Natural is range 0..255 is quit often in use. (long time ago that I learned a bit ADA) Having quality assurance and *Reliable Software in mind, then the ADA way much is smarter than using D's assert() or DBC instead. Hope we'll see this feature in D, maybe it is worth a Feature Request. Bjoern Peter C. Chapin schrieb:Steven Schveighoffer wrote:Let's say I want a way to create a type that's like a long, but is not implicitly convertable from a long. I can do: typedef long mytype; However, I can't create literals of this type. so if I want to initialize a mytype value to 6, I have to do: mytype x = cast(mytype)6L;FWIW, Ada solves this problem by considering literals in a special type called "universal integer." It's special because you can't actually declare any variables of that type. However, universal integers can be implicitly converted to other types derived from Integer. So, in Ada it looks like this type My_Type is range 0..10 -- Or whatever range you need. X : My_Type; Y : Integer; ... X := Y; -- Type mismatch. Compile error. X := 1; -- Fine. Universal integer converts to My_Type. This sounds like what you want for D. Note, by the way, that the range constraint on a type definition in Ada must be static. Thus the compiler can always tell if the value of the universal integer (which can only be a literal) is in the right range. Ada also has a concept of universal float to deal with float point literals in a similar way. Peter
Dec 04 2007
BLS wrote:IMO, in this case ADA is slicker than D. I remember that f.i. type Natural is range 0..255 is quit often in use.Actually that's subtype Natural is Integer range 0 .. Integer'Last; The values in the type are all the non-negative integers. Also it's a subtype so it can be freely mixed with its parent type (Integer). However, a run time check will be added when necessary to verify the range constraint. Sorry about the off topic post. I just felt the need to clarify that point. :-) Peter
Dec 04 2007
Peter C. Chapin schrieb:BLS wrote:Thanks Peter, that's it. Too long ago that I've used ADA. :-( However, having something similar in D would be great. BjoernIMO, in this case ADA is slicker than D. I remember that f.i. type Natural is range 0..255 is quit often in use.Actually that's subtype Natural is Integer range 0 .. Integer'Last; Peter
Dec 04 2007