CesiumGS / cesium-native

Apache License 2.0
414 stars 210 forks source link

Rename `OPAQUE` to `OPAQUE_ENUM` to deconflict with define in `wingdi.h` #796

Closed lilleyse closed 7 months ago

lilleyse commented 7 months ago

Renamed CesiumGltf::Material::AlphaMode::OPAQUE to OPAQUE_ENUM to deconflict with the define in wingdi.h

The fix was to add OPAQUE to the reserved words list and add a suffix to enums using reserved words.

Now projects using CesiumGltf don't need to add code like this everywhere:

#pragma push_macro("OPAQUE")
#undef OPAQUE

Unfortunately, this is a breaking change and it looks a little odd to see OPAQUE_ENUM, MASK, BLEND. So I'm open to alternatives here.

csciguy8 commented 7 months ago

Have you considered using a scoped enum instead? (C++ 11 feature)

struct AlphaMode

becomes

enum struct AlphaMode

This way we can keep our enum names, but also not play the "this platform's scope is polluted with X" game.

lilleyse commented 7 months ago

The original reason for using a struct with constants as opposed to an enum is described here: https://github.com/CesiumGS/cesium-native/issues/258#issuecomment-859280034

I'm not sure if an enum struct would work anyways. Here's a [Godbolt example](https://godbolt.org/#g:!((g:!((g:!((h:codeEditor,i:(filename:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,selection:(endColumn:2,endLineNumber:12,positionColumn:2,positionLineNumber:12,selectionStartColumn:2,selectionStartLineNumber:12,startColumn:2,startLineNumber:12),source:'%23define+OPAQUE+false%0A%0Aenum+struct+AlphaMode+%7B%0A++++OPAQUE,%0A++++BLEND,%0A++++MASK%0A%7D%3B%0A%0Aint+main()+%7B%0A++++const+auto+value+%3D+AlphaMode::OPAQUE%3B%0A++++return+1%3B%0A%7D'),l:'5',n:'0',o:'C%2B%2B+source+%231',t:'0')),k:33.333333333333336,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:compiler,i:(compiler:g122,filters:(b:'0',binary:'1',binaryObject:'1',commentOnly:'0',debugCalls:'1',demangle:'0',directives:'0',execute:'0',intel:'0',libraryCode:'0',trim:'1'),flagsViewOpen:'1',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!(),options:'',overrides:!(),selection:(endColumn:1,endLineNumber:1,positionColumn:1,positionLineNumber:1,selectionStartColumn:1,selectionStartLineNumber:1,startColumn:1,startLineNumber:1),source:1),l:'5',n:'0',o:'+x86-64+gcc+12.2+(Editor+%231)',t:'0')),k:33.333333333333336,l:'4',n:'0',o:'',s:0,t:'0'),(g:!((h:executor,i:(argsPanelShown:'1',compilationPanelShown:'0',compiler:g122,compilerName:'',compilerOutShown:'0',execArgs:'',execStdin:'',fontScale:14,fontUsePx:'0',j:1,lang:c%2B%2B,libs:!(),options:'',overrides:!(),runtimeTools:!(),source:1,stdinPanelShown:'1',tree:'1',wrap:'1'),l:'5',n:'0',o:'Executor+x86-64+gcc+12.2+(C%2B%2B,+Editor+%231)',t:'0')),k:33.33333333333333,l:'4',n:'0',o:'',s:0,t:'0')),l:'2',n:'0',o:'',t:'0')),version:4).

csciguy8 commented 7 months ago

Good example. I can't get that to work either.

Looks like if OPAQUE is already defined, this will cause problems defining AlphaMode, regardless if it's scoped or not.

I suppose the code that includes wingdi.h can try to stuff it into a separate namespace, but if we are looking for simple, it's hard to beat the renaming approach you did.

timoore commented 7 months ago

Good example. I can't get that to work either.

Looks like if OPAQUE is already defined, this will cause problems defining AlphaMode, regardless if it's scoped or not.

I suppose the code that includes wingdi.h can try to stuff it into a separate namespace, but if we are looking for simple, it's hard to beat the renaming approach you did.

I think that is going to be unwinnable, given that the offending symbols are macros.

I believe that spdlog is sucking wingdi.h into Cesium Native headers by including windows.h somewhere, and it might be possible rearrange or forward-declare the spdlog declarations so that wouldn't happen anymore in Cesium Native clients. But then any user who included windows.h themselves would run into this problem again.

kring commented 7 months ago

Possible related: https://github.com/CesiumGS/cesium-native/issues/584

I have more to say on this, will type it up soon.

kring commented 7 months ago

In my view, windows.h and friends are a total disaster. Including it in most projects, or alongside many third-party libraries, will cause all kinds of confusing errors. It has macros for all kinds of common things; not just OPAQUE but also MIN and MAX (also min and max I believe). It appends - again via the preprocessor - nonsense like _A and _W to the end of a bunch of common-ish names.

The only winning move, IMO, is not to play. Don't include windows.h. In the rare case that you're writing non-portable code that needs to use windows functionality directly, include windows.h only in a .cpp file that implements functions, declared in a header file (that does not include Windows headers), that can be called by the rest of your application. In other words, isolate the functionality in a single compilation unit that provides an interface to Windows without including any third-party libraries.

584 describes a case where a third-party library used by cesium-native fails to do this. The easiest workaround in that case is to enable exceptions, but it's probably reasonably easy to fix the library instead if necessary.

Unless there's a case I'm not aware of where this sort of approach creates an undue burden, I'd much prefer it over modifying cesium-native to avoid Windows' many macros.

lilleyse commented 7 months ago

Something else must be including windows.h in cesium-omniverse because we already have exceptions enabled. @timoore mentioned spdlog. It could also be a library in omniverse. I haven't looked deeper yet.

I'm going to close this PR, but it would still be nice to fix this somehow.