3

I'm not sure of the vocabulary here, but hopefully I can make myself understood.

As I'm working through the winapi with a less-than-rock-solid knowledge of C++, I find a lot of typedef stuff that, for me, seems to overcomplicate the issue and add one more thing I have to remember.

For example, UINT instead of unsigned int, HBITMAP which turns out is just a HANDLE, and lots of others.

My question is, can / should I substitute the more generic version of the type when possible, and just cast it down when it's needed (and, what's this called)?

For example, I'd like to write

  • void SomeFunction(unsigned int some_int) { ... } instead of void SomeFunction(UINT some_int) { ... }

  • HANDLE hBMP = LoadImage(...); ImageList_Add(... (HBITMAP) hBMP ...); instead of HBITMAP hBMP = ...

Is this good for newcomers, bad practice in general, or what?

Ben
  • 54,723
  • 49
  • 178
  • 224
  • 5
    `HANDLE` isn't a native type either; it's a `typedef` of `void *`. I'd stick with the WINAPI types when using the WINAPI, though. – chris Nov 28 '12 at 05:42
  • 5
    Types such as UINT exist so that the user of the library can become better decoupled from the compiler's implementation details. – DavidO Nov 28 '12 at 05:43
  • I agree with you in part, but I believe these typedefs are there to make the code more readable. – atoMerz Nov 28 '12 at 05:44

5 Answers5

7

1) The Win32 API is actually C, not C++.

The distinction becomes more important if you consider stuff like MFC or ATL, both of which are "object-oriented", C++-only APIs. And both of which are mercifully becoming (have become?) obsolete.

2) Microsoft likes to use lots of macros and typedefs. I can't say whether that's "good" or "bad". It's simply a fact of life. And it'll become second nature to you if you work with Windows for any length of time.

3) Most importantly - Yes: you should definitely follow the Microsoft conventions when you use the Microsoft APIs. Using "HANDLE" when the MSDN page says "HANDLE" is a Good Thing. Similar advice holds for "LPxxx", "TRUE", "FALSE", "INVALID_HANDLE", "HRESULT" etc etc. If that's what it says in MSDN, then that's what you should use.

Not only will it make your code more readable ... but, surprisingly often, it can also prevent subtle bugs you might cause by "second guessing" the "true type".

Do not try to "second guess" the types. It's just a Bad Idea.

Following the standard conventions will make life easier, safer, more reliable and more portable.

IMHO...

paulsm4
  • 114,292
  • 17
  • 138
  • 190
6

Please don't typedef the obvious (i.e. UINT for unsigned int). Rather, typedef to convey meaning. UINT no better than unsigned int (some may argue that it's shorter, but seriously, it's not that much shorter).

A typedef for a long name (like a typedef for std::map<unsigned int, flost>::const_iterator) is, in my opinion, okay, because it improves readability. UINT... not so much.

A typedef to convey specific meanings for specific types/uses (like HANDLE) are decent. HANDLE is, in my opinion, better than using raw void* because a) I don't care if HANDLE is a void*, b) it conveys its purpose.

A typedef for portability is nice (like a typedef for a 32-bit signed integer), as it allows for easier transitions between platforms/compilers.

Cornstalks
  • 37,137
  • 18
  • 79
  • 144
  • 4
    Typedeffing `UINT` for `unsigned int` is not used to make it shorter, it's rather for portability. If you typedef it as `unsigned int` on platform A, but then typedef it for another type on platform B, you will not have to rewrite your code as you normally would have to. – SingerOfTheFall Nov 28 '12 at 05:50
  • I'm not doing the typedefs :) just trying to keep dancing and not step on anyone's toes. – Ben Nov 28 '12 at 05:50
  • 2
    @SingerOfTheFall: If `UINT` isn't a `typedef` for `unsigned int`, then it's misleading, and if it is, it's pretty useless, seeing as `unsigned int` always means "unsigned integer" on any compiler/platform. A `typedef` for a 32-bit unsigned integer makes sense (because then it's more about the 32-bits than it is about the unsigned integer), but a `typedef` for an unspecified-sized unsigned integer is hardly portable. – Cornstalks Nov 28 '12 at 05:58
4

Is this good for newcomers, bad practice in general, or what?

It is a bad practice. You should not use the actual types.

The typedef's are usually for portability on different platforms & compiler implementations. So you should refrain yourself from using actual types instead of the typedef'ed name. Using actual types will make your code more tightly coupled to the platform & compiler implementation you use, in fact it may not work correctly if this combination changes.

Also, the typedefed names are more intuitive and widely known to programmers than the actual types, which is an additional reason why you should stick to those.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • 1
    For an explanation of how typedefs increase portability see [this](http://stackoverflow.com/a/516268/1599559). –  Nov 28 '12 at 07:41
  • On the other hand, using typedefs from Windows.h is the ultimate way of making your code non-portable. – Bo Persson Nov 28 '12 at 08:11
2

Typedefs are not only useful for portability, but also for future proofing. If MicroSoft decides at some point that UINT should be a typedef for an unsigned long int instead of an unsigned int then using UINT will make sure your code continues to work. This type of change is highly unlikely, especially with code from large companies like MS, but the idea still holds: typedefs are good for many kinds of abstraction. They are by no means bad, but you can occasionally find examples of poor usage. If you are using someone else's API, always use their typedefs. They probably have a good reason for the abstraction.

eestrada
  • 1,575
  • 14
  • 24
1

There are several reasons for using a typedef.

  1. abbreviating some type: typedef unsigned int UINT
  2. portability: typedef int INT64
  3. readability: typedef HANDLE HBITMAP

I'd rather read HBITMAP hBitmap = ... than HANDLE hBitmap = ... for the same reason that I would (in general) not write Animal dog = new Dog() because being more specific can help the reader of your code and you're not losing anything here because according to the Liskov substitution principle you can use the dog instance everywhere you can use an Animal instance.

You might also consider using BOOST_STRONG_TYPEDEF if you are using C++ with Boost, this basically creates a simple class, thus, giving you a real new type (which means that you cannot mix the uses like you can with HANDLE and HBITMAP.


What you describe as casting is not really casting, because the types are identical, a typedef only creates an alias, not a new type. So, writing

HANDLE hOtherBitmap = /* some code */;

HBITMAP hBitmap = (HBITMAP) hOtherBitmap;

is like writing

int i = /* some value */;

int k = (int) i;

Andre
  • 1,577
  • 1
  • 13
  • 25
  • Thanks for the explanation - those are good points. It just seems that with typedeffing, there's another level of complexity to keep track of. But message received - I'll use it! – Ben Nov 29 '12 at 00:24