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は最低限必要です)。
これは、 here で説明されている場合にstd::visit
がbad_variant_access
例外をスローし、その例外の実装がlibc ++の新しいバージョンに依存するため、iOSのバージョンを使用する必要があり、この新しいバージョンを出荷するmacOS(macOS 10.14およびiOS 12)。
ありがたいことに、c ++例外が有効になっている場合に利用可能な実装パスがありますoffこれは新しいlibc ++に依存しないため、可能であればそのオプションを使用できます。
追伸最小展開ターゲットを10.14に増やしても、10.13でプログラムを正常に実行できる場合については、この新しい例外がトリガーされる時点で問題が発生すると推測しています(依存する例外メソッドlibc ++の新しいバージョンは解決されません)。
std::variant
をスローする可能性のあるstd::bad_variant_access
機能はすべて、標準ヘッダーファイルでmacOS 10.14(および対応するiOS、tvOS、watchOS)以降で使用可能としてマークされています。これは、仮想std::bad_variant_access::what()
メソッドがinline
ではなく、libc++.dylib
(OSが提供)で定義されているためです。
いくつかの回避策があります(すべて技術的にはndefined behaviour)。私の好みに合わせて並べ替えています。
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いインターフェイスに対してラッパーを書くことができます。
_LIBCPP_DISABLE_AVAILABILITY
をプロジェクト設定に追加プリプロセッサマクロ(GCC_PREPROCESSOR_DEFINITIONS
)
Con:これは、他の可用性ガード(shared_mutex
、bad_optional_access
など)も抑制します。
すでに動作している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:元のインターフェイスを維持します。
次の行をプロジェクトソースファイルの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バージョンで動作します。