Today I ran into the following error:
Error 1 error C2733: second C linkage of overloaded function '_interlockedbittestandset' not allowed $VCDIR\include\intrin.h 944 Error 2 error C2733: second C linkage of overloaded function '_interlockedbittestandreset' not allowed $VCDIR\include\intrin.h 945
Eeek, what’s going on there? Well, my first instinct was to redefine the name to something else to see what breaks. Turns out that doesn’t help a lot depending on where you do that. The problem appears to be that the two intrinsic functions
_interlockedbittestandreset are declared both in
WinNT.h and in
intrin.h. Actually it’s also “defined” (in quotes because it’s using
#pragma intrinsic) as an intrinsic in
WinNT.h, and we won’t get around this easily, because if we change the name of those intrinsics before including
WinNT.h, the compiler is going to complain that it has no clue what intrinsic it is we want to use:
Warning 1 warning C4163: '_local_interlockedbittestandset' : not available as an intrinsic function $SDKDIR\Include\winnt.h 3124 Warning 2 warning C4163: '_local_interlockedbittestandreset' : not available as an intrinsic function $SDKDIR\Include\winnt.h 3125
Now that is annoying. However, I didn’t want to go for the invasive method of editing one of the SDK headers. Instead I decided to use the powers of the preprocessor to my advantage. The first issue here was to make sure
WinNT.h gets included first. For most Windows code this is a given with
Windows.h providing this prerequisite.
Next I had to include
intrin.h (which contains a convenient
#pragma once) in the main source file which created the mess in the first place. However, in order to mask out the second declaration of the function I had to rename it. That’s where the preprocessor comes in:
#if _MSC_VER >= 1400 // Following 8 lines: workaround for a bug in some older SDKs # pragma push_macro("_interlockedbittestandset") # pragma push_macro("_interlockedbittestandreset") # pragma push_macro("_interlockedbittestandset64") # pragma push_macro("_interlockedbittestandreset64") # define _interlockedbittestandset _local_interlockedbittestandset # define _interlockedbittestandreset _local_interlockedbittestandreset # define _interlockedbittestandset64 _local_interlockedbittestandset64 # define _interlockedbittestandreset64 _local_interlockedbittestandreset64 # include <intrin.h> // to force the header not to be included elsewhere # pragma pop_macro("_interlockedbittestandreset64") # pragma pop_macro("_interlockedbittestandset64") # pragma pop_macro("_interlockedbittestandreset") # pragma pop_macro("_interlockedbittestandset") #endif
intrin.h got introduced with VS 2005, we check for this precondition. Then we save (
push_macro) the values of the
_interlockedbittestandreset defines, if any. Then we rename them and include
intrin.h, thus creating a definition for functions
_local_interlockedbittestandreset and then we restore (
pop_macro) the old values of the macros using those function names, just in case.
This could of course be put into a local header, so as to not repeat this every time we need to use
intrin.h together in a source file.
make sure that none of your headers ends up including
intrin.h on its own before the above fix. This will break the fix as Roan found out and remarked in a comment. Sorry for not being more explicit about this requirement before.
Mozilla came up with a subset of my solution here. It won’t work if you make use of those functions, though. Because in this case later references will get renamed according to the macro expansion rules.
PS: the problem also exists for
_interlockedbittestandreset64 (now included in the fix above)
PPS: it should also be possible to throw this last block above the inclusion of
WinNT.h – haven’t tested that, though.