#pragma once // Workarounds for macos compatibility. On macOS we aren't allowed to touch anything in // std::variant that could throw if compiling with a target <10.14 because Apple fails hard at // properly updating their STL. Thus, if compiling in such a mode, we have to introduce // workarounds. // // This header defines a `var` namespace with `var::get` and `var::visit` implementations. On // everything except broken backwards macos, this is just an alias to `std`. On broken backwards // macos, we provide implementations that throw std::runtime_error in failure cases since the // std::bad_variant_access exception can't be touched. // // You also get a BROKEN_APPLE_VARIANT macro defined if targetting a problematic mac architecture. #include #ifdef __APPLE__ # include # if defined(__APPLE__) && MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_14 # define BROKEN_APPLE_VARIANT # endif #endif #ifndef BROKEN_APPLE_VARIANT namespace var = std; // Oh look, actual C++17 support #else // Oh look, apple. namespace var { // Apple won't let us use std::visit or std::get if targetting some version of macos earlier than // 10.14 because Apple is awful about not updating their STL. So we have to provide our own, and // then call these without `std::` -- on crappy macos we'll come here, on everything else we'll ADL // to the std:: implementation. template constexpr T& get(std::variant& var) { if (auto* v = std::get_if(&var)) return *v; throw std::runtime_error{"Bad variant access -- variant does not contain the requested type"}; } template constexpr const T& get(const std::variant& var) { if (auto* v = std::get_if(&var)) return *v; throw std::runtime_error{"Bad variant access -- variant does not contain the requested type"}; } template constexpr const T&& get(const std::variant&& var) { if (auto* v = std::get_if(&var)) return std::move(*v); throw std::runtime_error{"Bad variant access -- variant does not contain the requested type"}; } template constexpr T&& get(std::variant&& var) { if (auto* v = std::get_if(&var)) return std::move(*v); throw std::runtime_error{"Bad variant access -- variant does not contain the requested type"}; } template constexpr auto& get(std::variant& var) { if (auto* v = std::get_if(&var)) return *v; throw std::runtime_error{"Bad variant access -- variant does not contain the requested type"}; } template constexpr const auto& get(const std::variant& var) { if (auto* v = std::get_if(&var)) return *v; throw std::runtime_error{"Bad variant access -- variant does not contain the requested type"}; } template constexpr const auto&& get(const std::variant&& var) { if (auto* v = std::get_if(&var)) return std::move(*v); throw std::runtime_error{"Bad variant access -- variant does not contain the requested type"}; } template constexpr auto&& get(std::variant&& var) { if (auto* v = std::get_if(&var)) return std::move(*v); throw std::runtime_error{"Bad variant access -- variant does not contain the requested type"}; } template constexpr auto visit_helper(Visitor&& vis, Variant&& var) { if (var.index() == I) return vis(var::get(std::forward(var))); else if constexpr (sizeof...(More) > 0) return visit_helper(std::forward(vis), std::forward(var)); else throw std::runtime_error{"Bad visit -- variant is valueless"}; } template constexpr auto visit_helper(Visitor&& vis, Variant&& var, std::index_sequence) { return visit_helper(std::forward(vis), std::forward(var)); } // Only handle a single variant here because multi-variant invocation is notably harder (and we // don't need it). template constexpr auto visit(Visitor&& vis, Variant&& var) { return visit_helper(std::forward(vis), std::forward(var), std::make_index_sequence>>{}); } } // namespace var #endif