我找到了问题的源头,实际上与TBB无关,而是与Poco库有关。
考虑以下最小示例:
#include <Poco/Poco.h>
#include <tbb/tbb.h>
void main()
{
}
这将抛出编译器错误。
追踪路径
当包含tbb.h时,tbb.h的第51行将包含critical_section.h。然而,ciritcal_section.hpp包含了machine/winwdows_api.h,它看起来像这样(不必要的内容被省略):
tbb/machine/winwdows_api.h:
#if _WIN32 || _WIN64
#include <windows.h>
#if _WIN32_WINNT < 0x0600
#define InitializeCriticalSectionEx inlineInitializeCriticalSectionEx
inline BOOL WINAPI inlineInitializeCriticalSectionEx( LPCRITICAL_SECTION lpCriticalSection, DWORD dwSpinCount, DWORD )
{
return InitializeCriticalSectionAndSpinCount( lpCriticalSection, dwSpinCount );
}
#endif
正如您所看到的,
windows.h在检查
_WIN32_WINNT
宏之前被包含。这个宏在
sdkddkver.h中定义(该文件被包含在
windows.h中),如果它还没有被定义的话(在我的情况下,它被设置为Win10):
sdkddkver.h:
在
windows.h中,
_WIN32_WINNT
宏控制实际包含的Windows头文件版本。如果
_WIN32_WINNT
设置为早于Windows Vista的版本,则未定义函数
InitializeCriticalSectionEx
。
这个问题可以通过
machine/winwdows_api.h(如该文件的代码块所示)捕获,只需定义一个调用适当替代函数的宏
InitializeCriticalSectionEx
即可。
到目前为止一切正常。
问题
问题的根源在于Poco库的
Poco/UnWindows.h。在包含poco头文件时,最终会包含
UnWindows.h。
Poco/UnWindows.h(简化):
预处理器检查是否已经定义了
_WIN32_WINNT
,如果没有,则将其设置为0x0501,即Windows XP。之后,包含
windows.h。在前一章中,我提到
_WIN32_WINNT
控制实际包含的Windows头文件的版本。
现在设想一下,我们项目中的第一个include是来自Poco的头文件。这意味着
_WIN32_WINNT
将被设置为Windows XP,并且
windows.h将包括
Windows XP的Windows头文件(在我看来这已经是一个不好的迹象)。
但别担心,情况会变得更糟。
如果我们将include层次结构向上追溯一个级别,就会到达
Poco/Platform_WIN32.h。
Poco/Platform_WIN32.h(缩短版):
#include "Poco/UnWindows.h"
...
#if defined (_WIN32_WINNT_WINBLUE)
#ifdef _WIN32_WINNT
#undef _WIN32_WINNT
#endif
#define _WIN32_WINNT _WIN32_WINNT_WINBLUE
...
有趣,不是吗?首先,它包括
UnWindows.h,设置
_WIN32_WINNT
并导致包括Windows XP头文件,然后重新定义
_WIN32_WINNT
为Windows 8.1。我不知道为什么要这样做,也许有一个很好的原因,我不知道。
如果我们现在看一下最上面的最小示例,我们会发现Poco在TBB之前被包括。接下来会发生以下情况:
- 包括Poco头文件
- 将
_WIN32_WINNT
设置为Windows XP
- 包括Windows头文件(由于2),使用Windows XP版本
- 将
_WIN32_WINNT
重置为Windows 8.1
- 包括TBB头文件(Windows头文件已经被包括,所以TBB不需要在tbb/windows_api.h中再次包括它们)
- TBB通过
_WIN32_WINNT
检查Windows版本,并识别为Windows 8.1(由Poco设置)
- TBB认为
InitializeCriticalSectionEx
被定义了,因为Windows版本是8.1(或者是吗?Poco说:get rekt),而自Windows Vista以来就定义了InitializeCriticalSectionEx
。
- 不幸的是,Poco确保加载了Windows XP头文件,因此编译器会说:不行。
解决方案
要么自己先包括
windows.h,要么自己先设置
_WIN32_WINNT
:
#define _WIN32_WINNT 0x0A00
#include <Windows.h>
#include <Poco/Poco.h>
#include <tbb/tbb.h>
void main()
{
}
也许Poco的一些贡献者可以在这里澄清一些问题。 Poco版本是1.8.1-1,使用x64(通过vcpkg构建)。
更新
Poco正在处理这个问题。 更新可以在这里找到。