mosra / magnum

Lightweight and modular C++11 graphics middleware for games and data visualization
https://magnum.graphics/
Other
4.74k stars 439 forks source link

Conflict with near and far Macros When Including WinSock2.h After Matrix4.h #630

Closed Koromire closed 9 months ago

Koromire commented 9 months ago

Hello,

In the file Matrix4.h generated by the install, I encountered the following:

#ifdef CORRADE_TARGET_WINDOWS /* I so HATE windef.h */
#undef near
#undef far
#endif

If you include, for example, WinSock2.h after Matrix4.h, this will generate a multitude of errors due to the near and far macros being undefined.

A workaround that worked well for me was to add the following lines before the closing guard at the end of Matrix4.h:

#ifdef CORRADE_TARGET_WINDOWS
#define near
#define far
#endif

I'm not very familliar with all the aspect of magnum yet so I don't know if that would integrate well in all case, for me that solved the issue.

In any case, thank you for your time and effort; I appreciate the work being done on Magnum.

mosra commented 9 months ago

Hi, thanks for the appreciation!

Unfortunately I don't have a nice solution here. To make it work reliably always, I would have to refrain from using near and far anywhere in the code. Which is, as I unfortunately learned the hard way, rather hard to ensure in practice, given this problem is specific to a single platform and happens only if windows.h is included in the same compilation unit. Similar is with min() and max() macros, for which one has to #define NOMINMAX. Another such case is X11 headers on Linux, which define Complex or Status among other very-likely-to-conflict names, and those I'm #undef'ing also because otherwise I'd have to prefix all Magnum types or do similar nasty workarounds.

But besides those I'm trying to avoid conflicts with platform headers where possible. For example, Range2D used to be named Rectangle, which caused very annoying conflicts with the Rectangle() API in GDI, and after failing to find a solution, I gave up and renamed the type.

Re-defining near and far wouldn't work reliably, because there's Frustum::near(), Trade::CameraData::near() etc., which would be impossible to call with the macros re-defined. The only solution that proved to work reliably was including windows.h and related headers only where absolutely necessary, ideally just few source files that deal with Windows OS stuff, and definitely not in any headers, together with #define WIN32_LEAN_AND_MEAN, #define NOMINMAX and such. Which is a good thing to do regardless, since the headers are massive. And then, if it relies on near and far being defined, ensuring it's either included first, before Magnum headers that clean up the conflicting macros, or with the macros re-defined again. This is what I do in Magnum internally, the few windows.h includes that are needed are only in *.cpp files.

Alternatively you can switch the vanilla windows.h for something that's less horrific, such as https://github.com/Leandros/WindowsHModular.

I know it's not a great solution, especially if it's some 3rd party library that's to blame for pulling in windows.h, but that's the C legacy we have to live with :sweat_smile:

Koromire commented 9 months ago

Thanks for the quick reply!

Oh I see, I've already encountered this type of problem with min and max macros on another project, but I hadn't made the connection...

I included my windows-dependent headers before the magnum ones, so it didn't require too much modification, so it'll be fine :)