Intel TBB - 'InitializeCriticalSectionEx':未找到标识符编译器错误

Tim*_*imo 3 c++ tbb poco-libraries c++17 vcpkg

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

昨天,我已经开始使用vcpkg包管理器了.我通过vcpkg安装了OpenCV和TBB,一切似乎都有效.我创建了一个空项目,包括两者的标题,并测试新编译的库是否有效.在验证之后,我回到了我的主项目并删除了属性表,所以我可以使用vcpkg中的库.自上次成功编译以来,我没有以任何方式更改代码.

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

tbb\critical_section.h(53):错误C3861:'InitializeCriticalSectionEx':未找到标识符

有谁知道这里发生了什么或为什么会发生这个错误?

更新

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

Tim*_*imo 8

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

考虑最小的例子:

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

void main()
{   
}
Run Code Online (Sandbox Code Playgroud)

这将引发编译器错误.

追寻路径

当包含tbb.h时,critical_section.h包含在tbb.h的第51行中.但是,ciritcal_section.hpp包含machine/winwdows_api.h,它看起来像这样(不必要的东西被删除):

TBB /机/ 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
Run Code Online (Sandbox Code Playgroud)

如您所见,在检查宏之前包含了windows.h . 这个宏是在sdkddkver.h中定义的(它包含在windows.h中),iff它尚未定义(在我的例子中它设置为Win10):_WIN32_WINNT

sdkddkver.h:

#if !defined(_WIN32_WINNT) && !defined(_CHICAGO_)
#define  _WIN32_WINNT   0x0A00
#endif
Run Code Online (Sandbox Code Playgroud)

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>
Run Code Online (Sandbox Code Playgroud)

预处理器检查,如果_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
...
Run Code Online (Sandbox Code Playgroud)

好笑,不是吗?首先,它包括UnWindows.h,它设置_WIN32_WINNT并导致包含Windows XP标头,然后重新定义_WIN32_WINNT为Windows 8.1.我不知道为什么会这样,也许有一个很好的理由,idk.

如果我们现在查看最顶层的最小示例,我们会看到Poco包含在TBB之前.现在发生的是:

  1. 包括Poco标题
  2. 设置_WIN32_WINNT为Windows XP
  3. 包含Windows标题(Windows XP版本,因为2)
  4. 重置_WIN32_WINNT为Windows 8.1
  5. 包括TBB标头(已包含Windows标头,因此TBB不需要再次在tbb/windows_api.h中包含它们)
  6. TBB检查Windows版本_WIN32_WINNT并识别Windows 8.1(由Poco设置)
  7. TBB认为InitializeCriticalSectionEx是定义的,因为Windows版本是8.1(或者是它?Poco说:得到rekt)并且InitializeCriticalSectionEx是自Windows Vista以来定义的.
  8. 不幸的是,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()
{   
}
Run Code Online (Sandbox Code Playgroud)

也许Poco贡献者的某些人可以在这里澄清一些事情.Poco版本是用x64(通过vcpkg)构建的1.8.1-1.

更新

Poco就是这个问题.更新可以在这里找到.