Pop quiz: what’s the return type of strlen()? (Ignore the related question of why use strlen()?
for the moment.)
In fairness, it depends on who you ask. Recent Microsoft implementations return size_t, and POSIX agrees.
9 out of 10 dentists (and at least one blogger) agree that it is better to limit the use of casting in your code. If you’re going to do that, you’ll find out early on that you have to match the types your program uses to the types that your APIs use. Otherwise you will create porting difficulties and compiler warnings, which usually lead right back to casts.
In fact, the process of type selection is very different if you do things this way: you select variable types based on what the APIs you use expect.
In the case of strlen() above, you’ll get a complier warning if you try to use an unsigned long (for example) as the return type (under /Wp64 anyway), because on 64-bit Windows, size_t (strlen()’s real return type) is 64 bits, and unsigned long is 32 bits.
Win32 loves to use DWORDs everywhere, which is well enough as long as your types match, and as long as you keep in mind that the interface between things like strlen() and Win32 necessarily involves casting and range limitations. Also, doing things like forcing HANDLEs into DWORDs is obviously not a good idea.
In the end, porting code to be type-correct is about as much fun as porting C++ code to be const correct. It’s something that’s dramatically easier to do at design time. On the flip side, actually doing a port such as this can lead to a better understanding of any range issues that are intrinsic to the code (such as the CRT <-> Win32 interface I mentioned), and it’s great for finding bugs and as an in-depth code review. If you happen to enjoy that kind of thing, anyway.
My advice is to be precise from the outset about matching your types to the types expected and returned by the APIs you’re using.
So, you’re saying that if size_t is returned you should use size_t’s instead of, say, unsigned long’s?
Then what about DWORDs which are Microsoft specific?
Absolutely. If you use unsigned long, you’ll be silently truncating a 64-bit return type to 32 bits on a 64-bit computer. /Wp64 /W4 will warn you about this.
As for DWORDs and such, my philosophy is to follow the types that the API gives you. The useful range of the API’s output may be limited, but at that point it’s a problem with the API itself, not with your selection of types.
This is a real problem for people trying to write portable socket code, for one thing. recv() is defined by POSIX to return a ssize_t these days, but Microsoft’s implementation still returns an int. You really have no choice but to write different code for these two different APIs, if you want to ensure 64-bit portability.
[...] Kernel Mustard Reflections on Windows System Programming « API-driven types [...]
I was assigned the task to port a entire windows VPN client to 64-bit single handedly for last product release. I was able to get the whole thing working in a day’s time, thanks to wow64. I ended up porting the whole thing. Then I started doing fancy stuff, size_t stuff. In fact the issue was in a cross platform library which was developed by another guy. So I made all the size_t changes,compiled it,tested and then then told the original author of the code, I am going to checkin the warnings changes.He was bit uncomfortable when I showed him the changes, may be because he felt another chap was touching almost every line of his code. Well the warnings change didn’t go into the depot then as the logical reasoning he gave was that strings can’t be more than 2^32 in length in our scenario. So that was shelved till the next release. After reading your post, it was a reminder and I am going to pick it up from the shelf again for checkin.