英特尔 TBB - 'InitializeCriticalSectionEx':找不到标识符编译器错误

Intel TBB - 'InitializeCriticalSectionEx': identifier not found compiler error

我有一个依赖 OpenCV 和 TBB 的 VS (C++) 项目,因此我为每个库创建了 属性 工作表并将它们包含在项目中。一切正常,代码编译成功。

昨天,我开始使用 vcpkg 包管理器。我通过 vcpkg 安装了 OpenCV 和 TBB,一切似乎都正常。我创建了一个空项目,包含了两者的头文件并测试了新编译的库是否有效。验证之后,我回到我的主项目并删除了 属性 工作表,这样我就可以使用 vcpkg 中的库了。自上次成功编译以来,我没有以任何方式更改代码。

但是,当我现在尝试编译代码时,出现了两次此错误(在 main.cpp 和子模块中)

tbb\critical_section.h(53): error C3861: 'InitializeCriticalSectionEx': identifier not found

有人知道这里发生了什么或为什么会出现此错误吗?

更新

我自己发现了错误。我添加了 poco-libraries 标签,因为它实际上是 TBB 和 Poco 之间的冲突。

我找到了问题的根源,它实际上与 TBB 无关,而与 Poco library 有关。

考虑最小的例子:

#include <Poco/Poco.h>
#include <tbb/tbb.h>

void main()
{   
}

这将引发编译器错误。

追踪路径

当包含tbb.h时,critical_section.h包含在的第51行tbb.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:

#if !defined(_WIN32_WINNT) && !defined(_CHICAGO_)
#define  _WIN32_WINNT   0x0A00
#endif

windows.h中,_WIN32_WINNT宏控制实际包含windowsheader文件的哪个版本。如果 _WIN32_WINNT 设置为比 Windows Vista 更早的版本,则函数 InitializeCriticalSectionEx 未定义。

这个问题被 machine/winwdows_api.h 捕获(如您在该文件的代码块中所见),只需定义一个宏 InitializeCriticalSectionEx调用适当的替代函数。

到目前为止一切顺利。

问题

万恶之源在于Poco库的Poco/UnWindows.h。当包含 poco header 时,在某些时候 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 header 文件的哪个版本。

现在想象一下,我们项目中的第一个包含是来自 Poco 的 header。这意味着,_WIN32_WINNT 将被设置为 Windows XP 并且 windows.h 将包括 windows headers Windows XP(在我看来这已经是一个坏兆头)。

不过别担心,情况会变得更糟。

如果我们向上追踪包含层次结构,我们会到达 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 headers,接下来它重新定义 _WIN32_WINNT 为 Windows 8.1。我不知道为什么这样做,也许有一个很好的理由,idk。

如果我们现在查看最上面的最小示例,我们会发现 Poco 包含在 TBB 之前。现在发生的是:

  1. 包括 Poco headers
  2. _WIN32_WINNT 设置为 Windows XP
  3. 包括windowsheaders(WindowsXP版本,因为2)
  4. _WIN32_WINNT 重置为 Windows 8.1
  5. 包含 TBB headers(windows headers 已经包含,因此 TBB 不需要在 tbb/windows_api 中再次包含它们.h)
  6. TBB 通过 _WIN32_WINNT 检查 windows 版本并识别 Windows 8.1(由 Poco 设置)
  7. TBB 认为 InitializeCriticalSectionEx 已定义,因为 Windows 版本是 8.1(或者是?Poco 说:get rekt)并且 InitializeCriticalSectionEx 已定义,因为 Windows远景
  8. 不幸的是,Poco 确保加载了 Windows XP header,因此编译器说:不。

解决方案

要么预先包含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 正在处理这个问题。可以找到更新 here