That is the question... I have yet to find any resource that can convince me of the value of typedef and yet its crawling around everywhere in C++ programs. I know it's carry over from C programmers that just can't shake it, but is there really any place for it in pure C++. How might I benefit from using typedef? I'm working quite a bit in OpenGL of late and these typedefs are making it considerably difficult to fully grasp the API. While its not necessary for me to understand it fully, I like to know whats under the hood. Here's some examples from GL.h and GLU.h that piss me off.
Most of the uses above just obfuscate the code, as you've already noticed. typedefing a function pointer is common, because the function pointer syntax is ugly and unreadable in itself.
Without typedefs, generic programming would be virtually impossible. Look at the STL. Look at the boost libraries.
Well, looks like someone likes to declare their own keywords. I've heard of someone who liked to #define whilst while . Seems they liked to talk posh.
Aside from function pointers, this is another legitimate use of typedef ("CI" stands for "case insensitive, by the way): typedef std::map<std::string,Variable *,strcmpCI> variablesMap_t;
Typedefs for basic types allow the type to be retyped on platforms where the built-in type may differ. IE, if OpenGL expects 'int' to be 32-bits wide, then GLint might be typedefed as 'long' on X platform instead of 'int' if 'int' is only 16-bits wide. This aids in portability and helps prevent potential compiler issues where types may differ ever so subtley.
As for things like GLfloat/GLdouble/GLvoid where the type likely won't change -- it's probably just to maintain consistency.
Typedefs for function pointers greatly ease usage. Function pointers are a mess unless you typedef them.
Typedefing structs like that example is a goofy C thing that doesn't happen much in C++.
In short:
Typedefs can be easily changed. Using built-in types everywhere makes things hard to change. By typedefing everything instead of using built-in types you make the program more adaptable to different environments.
The counterargument though is that C++ wants to be strongly typed but typedefs are the antithesis to that.
A GLubyte can be assigned to a GLboolean and vice versa without complaint because they are the same underlying type (unsigned char).
Are you sure that's a C++ header file? Me thinks it's C.
Why? The treatment of struct. In C, struct occupies it's own namespace. So you need to say struct stat for example. In C++, that is not the case. So using a typedef on structs is helpful.
Although typedef creates synonyms, it can be useful. Consider the case where you need a 32 bit unsigned number. Using a name uint32 is more portable than using the native type directly and the intent is clear.
The presented list of typedefs is excessive and perhaps misleading in cases. For example, GLboolean being defined as an unsignedchar will introduce inefficiencies as C performs logic on ints.
1) It's a lot of extra code (doing that for each type? blech) Although I suppose you could template it.
2) It's not C friendly (remember openGL is a C lib as well, not just a C++ lib). Although I suppose you could #ifdef that
3) It makes basic types objects instead of basic types -- making them subject to additional compiler mingling, such as additional padding (sizeof(jsmith::GLubyte) != sizeof(opengl::GLubyte).. .which is quite a big deal when you consider these types are going to be used in packed arrays representing pixel data and other information that hardware expects to be formatted 'just so')
Plus, I don't know... it just seems silly to me to rebuild a class to act just like a basic type when you can just use a basic type. This just strikes me as overkill.
In the WIN16 API, the objects operated on handles, and were all decleared as HANDLE. Functions from GlobalAlloc, to CreateWindow and beyond all returned the same type, HANDLE. But what that meant was, there was no static check to stop you from passing the handle from CreateWindow to GlobalFree.
Even in the WIN32 API, there's nothing to stop you from passing a handle from FindFirstFile to CloseHandle, which is wrong.
The WIN16 fix was to use a macro to define a different kind of handle (using struct) for each place it was different. It was turned on by defining STRICT. There was no space or runtime cost, but it caught all of these handle misuse errors at compile time.
1) Agreed. Templating is a bit tricky; you can have a templated base type but you still need a real object
that at a minimum has forwarding constructors.
2) Definitely agreed, but who cares about C anyway :)
3) It does, but as long as jsmith::GLubyte contains no virtual functions and contains only a single data member value, then sizeof( jsmith::GLubyte ) == sizeof( jsmith::GLubyte::value ) == sizeof( opengl::GLubyte ).
(Ok, I think that is what the standard says off the top of my head.)
And I have to agree with you that in performance critical code, speed wins out over safety where safety incurs runtime penalties.
On some days I agree with you overall. It is unfortunately a lot of work. On the other hand, I've had enough experiences where I wrote code that used some typedef'ed types and I ended up passing parameters in the wrong order because Foo and Bar were both typedefs for std::string and of course the compiler didn't see a problem. Function expected Foo and Bar, I passed Bar and Foo in some places and the right order in others. Eventually I gave up and created full types for them. Turned runtime errors into compile time errors.
(Of course I'm not trying to say that you should never use fundamental types; it's a matter of code complexity and the likelihood of encountering problems such as the one I described.)
The point is sometimes it is useful to generate real types. I gave real-world example where it was useful. Additionally, when STRICT is defined in WIN16 a handles weren't void*. Further more, that lesson was forgotten in WIN32, and I gave an example where you can be caught out.
I also gave an example where typedef is the prefered thing to use and pointed out what I thought was wrong with the example given.
I'd wrap up by saying use features where they're helpful and solve a real problem, don't just use them to make an implementation look orthogonal.
but as long as jsmith::GLubyte contains no virtual functions and contains only a single data member value [snip]
Really? I guess that's what I get for assuming. I just recall that every time I tried to struct something it padded it to the next DWORD boundary. Perhaps I just never checked that for structs/classes which only contain a single member.