mirror of
https://github.com/nlohmann/json.git
synced 2026-10-06 06:14:51 +01:00
Fix std::terminate when converting std::optional with MSVC 2017
MSVC 2017 evaluates std::is_nothrow_assignable<json&, const T&> as true even if T's to_json throws, so to_json(json&, const std::optional<T>&) was noexcept there and the exception from #5642's test called std::terminate instead of propagating. Make that conversion never noexcept on MSVC 2017; all other compilers keep the exact condition. The static_asserts on the condition are skipped for MSVC 2017; the runtime check that the exception propagates still runs there. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
@@ -287,9 +287,20 @@ struct external_constructor<value_t::object>
|
|||||||
/////////////
|
/////////////
|
||||||
|
|
||||||
#ifdef JSON_HAS_CPP_17
|
#ifdef JSON_HAS_CPP_17
|
||||||
|
// whether storing the value of a std::optional<T> cannot throw; MSVC 2017
|
||||||
|
// evaluates std::is_nothrow_assignable as true even if T's to_json throws, so
|
||||||
|
// the exception would call std::terminate (#5642)
|
||||||
|
#if defined(_MSC_VER) && !defined(__clang__) && _MSC_VER < 1920
|
||||||
|
template<typename BasicJsonType, typename T>
|
||||||
|
using is_nothrow_optional_to_json = std::false_type;
|
||||||
|
#else
|
||||||
|
template<typename BasicJsonType, typename T>
|
||||||
|
using is_nothrow_optional_to_json = std::is_nothrow_assignable<BasicJsonType&, const T&>;
|
||||||
|
#endif
|
||||||
|
|
||||||
template<typename BasicJsonType, typename T,
|
template<typename BasicJsonType, typename T,
|
||||||
enable_if_t<std::is_constructible<BasicJsonType, T>::value, int> = 0>
|
enable_if_t<std::is_constructible<BasicJsonType, T>::value, int> = 0>
|
||||||
void to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept(std::is_nothrow_assignable<BasicJsonType&, const T&>::value)
|
void to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept(is_nothrow_optional_to_json<BasicJsonType, T>::value)
|
||||||
{
|
{
|
||||||
if (opt.has_value())
|
if (opt.has_value())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -7081,9 +7081,20 @@ struct external_constructor<value_t::object>
|
|||||||
/////////////
|
/////////////
|
||||||
|
|
||||||
#ifdef JSON_HAS_CPP_17
|
#ifdef JSON_HAS_CPP_17
|
||||||
|
// whether storing the value of a std::optional<T> cannot throw; MSVC 2017
|
||||||
|
// evaluates std::is_nothrow_assignable as true even if T's to_json throws, so
|
||||||
|
// the exception would call std::terminate (#5642)
|
||||||
|
#if defined(_MSC_VER) && !defined(__clang__) && _MSC_VER < 1920
|
||||||
|
template<typename BasicJsonType, typename T>
|
||||||
|
using is_nothrow_optional_to_json = std::false_type;
|
||||||
|
#else
|
||||||
|
template<typename BasicJsonType, typename T>
|
||||||
|
using is_nothrow_optional_to_json = std::is_nothrow_assignable<BasicJsonType&, const T&>;
|
||||||
|
#endif
|
||||||
|
|
||||||
template<typename BasicJsonType, typename T,
|
template<typename BasicJsonType, typename T,
|
||||||
enable_if_t<std::is_constructible<BasicJsonType, T>::value, int> = 0>
|
enable_if_t<std::is_constructible<BasicJsonType, T>::value, int> = 0>
|
||||||
void to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept(std::is_nothrow_assignable<BasicJsonType&, const T&>::value)
|
void to_json(BasicJsonType& j, const std::optional<T>& opt) noexcept(is_nothrow_optional_to_json<BasicJsonType, T>::value)
|
||||||
{
|
{
|
||||||
if (opt.has_value())
|
if (opt.has_value())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -853,11 +853,11 @@ TEST_CASE("std::optional")
|
|||||||
CHECK_THROWS_WITH_AS(json(opt), "cannot serialize throwing_to_json_type", std::runtime_error&);
|
CHECK_THROWS_WITH_AS(json(opt), "cannot serialize throwing_to_json_type", std::runtime_error&);
|
||||||
|
|
||||||
// the conversion is noexcept exactly when converting the contained value is
|
// the conversion is noexcept exactly when converting the contained value is
|
||||||
// (MSVC 2017 evaluates the conditional noexcept of this conversion as true)
|
// (except with MSVC 2017, where it is never noexcept, see to_json.hpp)
|
||||||
#if !defined(_MSC_VER) || _MSC_VER >= 1920
|
#if !defined(_MSC_VER) || defined(__clang__) || _MSC_VER >= 1920
|
||||||
static_assert(!std::is_nothrow_constructible<json, const std::optional<throwing_to_json_type>&>::value);
|
static_assert(!std::is_nothrow_constructible<json, const std::optional<throwing_to_json_type>&>::value);
|
||||||
#endif
|
|
||||||
static_assert(std::is_nothrow_constructible<json, const std::optional<int>&>::value);
|
static_assert(std::is_nothrow_constructible<json, const std::optional<int>&>::value);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user