web-dev-qa-db-ja.com

使用できない関数std :: visitへのXcode 10呼び出し

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;
}

次のエラーが表示されます。

main.cpp:27:5:利用できない関数「visit」の呼び出し:macOS 10.14で導入

ただし、最小展開ターゲットをmacOS 10.14に変更すると、macOS 10.13を実行している場合でも、コードは正常にコンパイルされ、機能します。

std::visitは関数テンプレートであり、OSバージョン(実際にサポートされているよりも低いバージョンのmacでコードを実行することで証明した)に依存すべきではないため、これをバグと見なしてAppleまたはこれは予想される動作ですか?

IOS用にコンパイルする場合も同じことが起こります(iOS 12は最低限必要です)。

17
dCubelic

これは、 here で説明されている場合にstd::visitbad_variant_access例外をスローし、その例外の実装がlibc ++の新しいバージョンに依存するため、iOSのバージョンを使用する必要があり、この新しいバージョンを出荷するmacOS(macOS 10.14およびiOS 12)。

ありがたいことに、c ++例外が有効になっている場合に利用可能な実装パスがありますoffこれは新しいlibc ++に依存しないため、可能であればそのオプションを使用できます。

追伸最小展開ターゲットを10.14に増やしても、10.13でプログラムを正常に実行できる場合については、この新しい例外がトリガーされる時点で問題が発生すると推測しています(依存する例外メソッドlibc ++の新しいバージョンは解決されません)。

10
m1h4

std::variantをスローする可能性のあるstd::bad_variant_access機能はすべて、標準ヘッダーファイルでmacOS 10.14(および対応するiOS、tvOS、watchOS)以降で使用可能としてマークされています。これは、仮想std::bad_variant_access::what()メソッドがinlineではなく、libc++.dylib(OSが提供)で定義されているためです。

いくつかの回避策があります(すべて技術的にはndefined behaviour)。私の好みに合わせて並べ替えています。

1)実装を把握する

std::visitは、バリアント引数の1つがvalueless_by_exceptionである場合にのみスローされます。実装を調べると、次の回避策を使用する手がかりが得られます(vsがバリアントのパラメーターパックであると想定)

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

Con:将来のlibc ++バージョンでは動作しなくなる可能性があります。 Uいインターフェイス。

Pro:コンパイラーが壊れると大声で叫ぶでしょう。回避策は簡単に適応できます。 wrapperいインターフェイスに対してラッパーを書くことができます。

2)可用性コンパイラエラーの抑制...

_LIBCPP_DISABLE_AVAILABILITYをプロジェクト設定に追加プリプロセッサマクロGCC_PREPROCESSOR_DEFINITIONS

Con:これは、他の可用性ガード(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

ここで、最初の行は_typeinfo for std::bad_variant_accessに変換されます。これは、動的リンカー(dyld)が、はじめに述べたwhat()メソッドを指すvtableを見つけることができないことを意味します。

Con:特定のOSバージョンでのみ動作します。動作しない場合は起動時にのみ知ることができます。

Pro:元のインターフェイスを維持します。

2b)...そして、独自の例外実装を提供する

次の行をプロジェクトソースファイルの1つに追加します。

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

10.10.0、10.12.6、10.13.0、10.14.1のスタンドアロンバイナリでこれをテストしました。サンプルコードは、std::bad_variant_accessをスローし、std::exception const& exでキャッチし、仮想ex.what()

Con:バイナリの境界を越えてRTTIまたは例外処理(たとえば、異なる共有オブジェクトライブラリ)を使用すると、このトリックが壊れることが前提です。しかし、これは仮定に過ぎず、だからこそこの回避策を最後に置いたのです。いつ壊れるのか、どのような症状になるのかはわかりません。

Pro:元のインターフェイスを維持します。おそらくすべてのOSバージョンで動作します。

9
Tobi