我找到了问题的根源,它实际上与 TBB 无关,而是与Poco 库有关。
考虑最小的例子:
#include <Poco/Poco.h>
#include <tbb/tbb.h>
void main()
{
}
这将引发编译器错误。
追踪路径
当包含tbb.h时,critical_section.h包含在tbb.h的第 51 行中。然而,ciritcal_section.hpp包括machine/winwdows_api.h看起来像这样(不必要的东西被剪掉了):
待定/机器/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 。该宏在sdkddkver.h(包含在windows.h中)中定义,如果尚未定义(在我的情况下它设置为 Win10):_WIN32_WINNT
sdkddkver.h:
#if !defined(_WIN32_WINNT) && !defined(_CHICAGO_)
#define _WIN32_WINNT 0x0A00
#endif
在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(缩短):
#if defined(_WIN32_WINNT)
#if (_WIN32_WINNT < 0x0501)
#error Unsupported Windows version.
#endif
#elif defined(NTDDI_VERSION)
#if (NTDDI_VERSION < 0x05010100)
#error Unsupported Windows version.
#endif
#elif !defined(_WIN32_WINNT)
#define _WIN32_WINNT 0x0501
#define NTDDI_VERSION 0x05010100
#endif
#endif
#include <windows.h>
预处理器检查是否_WIN32_WINNT
已定义,如果没有,则将其设置为 0x0501,即 Windows XP。之后,包含windows.h。在上一章中,我提到_WIN32_WINNT
控制实际包含哪个版本的 windows 头文件。
现在想象一下,我们项目中的第一个包含是来自 Poco 的标头。这意味着,这_WIN32_WINNT
将设置为 Windows XP 并且windows.h将包含Windows XP的 windows 标头(imo 已经是一个不好的迹象)。
但别担心,情况会变得更糟。
如果我们向上跟踪包含层次结构,我们会到达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。我不知道为什么会这样,也许有一个很好的理由,idk。
如果我们现在看一下最顶部的最小示例,我们会看到 Poco 包含在 TBB 之前。现在发生的是:
- 包括 Poco 标头
- 设置
_WIN32_WINNT
为 Windows XP
- 包括 windows 标题(Windows XP 版本,因为 2)
- 重置
_WIN32_WINNT
为 Windows 8.1
- 包含 TBB 标头(windows 标头已经包含,因此 TBB 不需要在tbb/windows_api.h中再次包含它们)
- TBB 通过检查 Windows 版本
_WIN32_WINNT
并识别 Windows 8.1(由 Poco 设置)
- TBB 认为
InitializeCriticalSectionEx
是定义的,因为 Windows 版本是 8.1(或者它是?Poco 说:get rekt)并且InitializeCriticalSectionEx
是从 Windows Vista 开始定义的。
- 不幸的是,Poco 确保加载了 Windows XP 头文件,因此编译器说:不。
解决方案
要么预先包含windows.h,要么预先设置_WIN32_WINNT
自己:
#define _WIN32_WINNT 0x0A00 // either this
#include <Windows.h> // or this
#include <Poco/Poco.h>
#include <tbb/tbb.h>
void main()
{
}
也许 Poco 贡献者中的某个人可以在这里澄清一些事情。Poco 版本是 1.8.1-1,使用 x64(通过 vcpkg)构建。
更新
Poco 正在讨论这个问题。可在此处找到更新。