This commit is contained in:
Gareth Andrew Lloyd 2025-02-28 21:05:18 +01:00 committed by GitHub
commit fe0942314a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 153 additions and 70 deletions

View File

@ -568,8 +568,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
if (t == value_t::array || t == value_t::object) if (t == value_t::array || t == value_t::object)
{ {
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND))
try
{
#endif
// flatten the current json_value to a heap-allocated stack // flatten the current json_value to a heap-allocated stack
std::vector<basic_json> stack; std::vector<basic_json, allocator_type> stack;
// move the top-level items to stack // move the top-level items to stack
if (t == value_t::array) if (t == value_t::array)
@ -613,6 +617,16 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// it's now safe that current_item get destructed // it's now safe that current_item get destructed
// since it doesn't have any children // since it doesn't have any children
} }
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND))
}
catch (...) // NOLINT(bugprone-empty-catch)
{
// Recursion avoidance has issue allocating temporary space. This may have been `std::bad_alloc`
// or any other exception thrown by a custom allocator.
// RAII will correctly clean up anything moved into `stack`.
// Then we continue with regular recursion based destroy, which will not heap allocate.
}
#endif
} }
switch (t) switch (t)

View File

@ -20567,8 +20567,12 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
} }
if (t == value_t::array || t == value_t::object) if (t == value_t::array || t == value_t::object)
{ {
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND))
try
{
#endif
// flatten the current json_value to a heap-allocated stack // flatten the current json_value to a heap-allocated stack
std::vector<basic_json> stack; std::vector<basic_json, allocator_type> stack;
// move the top-level items to stack // move the top-level items to stack
if (t == value_t::array) if (t == value_t::array)
@ -20612,6 +20616,16 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec
// it's now safe that current_item get destructed // it's now safe that current_item get destructed
// since it doesn't have any children // since it doesn't have any children
} }
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND))
}
catch (...) // NOLINT(bugprone-empty-catch)
{
// Recursion avoidance has issue allocating temporary space. This may have been `std::bad_alloc`
// or any other exception thrown by a custom allocator.
// RAII will correctly clean up anything moved into `stack`.
// Then we continue with regular recursion based destroy, which will not heap allocate.
}
#endif
} }
switch (t) switch (t)

View File

@ -261,3 +261,58 @@ TEST_CASE("bad my_allocator::construct")
j["test"].push_back("should not leak"); j["test"].push_back("should not leak");
} }
} }
#if (defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND))
namespace
{
thread_local bool should_throw = false;
struct QuotaReached : std::exception {};
template<class T>
struct allocator_controlled_throw : std::allocator<T>
{
allocator_controlled_throw() = default;
template <class U>
allocator_controlled_throw(allocator_controlled_throw<U> /*unused*/) {}
template <class U>
struct rebind
{
using other = allocator_controlled_throw<U>;
};
T* allocate(size_t n)
{
if (should_throw)
{
throw QuotaReached{};
}
return std::allocator<T>::allocate(n);
}
};
} // namespace
TEST_CASE("controlled my_allocator::allocate")
{
SECTION("~basic_json tolerant of internal exceptions")
{
using my_alloc_json = nlohmann::basic_json<std::map,
std::vector,
std::string,
bool,
std::int64_t,
std::uint64_t,
double,
allocator_controlled_throw>;
{
auto j = my_alloc_json{1, 2, 3, 4};
should_throw = true;
// `j` is destroyed, ~basic_json is noexcept
// if allocation attempted, exception thrown
// exception should be internally handled
} // should not std::terminate
}
}
#endif