www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - using a typedefed variable with library classes

reply Charles Hixson <charleshixsn earthlink.net> writes:
Is it possible to use a typedefed variable with library classes?

In particular, what I've done is:

typedef  int  TestType;
TestType  t;
File      f;

t = 42;
f.write (t);

And the response I get is the compiler asking whether I want to write 
bytes or a long.

Am I doing something grossly wrong?  Or is typedef just broken?  Or is 
it really supposed to work that way for some not-understood reason?
(alias works fine...but this seems to make typedef basically useless. 
Unless there's a way of adding functions to the library classes [like 
file] so that they'll understand how to cast the typedefed variable.)
Jan 10 2009
parent reply Bill Baxter <wbaxter gmail.com> writes:
On Sun, Jan 11, 2009 at 8:23 AM, Charles Hixson
<charleshixsn earthlink.net> wrote:
 Is it possible to use a typedefed variable with library classes?

 In particular, what I've done is:

 typedef  int  TestType;
 TestType  t;
 File      f;

 t = 42;
 f.write (t);

 And the response I get is the compiler asking whether I want to write bytes
 or a long.

 Am I doing something grossly wrong?  Or is typedef just broken?  Or is it
 really supposed to work that way for some not-understood reason?
 (alias works fine...but this seems to make typedef basically useless. Unless
 there's a way of adding functions to the library classes [like file] so that
 they'll understand how to cast the typedefed variable.)
I've never found a use for typedef myself. I don't think it's used much, so it could be that it's a special case that Andrei didn't consider when re-writing write() [at least I'm assuming you're talking D2, based on the error message]. Or it could be that it is a bug with the compiler. But either way I think no one uses it, so it's likely to flush out lots of corner cases. --bb
Jan 10 2009
next sibling parent Charles Hixson <charleshixsn earthlink.net> writes:
Bill Baxter wrote:
 On Sun, Jan 11, 2009 at 8:23 AM, Charles Hixson
 <charleshixsn earthlink.net> wrote:
 Is it possible to use a typedefed variable with library classes?

 In particular, what I've done is:

 typedef  int  TestType;
 TestType  t;
 File      f;

 t = 42;
 f.write (t);

 And the response I get is the compiler asking whether I want to write bytes
 or a long.

 Am I doing something grossly wrong?  Or is typedef just broken?  Or is it
 really supposed to work that way for some not-understood reason?
 (alias works fine...but this seems to make typedef basically useless. Unless
 there's a way of adding functions to the library classes [like file] so that
 they'll understand how to cast the typedefed variable.)
I've never found a use for typedef myself. I don't think it's used much, so it could be that it's a special case that Andrei didn't consider when re-writing write() [at least I'm assuming you're talking D2, based on the error message]. Or it could be that it is a bug with the compiler. But either way I think no one uses it, so it's likely to flush out lots of corner cases. --bb
Sorry. Yes, it was D2. Guess I'll just avoid it then. (Pity.)
Jan 10 2009
prev sibling parent reply bearophile <bearophileHUGS lycos.com> writes:
Bill Baxter:
 I've never found a use for typedef myself.  I don't think it's used much,<
In Pascal (and its variant and children, like ObjectPascals, etc) there is a section named Type where you define your typedefs. People used to program in Pascal-like languages (ObjectPascals, Ada, Oberon, etc) may appreciate the typedef of D and they may use it. I use typedef now and then, I presume it's mostly useful for imperative style of programming and not much in OOP. A simple usage example: you have a procedural/functional program that has several functions that process a matrix, the same matrix, for example a float[12][7]. In such situation you may want to define: typedef float[12][7] FPfield; Then if you use FPField in function signatures like this: void foo(FPfield mat, ...) { ... } you gain some things: - The type name may be shorter, saving you some typing. And you don't need to remember each time the size of the dimensions. - If you later want to FPfield into a matrix of doubles or the matrix you have to change only one line. If your code uses a dynamic array this is less important. But from coding a lot of programs in D I have seen programs up to 2-5-10 times faster when I use 3D/4D static arrays, mostly because lot of address computations are done at compile time or partially optimized away instead of run time, and because of higher cache coherence. I can show an extreme example, if you want. D dynamic arrays can't replace all static arrays where speed matters. - It's a way to document the code, because you aren't just giving a generic matrix to foo, you are giving it a FPfield, this often has an important semantic meaning. - It's type safe, so you don't risk giving the wrong matrix to foo. You can't do this well with an alias. This is more useful in largish programs. If you use OOP or lot generic programming this becomes less important. Bye, bearophile
Jan 10 2009
parent reply Bill Baxter <wbaxter gmail.com> writes:
On Sun, Jan 11, 2009 at 10:16 AM, bearophile <bearophileHUGS lycos.com> wro=
te:
 Bill Baxter:
 I've never found a use for typedef myself.  I don't think it's used much=
,<
 In Pascal (and its variant and children, like ObjectPascals, etc) there i=
s a section named Type where you define your typedefs. People used to progr= am in Pascal-like languages (ObjectPascals, Ada, Oberon, etc) may appreciat= e the typedef of D and they may use it.
 I use typedef now and then, I presume it's mostly useful for imperative s=
tyle of programming and not much in OOP.
 A simple usage example: you have a procedural/functional program that has=
several functions that process a matrix, the same matrix, for example a fl= oat[12][7]. In such situation you may want to define:
 typedef float[12][7] FPfield;

 Then if you use FPField in function signatures like this:
 void foo(FPfield mat, ...) { ... }
 you gain some things:
 - The type name may be shorter, saving you some typing. And you don't nee=
d to remember each time the size of the dimensions.
 - If you later want to FPfield into a matrix of doubles or the matrix you=
have to change only one line. If your code uses a dynamic array this is le= ss important. But from coding a lot of programs in D I have seen programs u= p to 2-5-10 times faster when I use 3D/4D static arrays, mostly because lot= of address computations are done at compile time or partially optimized aw= ay instead of run time, and because of higher cache coherence. I can show a= n extreme example, if you want. D dynamic arrays can't replace all static a= rrays where speed matters.
 - It's a way to document the code, because you aren't just giving a gener=
ic matrix to foo, you are giving it a FPfield, this often has an important = semantic meaning.
 - It's type safe, so you don't risk giving the wrong matrix to foo. You c=
an't do this well with an alias. This is more useful in largish programs.
 If you use OOP or lot generic programming this becomes less important.
Actually it might be useful to me. The first time I tried to use it my thought was to do typedef Exception MyException; To create a different exception type. That doesn't work so I kinda just said, eh whatever, this typedef stuff doesn't work yet. But probably that's just because that's not the use case they were intended= for. Something I might be able to use it for is for index types. Like this: typedef size_t VertexHandle; typedef size_t FaceHandle; Now I can't accidentally assign a vertex handle to a face handle. That could be useful. --bb
Jan 10 2009
parent reply bearophile <bearophileHUGS lycos.com> writes:
Bill Baxter:

 The first time I tried to use it my thought was to do
   typedef Exception MyException;
 To create a different exception type.  That doesn't work so I kinda
 just said, eh whatever, this typedef stuff doesn't work yet.
You are right, typedef was invented before classes and OOP, so what you say doesn't work :-) But I think typedef may be modified to work as you want too, that is to define a subtype (so the runtime typeinfo of MyException too has to be different!). I/we can ask Walter what he thinks about this.
 Now I can't accidentally assign a vertex handle to a face handle.
 That could be useful.
Right. But so far I don't see problems in extending the semantics of typedef to work with OOP too. Maybe other people here can spot a problem. I presume Walter doesn't read this newsgroup, so I can post a summary in the main D group too, later. Bye, bearophile
Jan 11 2009
parent reply Charles Hixson <charleshixsn earthlink.net> writes:
bearophile wrote:
 Bill Baxter:
 
 The first time I tried to use it my thought was to do
   typedef Exception MyException;
 To create a different exception type.  That doesn't work so I kinda
 just said, eh whatever, this typedef stuff doesn't work yet.
You are right, typedef was invented before classes and OOP, so what you say doesn't work :-) But I think typedef may be modified to work as you want too, that is to define a subtype (so the runtime typeinfo of MyException too has to be different!). I/we can ask Walter what he thinks about this.
 Now I can't accidentally assign a vertex handle to a face handle.
 That could be useful.
Right. But so far I don't see problems in extending the semantics of typedef to work with OOP too. Maybe other people here can spot a problem. I presume Walter doesn't read this newsgroup, so I can post a summary in the main D group too, later. Bye, bearophile
Well, my use case just involves being able to use library function with the proper base type. (I.e., int instead of long or byte when I do typedef int LocalType; LocalType t; File f; f.write(t); I'll grant that I *can* use alias for such a purpose, but that doesn't distinguish between LocalType and ArrayIndex in routines defined for those types. E.g.: typedef int ArrayIndex; void zeroIndex(out ArrayIndex ai) { ai = 0; } zeroIndex(t); Should throw an error. If I'm using alias it doesn't (and shouldn't);
Jan 11 2009
parent reply Sergey Gromov <snake.scaly gmail.com> writes:
Sun, 11 Jan 2009 18:09:15 -0800, Charles Hixson wrote:

 Well, my use case just involves being able to use library function with 
 the proper base type.  (I.e., int instead of long or byte when I do
 typedef int LocalType;
 LocalType t;
 File f;
 f.write(t);
 
 I'll grant that I *can* use alias for such a purpose, but that doesn't 
 distinguish between LocalType and ArrayIndex in routines defined for 
 those types.  E.g.:
 typedef  int ArrayIndex;
 void zeroIndex(out ArrayIndex ai)  {	ai = 0;  }
 zeroIndex(t);
 
 Should throw an error.  If I'm using alias it doesn't (and shouldn't);
Well, I presume File.write() has many overloads, including one for int but none for LocalType. When aliasing, the LocalType *is* an int, it matches exactly File.write(int) and the compiler is happy. However, with a typedef, LocalType is a distinct type. Yes it casts to int implicitly, but likewise it casts implicitly to char, short and long. So compiler gets a whole load of File.write() functions matching with conversions, and fails because of the ambiguity. That's how the language works, and it's pretty consistent IMO. What you can do is: f.write(cast(int)t); or use templates to generalize: auto fileWrite(T : LocalType)(File f, T v) { return f.write(cast(int)v); } auto fileWrite(T)(File f, T v) { return f.write(v); } fileWrite(f, t); // OK fileWrite(f, 15); // OK When we get generic function calls you'd be even able to write: void write(File f, LocalType l) { f.write(cast(int)l); } f.write(t); // OK, write(f, t) is called
Jan 12 2009
next sibling parent reply Bill Baxter <wbaxter gmail.com> writes:
On Tue, Jan 13, 2009 at 1:48 AM, Sergey Gromov <snake.scaly gmail.com> wrote:

 However, with a typedef, LocalType is a distinct type.  Yes it casts to
 int implicitly, but likewise it casts implicitly to char, short and
 long.  So compiler gets a whole load of File.write() functions matching
 with conversions, and fails because of the ambiguity.

 That's how the language works, and it's pretty consistent IMO.  What you
 can do is:
But the difference is LocalType can be converted to int exactly in all cases. Given a choice of int,char,short,etc. clearly the conversion to int is best choice. It may be consistent with other cases involving multiple legal conversions, but usually you don't have such a single clearly preferred conversion. It seems to significantly reduce the utility of typedef. --bb
Jan 12 2009
parent Sergey Gromov <snake.scaly gmail.com> writes:
Tue, 13 Jan 2009 05:59:58 +0900, Bill Baxter wrote:

 On Tue, Jan 13, 2009 at 1:48 AM, Sergey Gromov <snake.scaly gmail.com> wrote:
 
 However, with a typedef, LocalType is a distinct type.  Yes it casts to
 int implicitly, but likewise it casts implicitly to char, short and
 long.  So compiler gets a whole load of File.write() functions matching
 with conversions, and fails because of the ambiguity.

 That's how the language works, and it's pretty consistent IMO.  What you
 can do is:
But the difference is LocalType can be converted to int exactly in all cases. Given a choice of int,char,short,etc. clearly the conversion to int is best choice. It may be consistent with other cases involving multiple legal conversions, but usually you don't have such a single clearly preferred conversion. It seems to significantly reduce the utility of typedef.
It's about overload resolution rules. The specs say: The levels of matching are: * no match * match with implicit conversions * exact match There is no notion of 'best' implicit conversion.
Jan 12 2009
prev sibling parent reply Charles Hixson <charleshixsn earthlink.net> writes:
Sergey Gromov wrote:
 Sun, 11 Jan 2009 18:09:15 -0800, Charles Hixson wrote:
 
 Well, my use case just involves being able to use library function with 
 the proper base type.  (I.e., int instead of long or byte when I do
 typedef int LocalType;
 LocalType t;
 File f;
 f.write(t);

 I'll grant that I *can* use alias for such a purpose, but that doesn't 
 distinguish between LocalType and ArrayIndex in routines defined for 
 those types.  E.g.:
 typedef  int ArrayIndex;
 void zeroIndex(out ArrayIndex ai)  {	ai = 0;  }
 zeroIndex(t);

 Should throw an error.  If I'm using alias it doesn't (and shouldn't);
Well, I presume File.write() has many overloads, including one for int but none for LocalType. When aliasing, the LocalType *is* an int, it matches exactly File.write(int) and the compiler is happy. However, with a typedef, LocalType is a distinct type. Yes it casts to int implicitly, but likewise it casts implicitly to char, short and long. So compiler gets a whole load of File.write() functions matching with conversions, and fails because of the ambiguity. That's how the language works, and it's pretty consistent IMO. What you can do is: f.write(cast(int)t); or use templates to generalize: auto fileWrite(T : LocalType)(File f, T v) { return f.write(cast(int)v); } auto fileWrite(T)(File f, T v) { return f.write(v); } fileWrite(f, t); // OK fileWrite(f, 15); // OK When we get generic function calls you'd be even able to write: void write(File f, LocalType l) { f.write(cast(int)l); } f.write(t); // OK, write(f, t) is called
A) Yes, it works the way that you say. This damages it's utility. B) I'm replying to a question as to how typedef could reasonably be extended.
Jan 14 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
Charles Hixson wrote:
 A) Yes, it works the way that you say.  This damages it's utility.
 B) I'm replying to a question as to how typedef could reasonably be 
 extended.
The point of a typedef is to provide additional type safety. This would not exist if you could implicitly cast back and forth. Unless you want an implicit cast from a typedef type to the base type, and not the reverse -- that might be reasonable (I can't immediately see any issues with it).
Jan 15 2009
parent reply Charles Hixson <charleshixsn earthlink.net> writes:
Christopher Wright wrote:
 Charles Hixson wrote:
 A) Yes, it works the way that you say.  This damages it's utility.
 B) I'm replying to a question as to how typedef could reasonably be 
 extended.
The point of a typedef is to provide additional type safety. This would not exist if you could implicitly cast back and forth. Unless you want an implicit cast from a typedef type to the base type, and not the reverse -- that might be reasonable (I can't immediately see any issues with it).
That's all that's needed to solve the use case that I presented. Unfortunately, what it tried to do was cast it to either byte or long rather than the base case (which was int). Fortunately it couldn't decide which to cast it as, as either choice would have been an error. (I'm writing to a binary file, and the size of the item written is significant.) Because of this, to avoid scattering casts throughout the program, I had to replace the typedef with alias. This works, but it also definitely prevents the type safety that a typedef could (should) have provided.
Jan 17 2009
parent reply Christopher Wright <dhasenan gmail.com> writes:
Charles Hixson wrote:
 Unfortunately, what it tried to do was cast it to either byte or long 
 rather than the base case (which was int).  Fortunately it couldn't 
 decide which to cast it as, as either choice would have been an error. 
 (I'm writing to a binary file, and the size of the item written is 
 significant.)
The following works for me (using LDC): typedef int mint; void foo (long i) {} void main () { mint a = 1; foo (a); } However, this also works: typedef int mint; void foo (byte i) {} void main () { mint a = 1; foo (a); } It's an implicit narrowing conversion -- that involves data loss. This is a bug.
Jan 18 2009
next sibling parent Christopher Wright <dhasenan gmail.com> writes:
Christopher Wright wrote:
 It's an implicit narrowing conversion -- that involves data loss. This 
 is a bug.
Actually, you said you had two methods, something like this: void foo (byte i) {} void foo (long i) {} void main () { int a = 1; foo (a); byte b = a; // no casts } This example doesn't use typedefs but still complains about overloads. I find it odd that you can implicitly cast an arithmetic type of one width to another of a smaller width. I'm making a feature enhancement request for this.
Jan 18 2009
prev sibling parent Christopher Wright <dhasenan gmail.com> writes:
Christopher Wright wrote:
 It's an implicit narrowing conversion -- that involves data loss. This 
 is a bug.
Okay, you can compile with warnings and dmd yells at you. But that's about it. This is pretty cruddy.
Jan 18 2009