windows.h breaks the standard library (and my will to live)

While working on not-so-old code, I ended up with a strange compilation error in MS Visual Studio :

Here was the incriminated code :

//...

const T_LongType BIG_OFFSET = std::numeric_limits<T_LongType>::max() / 2;

//...

And here the errors :

1>MyFile.cpp(42): error C2589: '(' : illegal token on the right side of '::'
1>MyFile.cpp(42): error C2059: syntax error : '::'
1>MyFile.cpp(42): error C2059: syntax error : ')'
1>MyFile.cpp(42): error C2059: syntax error : ')'

It came with a bunch of warnings I won’t even bother writing here.

I took me a hella lot of time to find out what was wrong. The type T_LongType was correctly defined (actually a typedef of long) and I didn’t forget to include <limits>.

Many of you may already know the culprit, and it’s the following line :

#include <windows.h>

Indeed, if we look into the code of this library, we can see a surprising piece of code :

#ifndef NOMINMAX

#ifndef max
#define max(a,b)            (((a) > (b)) ? (a) : (b))

#ifndef min
#define min(a,b)            (((a) < (b)) ? (a) : (b))

#endif  /* NOMINMAX */

To my defense, the file in which the error occured did not include windows.h. It was included by another header.

Explanation

The fact that windows.h defines macros called min and max implies that, during the preprocessor phase of the compilation, every instances of the words min and max will be replaced.

This means that, after the preprocessing, instead of seeing this :

const T_LongType BIG_OFFSET = std::numeric_limits<T_LongType>::max() / 2;

The compiler will see this :

const T_LongType BIG_OFFSET = std::numeric_limits<T_LongType>::(((a) > (b)) ? (a) : (b))() / 2;

Which makes no sense, thus the compilation errors mentioned above.

Many reasons to not include windows.h

Here is a non-exhaustive list that why it is a bad practice to include this header :

  • It breaks the standard library just by including it. The way we use the functionality of the standard library should work whatever the header files we include.
  • It forces you to define NOMINMAX and the beginning of each headers that include windows.h. And if you ever happen to forget this, every file that will include your header will have to define it.
  • Since it’s OS-dependant, it’s better to avoid it as long as you can. If you use where you don’t need to, you won’t be able to port your code to other systems, you may obtain bad coding habits (by relying too much on it) and to not forget that the more specific a library is, the less maintained it will be.

Conclusion

All in all there are ways to use windows.h safely, but you must be absolutely be solid in how you include it to avoid side-effects to other sources and headers.

As long as you can, don’t use it.

Categories: ufo

2 thoughts on “windows.h breaks the standard library (and my will to live)

  1. The best way to include Windows.h is to define WIN32_LEAN_AND_MEAN; NOSERVICE; NOMCX; NOIME;NOMINMAX; globally before including it, this ensures that the minimal header files are pulled in by Windows.h
    The NOMINMAX problem is already very old. Microsoft will probably never make any big changes anymore to Windows.h, as it will break too many (legacy) apps.
    They have started however a project to make Windows.h obsolete for modern C++ apps:
    https://github.com/microsoft/cppwin32
    This project is however very silent since its introduction.

    Like

  2. Meh. Known for eons. This remnant from the 80s can’t be fixed because Hyrum’s Law. Whoever included Windows.h in the past 30 years without #define NOMINMAX before the #include directive is outride hostile to the community.

    Like

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s