Xcode 10调用不可用的函数std :: visit

dCu*_*lic 17 c++ xcode ios c++17

使用Xcode 10 GM编译以下程序时:

#include <iostream>
#include <string>
#include <variant>

void hello(int) {
    std::cout << "hello, int" << std::endl;
}

void hello(std::string const & msg) {
    std::cout << "hello, " << msg << std::endl;
}

int main(int argc, const char * argv[]) {
    // insert code here...
    std::variant< int, std::string > var;

    std::visit
    (
        []( auto parameter )
        {
            hello( parameter );
        },
        var
     );

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

main.cpp:27:5:调用不可用的函数'visit':在macOS 10.14中引入

但是,如果我将最小部署目标更改为macOS 10.14,则代码​​编译正常并且可以正常运行,即使我正在运行macOS 10.13.

由于std::visit是功能模板,并且不应该依赖于操作系统版本(我通过在较低版本的mac上运行代码而不是实际支持的证明),这应该被视为错误并报告给Apple还是这种预期的行为?

编译iOS时会出现同样的情况(iOS 12是最低限度的预期).

m1h*_*1h4 10

发生这种情况是因为在此处描述std::visitbad_variant_access情况下引发异常,并且由于该异常的实现依赖于较新版本的libc ++,因此需要使用iOS和macOS版本(macOS 10.14和iOS 12).

Thankfuly,有当一个可用的实现路径C++异常被关不依赖于新的libc ++所以如果可能的话,你可以使用该选项.

PS关于你将最低部署目标增加到10.14并且仍然能够在10.13上正常运行程序的情况我估计你会遇到问题,因为这个新异常会被触发(因为依赖的异常方法)在较新版本的libc ++上将无法解决).


Tob*_*obi 9

从标准头文件中的macOS 10.14(以及相应的iOS,tvOS和watchOS)开始,所有std::variant可能抛出的功能std::bad_variant_access都标记为可用。这是因为std::bad_variant_access::what()没有inline(因此libc++.dylib由OS提供)定义了虚拟方法。

有几种解决方法(所有技术上未定义的行为),根据我的个人喜好排序:

1)抓紧实施

std::visit仅在变体参数之一为时抛出valueless_by_exception。研究实现可以为您提供使用以下变通办法的线索(假定vs是变量的参数包):

if (... && !vs.valueless_by_exception() ) {
  std::__variant_detail::__visitation::__variant::__visit_value(visitor, vs...);
} else {
  // error handling
}
Run Code Online (Sandbox Code Playgroud)

缺点:将来的libc ++版本可能会中断。界面难看。

优点:编译器在中断时可能会大吼大叫,并且可以轻松地采用变通办法。您可以针对丑陋的界面编写包装器。

2)抑制可用性编译器错误...

添加_LIBCPP_DISABLE_AVAILABILITY到项目设置预处理器宏GCC_PREPROCESSOR_DEFINITIONS

缺点:这还将禁止其他可用性保护措施(shared_mutexbad_optional_access等等)。

2a)...并使用它

事实证明,它已经可以在High Sierra中使用了,不仅适用于Mojave(我已经测试到10.13.0)。

在10.12.6及更低版本中,您会遇到运行时错误:

dyld: Symbol not found: __ZTISt18bad_variant_access
  Referenced from: [...]/VariantAccess
  Expected in: /usr/lib/libc++.1.dylib
 in [...]/VariantAccess
Abort trap: 6
Run Code Online (Sandbox Code Playgroud)

第一行取消约束_typeinfo for std::bad_variant_access。这意味着动态链接器(dyld)找不到指向what()引言中提到的方法的vtable 。

缺点:仅适用于某些操作系统版本,只有在启动时才能了解它是否起作用。

Pro:保持原始界面。

2b)...并提供您自己的异常实现

将以下行添加到项目源文件之一:

// Strongly undefined behaviour (violates one definition rule)
const char* std::bad_variant_access::what() const noexcept {
    return "bad_variant_access";
}
Run Code Online (Sandbox Code Playgroud)

我已经针对10.10.0、10.12.6、10.13.0、10.14.1上的独立二进制文件对此进行了测试,即使在引发a std::bad_variant_access,被捕获std::exception const& ex并调用virtual的情况下,我的示例代码也可以正常工作ex.what()

缺点:我的假设是,在使用RTTI或跨二进制边界的异常处理(例如,不同的共享库)时,此技巧将失效。但这只是一个假设,这就是为什么我最后采用此解决方法:我不知道它何时会破裂以及症状会是什么。

Pro:保持原始界面。可能会在所有OS版本上运行。