mirror of
https://github.com/nlohmann/json.git
synced 2026-10-16 11:17:13 +00:00
Fix CI failures on develop from the API performance stack
- Mark the parameters of the new out-of-line throw helpers in json.hpp as used, as JSON_THROW drops its argument with JSON_NOEXCEPTION (clang -Wunused-parameter). - all_children_leaves() iterates with begin()/end() via std::all_of: a custom array_t such as the one in the custom_array_type example has no rbegin() (examples check, clang-tidy readability-use-anyofallof). - The rvalue to_json overloads (#5837) copy the elements of containers with const elements such as std::set instead of moving them: GCC 4.9's std::move_iterator cannot bind them. Object types are ruled out before std::ranges::view is checked, which needs an incomplete ordered_json for its object_t (GCC 11, C++20, unit-regression3 #3312). - NOLINT the rvalue to_json parameters that are deliberately not forwarded, and fix the other clang-tidy findings (modernize-use-auto in zmij.hpp, readability-math-missing-parentheses in json_pointer.hpp). - cpplint: NOLINT(readability/braces) on the new scalar fast-path macro like on JSON_IMPLEMENT_OPERATOR, and include <map> in json.hpp. - Tests: drop a useless cast (GCC -Wuseless-cast) and a signed/unsigned comparison (MSVC C4018), wrap a vector<json> comparison in parentheses so doctest does not try to print it (ambiguous operator<< on MSVC), and also strip JSON_DIAGNOSTIC_POSITIONS' "(bytes x-y) " from the patch messages in unit-throw_messages. - Windows clang (MinGW): link with LLD. unit-msgpack.cpp (C++11) and unit-regression2.cpp (C++20) have more than 32767 sections again (33363 and 33197), which GNU ld cannot link; LLD links them. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This commit is contained in:
10 files changed
+137
-67
No files matched your search
@@ -166,19 +166,21 @@ jobs:
|
|||||||
# link, but the binaries clang 11.0.1 and clang 18.1.8 then produce crash
|
# link, but the binaries clang 11.0.1 and clang 18.1.8 then produce crash
|
||||||
# before doctest prints its first line - 39 of 102 tests on clang 18.
|
# before doctest prints its first line - 39 of 102 tests on clang 18.
|
||||||
# Keep the objects small by splitting the test files instead.
|
# Keep the objects small by splitting the test files instead.
|
||||||
# Objects with more than 65535 sections fail to link with "relocation
|
# The tests link with LLD: GNU ld fails on objects with more than 32767
|
||||||
# truncated to fit: IMAGE_REL_AMD64_REL32 against `.rdata'": the MinGW
|
# sections with "relocation truncated to fit: IMAGE_REL_AMD64_REL32
|
||||||
# linker stores the section an associative COMDAT section belongs to in
|
# against `.rdata'", as it stores the section an associative COMDAT
|
||||||
# 16 bits (x_associated in binutils' include/coff/internal.h), so it
|
# section belongs to in 16 bits (x_associated in binutils'
|
||||||
# discards the jump tables of inline functions together with the wrong
|
# include/coff/internal.h) and so discards the jump tables of inline
|
||||||
# function. Each basic_json specialization a test file instantiates adds
|
# functions together with the wrong function. Each basic_json
|
||||||
# many sections, so test a second one in a file of its own.
|
# specialization and input type a test file instantiates adds many
|
||||||
|
# sections, so unit-msgpack.cpp and unit-regression2.cpp kept crossing
|
||||||
|
# that limit as the library grew.
|
||||||
- name: Run CMake
|
- name: Run CMake
|
||||||
run: cmake -S . -B build ^
|
run: cmake -S . -B build ^
|
||||||
-DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" ^
|
-DCMAKE_CXX_COMPILER="C:/Program Files/LLVM/bin/clang++.exe" ^
|
||||||
-DCMAKE_CXX_FLAGS="--target=x86_64-w64-mingw32 -stdlib=libstdc++ -pthread" ^
|
-DCMAKE_CXX_FLAGS="--target=x86_64-w64-mingw32 -stdlib=libstdc++ -pthread" ^
|
||||||
-DCMAKE_CXX_FLAGS_DEBUG="-g0" ^
|
-DCMAKE_CXX_FLAGS_DEBUG="-g0" ^
|
||||||
-DCMAKE_EXE_LINKER_FLAGS="-lwinpthread" ^
|
-DCMAKE_EXE_LINKER_FLAGS="-fuse-ld=lld -lwinpthread" ^
|
||||||
-G"MinGW Makefiles" ^
|
-G"MinGW Makefiles" ^
|
||||||
-DCMAKE_BUILD_TYPE=Debug ^
|
-DCMAKE_BUILD_TYPE=Debug ^
|
||||||
-DJSON_BuildTests=On
|
-DJSON_BuildTests=On
|
||||||
|
|||||||
@@ -13,7 +13,7 @@
|
|||||||
#include <optional> // optional
|
#include <optional> // optional
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <iterator> // begin, end
|
#include <iterator> // begin, end, make_move_iterator, move_iterator
|
||||||
#include <memory> // allocator_traits
|
#include <memory> // allocator_traits
|
||||||
#include <string> // basic_string, char_traits
|
#include <string> // basic_string, char_traits
|
||||||
#include <tuple> // tuple, get
|
#include <tuple> // tuple, get
|
||||||
@@ -48,6 +48,32 @@ namespace detail
|
|||||||
* leave j with the type of the new value, but the pointer to the destroyed old one.
|
* leave j with the type of the new value, but the pointer to the destroyed old one.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// a move iterator over the elements of an rvalue container, unless they are
|
||||||
|
// const (e.g., std::set): libstdc++ before GCC 5 defines the reference of a
|
||||||
|
// std::move_iterator as value_type&&, which cannot bind to a const element
|
||||||
|
template<typename Iterator>
|
||||||
|
std::move_iterator<Iterator> make_move_iterator_if_mutable_impl(Iterator it, std::true_type /*mutable*/)
|
||||||
|
{
|
||||||
|
return std::make_move_iterator(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Iterator>
|
||||||
|
Iterator make_move_iterator_if_mutable_impl(Iterator it, std::false_type /*mutable*/)
|
||||||
|
{
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Iterator>
|
||||||
|
using is_mutable_iterator = std::integral_constant < bool,
|
||||||
|
!std::is_const<typename std::remove_reference<decltype(*std::declval<Iterator&>())>::type>::value >;
|
||||||
|
|
||||||
|
template<typename Iterator>
|
||||||
|
auto make_move_iterator_if_mutable(Iterator it)
|
||||||
|
-> decltype(make_move_iterator_if_mutable_impl(it, is_mutable_iterator<Iterator> {}))
|
||||||
|
{
|
||||||
|
return make_move_iterator_if_mutable_impl(it, is_mutable_iterator<Iterator> {});
|
||||||
|
}
|
||||||
|
|
||||||
template<value_t> struct external_constructor;
|
template<value_t> struct external_constructor;
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
@@ -174,7 +200,7 @@ struct external_constructor<value_t::array>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::array_t>(std::make_move_iterator(begin(arr)), std::make_move_iterator(end(arr))));
|
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::array_t>(make_move_iterator_if_mutable(begin(arr)), make_move_iterator_if_mutable(end(arr))));
|
||||||
j.replace_value(value_t::array, value);
|
j.replace_value(value_t::array, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,7 +261,7 @@ struct external_constructor<value_t::object>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::object_t>(std::make_move_iterator(begin(obj)), std::make_move_iterator(end(obj))));
|
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::object_t>(make_move_iterator_if_mutable(begin(obj)), make_move_iterator_if_mutable(end(obj))));
|
||||||
j.replace_value(value_t::object, value);
|
j.replace_value(value_t::object, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -413,25 +439,27 @@ using is_movable_container_rvalue = negation<disjunction<
|
|||||||
std::is_const<T>,
|
std::is_const<T>,
|
||||||
has_dedicated_to_json<T>,
|
has_dedicated_to_json<T>,
|
||||||
is_basic_json<T>,
|
is_basic_json<T>,
|
||||||
is_range_view_type<T>,
|
|
||||||
is_enum_keyed_map_type<T>,
|
is_enum_keyed_map_type<T>,
|
||||||
std::is_same<T, std::vector<bool>>>>;
|
std::is_same<T, std::vector<bool>>>>;
|
||||||
|
|
||||||
// rvalue array-like containers
|
// rvalue array-like containers; object types are ruled out before views are
|
||||||
|
// checked, as std::ranges::view<T> for the object_t of an ordered_json would
|
||||||
|
// need that ordered_json to be complete (GCC 11, see #3312)
|
||||||
template<typename BasicJsonType, typename T>
|
template<typename BasicJsonType, typename T>
|
||||||
using is_rvalue_array_to_json = conjunction<is_movable_container_rvalue<T>,
|
using is_rvalue_array_to_json = conjunction<is_movable_container_rvalue<T>,
|
||||||
|
negation<is_compatible_object_type<BasicJsonType, T>>,
|
||||||
|
negation<is_range_view_type<T>>,
|
||||||
is_compatible_array_type<BasicJsonType, T>,
|
is_compatible_array_type<BasicJsonType, T>,
|
||||||
negation<disjunction<
|
negation<disjunction<
|
||||||
std::is_same<T, typename BasicJsonType::array_t>,
|
std::is_same<T, typename BasicJsonType::array_t>,
|
||||||
std::is_same<T, typename BasicJsonType::binary_t>,
|
std::is_same<T, typename BasicJsonType::binary_t>,
|
||||||
is_compatible_object_type<BasicJsonType, T>,
|
|
||||||
is_compatible_string_type<BasicJsonType, T>,
|
is_compatible_string_type<BasicJsonType, T>,
|
||||||
is_compatible_binary_type<BasicJsonType, T>>>>;
|
is_compatible_binary_type<BasicJsonType, T>>>>;
|
||||||
|
|
||||||
// rvalue containers: their elements are moved instead of copied
|
// rvalue containers: their elements are moved instead of copied
|
||||||
template<typename BasicJsonType, typename CompatibleArrayType,
|
template<typename BasicJsonType, typename CompatibleArrayType,
|
||||||
enable_if_t<is_rvalue_array_to_json<BasicJsonType, CompatibleArrayType>::value, int> = 0>
|
enable_if_t<is_rvalue_array_to_json<BasicJsonType, CompatibleArrayType>::value, int> = 0>
|
||||||
inline void to_json(BasicJsonType& j, CompatibleArrayType && arr)
|
inline void to_json(BasicJsonType& j, CompatibleArrayType && arr) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
{
|
{
|
||||||
external_constructor<value_t::array>::construct_moving(j, arr);
|
external_constructor<value_t::array>::construct_moving(j, arr);
|
||||||
}
|
}
|
||||||
@@ -509,7 +537,8 @@ inline void to_json(BasicJsonType& j, const EnumKeyedMap& map)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// rvalue object-like containers
|
// rvalue object-like containers (views have no key_type and mapped_type, so
|
||||||
|
// they are no compatible object type)
|
||||||
template<typename BasicJsonType, typename T>
|
template<typename BasicJsonType, typename T>
|
||||||
using is_rvalue_object_to_json = conjunction<is_movable_container_rvalue<T>,
|
using is_rvalue_object_to_json = conjunction<is_movable_container_rvalue<T>,
|
||||||
negation<std::is_same<T, typename BasicJsonType::object_t>>,
|
negation<std::is_same<T, typename BasicJsonType::object_t>>,
|
||||||
@@ -517,7 +546,7 @@ using is_rvalue_object_to_json = conjunction<is_movable_container_rvalue<T>,
|
|||||||
|
|
||||||
template<typename BasicJsonType, typename CompatibleObjectType,
|
template<typename BasicJsonType, typename CompatibleObjectType,
|
||||||
enable_if_t<is_rvalue_object_to_json<BasicJsonType, CompatibleObjectType>::value, int> = 0>
|
enable_if_t<is_rvalue_object_to_json<BasicJsonType, CompatibleObjectType>::value, int> = 0>
|
||||||
inline void to_json(BasicJsonType& j, CompatibleObjectType && obj)
|
inline void to_json(BasicJsonType& j, CompatibleObjectType && obj) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
{
|
{
|
||||||
external_constructor<value_t::object>::construct_moving(j, obj);
|
external_constructor<value_t::object>::construct_moving(j, obj);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ JSON_HEDLEY_ALWAYS_INLINE shortest_decimal to_shortest(std::uint64_t bits) noexc
|
|||||||
const uint128_parts p = umul192_hi128(p10.high, p10.low, bin_sig << static_cast<unsigned>(shift));
|
const uint128_parts p = umul192_hi128(p10.high, p10.low, bin_sig << static_cast<unsigned>(shift));
|
||||||
std::uint64_t integral = p.high >> static_cast<unsigned>(extra_shift);
|
std::uint64_t integral = p.high >> static_cast<unsigned>(extra_shift);
|
||||||
// (no casts in the shifts: MSVC 2015 warns C4554 on them despite the parentheses)
|
// (no casts in the shifts: MSVC 2015 warns C4554 on them despite the parentheses)
|
||||||
constexpr unsigned fractional_low_shift = static_cast<unsigned>(extra_shift);
|
constexpr auto fractional_low_shift = static_cast<unsigned>(extra_shift);
|
||||||
constexpr unsigned fractional_high_shift = 64u - fractional_low_shift;
|
constexpr unsigned fractional_high_shift = 64u - fractional_low_shift;
|
||||||
const std::uint64_t fractional = (p.high << fractional_high_shift) | (p.low >> fractional_low_shift);
|
const std::uint64_t fractional = (p.high << fractional_high_shift) | (p.low >> fractional_low_shift);
|
||||||
std::uint64_t digit = 0;
|
std::uint64_t digit = 0;
|
||||||
|
|||||||
@@ -334,7 +334,7 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
break; // not a plain number: let strtoull decide
|
break; // not a plain number: let strtoull decide
|
||||||
}
|
}
|
||||||
acc = static_cast<size_type>(acc * 10 + static_cast<size_type>(c - '0'));
|
acc = static_cast<size_type>((acc * 10) + static_cast<size_type>(c - '0'));
|
||||||
if (JSON_HEDLEY_UNLIKELY(i == s.size() - 1))
|
if (JSON_HEDLEY_UNLIKELY(i == s.size() - 1))
|
||||||
{
|
{
|
||||||
idx = acc;
|
idx = acc;
|
||||||
|
|||||||
+17
-15
@@ -29,6 +29,7 @@
|
|||||||
#endif // JSON_NO_IO
|
#endif // JSON_NO_IO
|
||||||
#include <iterator> // make_move_iterator, random_access_iterator_tag
|
#include <iterator> // make_move_iterator, random_access_iterator_tag
|
||||||
#include <limits> // numeric_limits
|
#include <limits> // numeric_limits
|
||||||
|
#include <map> // map
|
||||||
#include <memory> // unique_ptr
|
#include <memory> // unique_ptr
|
||||||
#include <set> // swap, operator!=
|
#include <set> // swap, operator!=
|
||||||
#include <stdexcept> // length_error
|
#include <stdexcept> // length_error
|
||||||
@@ -754,26 +755,20 @@ private:
|
|||||||
// one level deep); stops at the first structured child it meets
|
// one level deep); stops at the first structured child it meets
|
||||||
static bool all_children_leaves(const basic_json& v) noexcept
|
static bool all_children_leaves(const basic_json& v) noexcept
|
||||||
{
|
{
|
||||||
|
// array_t only needs forward iterators (see custom_array_type)
|
||||||
if (v.m_data.m_type == value_t::array)
|
if (v.m_data.m_type == value_t::array)
|
||||||
{
|
{
|
||||||
const auto& a = *v.m_data.m_value.array;
|
const auto& a = *v.m_data.m_value.array;
|
||||||
for (auto it = a.rbegin(); it != a.rend(); ++it)
|
return std::all_of(a.begin(), a.end(), [](const basic_json & el)
|
||||||
{
|
{
|
||||||
if (!has_no_children(*it))
|
return has_no_children(el);
|
||||||
{
|
});
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
for (const auto& el : *v.m_data.m_value.object)
|
const auto& o = *v.m_data.m_value.object;
|
||||||
|
return std::all_of(o.begin(), o.end(), [](const typename object_t::value_type & el)
|
||||||
{
|
{
|
||||||
if (!has_no_children(el.second))
|
return has_no_children(el.second);
|
||||||
{
|
});
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The walk in destroy_container() may take the children of a
|
// The walk in destroy_container() may take the children of a
|
||||||
@@ -3043,18 +3038,21 @@ public:
|
|||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_iterator_range_of_null(const basic_json* obj)
|
static void throw_iterator_range_of_null(const basic_json* obj)
|
||||||
{
|
{
|
||||||
|
static_cast<void>(obj); // unused when JSON_NOEXCEPTION is defined
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_of_null, detail::concat("cannot construct with iterators from ", obj->type_name()), obj));
|
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_of_null, detail::concat("cannot construct with iterators from ", obj->type_name()), obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_incompatible_reference_type(const basic_json& obj)
|
static void throw_incompatible_reference_type(const basic_json& obj)
|
||||||
{
|
{
|
||||||
|
static_cast<void>(obj); // unused when JSON_NOEXCEPTION is defined
|
||||||
JSON_THROW(type_error::create(detail::exception_id::incompatible_reference_type, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
|
JSON_THROW(type_error::create(detail::exception_id::incompatible_reference_type, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_array_index_exceeds_size_type(size_type idx)
|
static void throw_array_index_exceeds_size_type(size_type idx)
|
||||||
{
|
{
|
||||||
|
static_cast<void>(idx); // unused when JSON_NOEXCEPTION is defined
|
||||||
JSON_THROW(std::length_error(detail::concat("array index ", std::to_string(idx), " exceeds size_type")));
|
JSON_THROW(std::length_error(detail::concat("array index ", std::to_string(idx), " exceeds size_type")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3062,6 +3060,8 @@ public:
|
|||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_array_index_out_of_range(size_type idx, const basic_json& j)
|
static void throw_array_index_out_of_range(size_type idx, const basic_json& j)
|
||||||
{
|
{
|
||||||
|
static_cast<void>(idx); // unused when JSON_NOEXCEPTION is defined
|
||||||
|
static_cast<void>(j);
|
||||||
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), &j));
|
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3070,6 +3070,8 @@ public:
|
|||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_key_not_found(const KeyType& key, const basic_json& j)
|
static void throw_key_not_found(const KeyType& key, const basic_json& j)
|
||||||
{
|
{
|
||||||
|
static_cast<void>(key); // unused when JSON_NOEXCEPTION is defined
|
||||||
|
static_cast<void>(j);
|
||||||
JSON_THROW(out_of_range::create(detail::exception_id::key_not_found, detail::concat("key '", key, "' not found"), &j));
|
JSON_THROW(out_of_range::create(detail::exception_id::key_not_found, detail::concat("key '", key, "' not found"), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -5453,7 +5455,7 @@ public:
|
|||||||
// inline fast path for two values of the same non-owning scalar type; anything
|
// inline fast path for two values of the same non-owning scalar type; anything
|
||||||
// else falls through to the out-of-line general comparison
|
// else falls through to the out-of-line general comparison
|
||||||
#define JSON_IMPLEMENT_SCALAR_FAST(op, null_result) \
|
#define JSON_IMPLEMENT_SCALAR_FAST(op, null_result) \
|
||||||
if (lhs.m_data.m_type == rhs.m_data.m_type) \
|
if (lhs.m_data.m_type == rhs.m_data.m_type) /* NOLINT(readability/braces) */ \
|
||||||
{ \
|
{ \
|
||||||
switch (lhs.m_data.m_type) \
|
switch (lhs.m_data.m_type) \
|
||||||
{ \
|
{ \
|
||||||
|
|||||||
Generated
+57
-26
@@ -29,6 +29,7 @@
|
|||||||
#endif // JSON_NO_IO
|
#endif // JSON_NO_IO
|
||||||
#include <iterator> // make_move_iterator, random_access_iterator_tag
|
#include <iterator> // make_move_iterator, random_access_iterator_tag
|
||||||
#include <limits> // numeric_limits
|
#include <limits> // numeric_limits
|
||||||
|
#include <map> // map
|
||||||
#include <memory> // unique_ptr
|
#include <memory> // unique_ptr
|
||||||
#include <set> // swap, operator!=
|
#include <set> // swap, operator!=
|
||||||
#include <stdexcept> // length_error
|
#include <stdexcept> // length_error
|
||||||
@@ -6555,7 +6556,7 @@ NLOHMANN_JSON_NAMESPACE_END
|
|||||||
#include <optional> // optional
|
#include <optional> // optional
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <iterator> // begin, end
|
#include <iterator> // begin, end, make_move_iterator, move_iterator
|
||||||
#include <memory> // allocator_traits
|
#include <memory> // allocator_traits
|
||||||
#include <string> // basic_string, char_traits
|
#include <string> // basic_string, char_traits
|
||||||
#include <tuple> // tuple, get
|
#include <tuple> // tuple, get
|
||||||
@@ -7204,6 +7205,32 @@ namespace detail
|
|||||||
* leave j with the type of the new value, but the pointer to the destroyed old one.
|
* leave j with the type of the new value, but the pointer to the destroyed old one.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
// a move iterator over the elements of an rvalue container, unless they are
|
||||||
|
// const (e.g., std::set): libstdc++ before GCC 5 defines the reference of a
|
||||||
|
// std::move_iterator as value_type&&, which cannot bind to a const element
|
||||||
|
template<typename Iterator>
|
||||||
|
std::move_iterator<Iterator> make_move_iterator_if_mutable_impl(Iterator it, std::true_type /*mutable*/)
|
||||||
|
{
|
||||||
|
return std::make_move_iterator(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Iterator>
|
||||||
|
Iterator make_move_iterator_if_mutable_impl(Iterator it, std::false_type /*mutable*/)
|
||||||
|
{
|
||||||
|
return it;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename Iterator>
|
||||||
|
using is_mutable_iterator = std::integral_constant < bool,
|
||||||
|
!std::is_const<typename std::remove_reference<decltype(*std::declval<Iterator&>())>::type>::value >;
|
||||||
|
|
||||||
|
template<typename Iterator>
|
||||||
|
auto make_move_iterator_if_mutable(Iterator it)
|
||||||
|
-> decltype(make_move_iterator_if_mutable_impl(it, is_mutable_iterator<Iterator> {}))
|
||||||
|
{
|
||||||
|
return make_move_iterator_if_mutable_impl(it, is_mutable_iterator<Iterator> {});
|
||||||
|
}
|
||||||
|
|
||||||
template<value_t> struct external_constructor;
|
template<value_t> struct external_constructor;
|
||||||
|
|
||||||
template<>
|
template<>
|
||||||
@@ -7330,7 +7357,7 @@ struct external_constructor<value_t::array>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::array_t>(std::make_move_iterator(begin(arr)), std::make_move_iterator(end(arr))));
|
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::array_t>(make_move_iterator_if_mutable(begin(arr)), make_move_iterator_if_mutable(end(arr))));
|
||||||
j.replace_value(value_t::array, value);
|
j.replace_value(value_t::array, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7391,7 +7418,7 @@ struct external_constructor<value_t::object>
|
|||||||
using std::begin;
|
using std::begin;
|
||||||
using std::end;
|
using std::end;
|
||||||
|
|
||||||
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::object_t>(std::make_move_iterator(begin(obj)), std::make_move_iterator(end(obj))));
|
const typename BasicJsonType::json_value value(j.template create<typename BasicJsonType::object_t>(make_move_iterator_if_mutable(begin(obj)), make_move_iterator_if_mutable(end(obj))));
|
||||||
j.replace_value(value_t::object, value);
|
j.replace_value(value_t::object, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -7569,25 +7596,27 @@ using is_movable_container_rvalue = negation<disjunction<
|
|||||||
std::is_const<T>,
|
std::is_const<T>,
|
||||||
has_dedicated_to_json<T>,
|
has_dedicated_to_json<T>,
|
||||||
is_basic_json<T>,
|
is_basic_json<T>,
|
||||||
is_range_view_type<T>,
|
|
||||||
is_enum_keyed_map_type<T>,
|
is_enum_keyed_map_type<T>,
|
||||||
std::is_same<T, std::vector<bool>>>>;
|
std::is_same<T, std::vector<bool>>>>;
|
||||||
|
|
||||||
// rvalue array-like containers
|
// rvalue array-like containers; object types are ruled out before views are
|
||||||
|
// checked, as std::ranges::view<T> for the object_t of an ordered_json would
|
||||||
|
// need that ordered_json to be complete (GCC 11, see #3312)
|
||||||
template<typename BasicJsonType, typename T>
|
template<typename BasicJsonType, typename T>
|
||||||
using is_rvalue_array_to_json = conjunction<is_movable_container_rvalue<T>,
|
using is_rvalue_array_to_json = conjunction<is_movable_container_rvalue<T>,
|
||||||
|
negation<is_compatible_object_type<BasicJsonType, T>>,
|
||||||
|
negation<is_range_view_type<T>>,
|
||||||
is_compatible_array_type<BasicJsonType, T>,
|
is_compatible_array_type<BasicJsonType, T>,
|
||||||
negation<disjunction<
|
negation<disjunction<
|
||||||
std::is_same<T, typename BasicJsonType::array_t>,
|
std::is_same<T, typename BasicJsonType::array_t>,
|
||||||
std::is_same<T, typename BasicJsonType::binary_t>,
|
std::is_same<T, typename BasicJsonType::binary_t>,
|
||||||
is_compatible_object_type<BasicJsonType, T>,
|
|
||||||
is_compatible_string_type<BasicJsonType, T>,
|
is_compatible_string_type<BasicJsonType, T>,
|
||||||
is_compatible_binary_type<BasicJsonType, T>>>>;
|
is_compatible_binary_type<BasicJsonType, T>>>>;
|
||||||
|
|
||||||
// rvalue containers: their elements are moved instead of copied
|
// rvalue containers: their elements are moved instead of copied
|
||||||
template<typename BasicJsonType, typename CompatibleArrayType,
|
template<typename BasicJsonType, typename CompatibleArrayType,
|
||||||
enable_if_t<is_rvalue_array_to_json<BasicJsonType, CompatibleArrayType>::value, int> = 0>
|
enable_if_t<is_rvalue_array_to_json<BasicJsonType, CompatibleArrayType>::value, int> = 0>
|
||||||
inline void to_json(BasicJsonType& j, CompatibleArrayType && arr)
|
inline void to_json(BasicJsonType& j, CompatibleArrayType && arr) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
{
|
{
|
||||||
external_constructor<value_t::array>::construct_moving(j, arr);
|
external_constructor<value_t::array>::construct_moving(j, arr);
|
||||||
}
|
}
|
||||||
@@ -7665,7 +7694,8 @@ inline void to_json(BasicJsonType& j, const EnumKeyedMap& map)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// rvalue object-like containers
|
// rvalue object-like containers (views have no key_type and mapped_type, so
|
||||||
|
// they are no compatible object type)
|
||||||
template<typename BasicJsonType, typename T>
|
template<typename BasicJsonType, typename T>
|
||||||
using is_rvalue_object_to_json = conjunction<is_movable_container_rvalue<T>,
|
using is_rvalue_object_to_json = conjunction<is_movable_container_rvalue<T>,
|
||||||
negation<std::is_same<T, typename BasicJsonType::object_t>>,
|
negation<std::is_same<T, typename BasicJsonType::object_t>>,
|
||||||
@@ -7673,7 +7703,7 @@ using is_rvalue_object_to_json = conjunction<is_movable_container_rvalue<T>,
|
|||||||
|
|
||||||
template<typename BasicJsonType, typename CompatibleObjectType,
|
template<typename BasicJsonType, typename CompatibleObjectType,
|
||||||
enable_if_t<is_rvalue_object_to_json<BasicJsonType, CompatibleObjectType>::value, int> = 0>
|
enable_if_t<is_rvalue_object_to_json<BasicJsonType, CompatibleObjectType>::value, int> = 0>
|
||||||
inline void to_json(BasicJsonType& j, CompatibleObjectType && obj)
|
inline void to_json(BasicJsonType& j, CompatibleObjectType && obj) // NOLINT(cppcoreguidelines-missing-std-forward)
|
||||||
{
|
{
|
||||||
external_constructor<value_t::object>::construct_moving(j, obj);
|
external_constructor<value_t::object>::construct_moving(j, obj);
|
||||||
}
|
}
|
||||||
@@ -21277,7 +21307,7 @@ class json_pointer
|
|||||||
{
|
{
|
||||||
break; // not a plain number: let strtoull decide
|
break; // not a plain number: let strtoull decide
|
||||||
}
|
}
|
||||||
acc = static_cast<size_type>(acc * 10 + static_cast<size_type>(c - '0'));
|
acc = static_cast<size_type>((acc * 10) + static_cast<size_type>(c - '0'));
|
||||||
if (JSON_HEDLEY_UNLIKELY(i == s.size() - 1))
|
if (JSON_HEDLEY_UNLIKELY(i == s.size() - 1))
|
||||||
{
|
{
|
||||||
idx = acc;
|
idx = acc;
|
||||||
@@ -26454,7 +26484,7 @@ JSON_HEDLEY_ALWAYS_INLINE shortest_decimal to_shortest(std::uint64_t bits) noexc
|
|||||||
const uint128_parts p = umul192_hi128(p10.high, p10.low, bin_sig << static_cast<unsigned>(shift));
|
const uint128_parts p = umul192_hi128(p10.high, p10.low, bin_sig << static_cast<unsigned>(shift));
|
||||||
std::uint64_t integral = p.high >> static_cast<unsigned>(extra_shift);
|
std::uint64_t integral = p.high >> static_cast<unsigned>(extra_shift);
|
||||||
// (no casts in the shifts: MSVC 2015 warns C4554 on them despite the parentheses)
|
// (no casts in the shifts: MSVC 2015 warns C4554 on them despite the parentheses)
|
||||||
constexpr unsigned fractional_low_shift = static_cast<unsigned>(extra_shift);
|
constexpr auto fractional_low_shift = static_cast<unsigned>(extra_shift);
|
||||||
constexpr unsigned fractional_high_shift = 64u - fractional_low_shift;
|
constexpr unsigned fractional_high_shift = 64u - fractional_low_shift;
|
||||||
const std::uint64_t fractional = (p.high << fractional_high_shift) | (p.low >> fractional_low_shift);
|
const std::uint64_t fractional = (p.high << fractional_high_shift) | (p.low >> fractional_low_shift);
|
||||||
std::uint64_t digit = 0;
|
std::uint64_t digit = 0;
|
||||||
@@ -30788,26 +30818,20 @@ private:
|
|||||||
// one level deep); stops at the first structured child it meets
|
// one level deep); stops at the first structured child it meets
|
||||||
static bool all_children_leaves(const basic_json& v) noexcept
|
static bool all_children_leaves(const basic_json& v) noexcept
|
||||||
{
|
{
|
||||||
|
// array_t only needs forward iterators (see custom_array_type)
|
||||||
if (v.m_data.m_type == value_t::array)
|
if (v.m_data.m_type == value_t::array)
|
||||||
{
|
{
|
||||||
const auto& a = *v.m_data.m_value.array;
|
const auto& a = *v.m_data.m_value.array;
|
||||||
for (auto it = a.rbegin(); it != a.rend(); ++it)
|
return std::all_of(a.begin(), a.end(), [](const basic_json & el)
|
||||||
{
|
{
|
||||||
if (!has_no_children(*it))
|
return has_no_children(el);
|
||||||
{
|
});
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
for (const auto& el : *v.m_data.m_value.object)
|
const auto& o = *v.m_data.m_value.object;
|
||||||
|
return std::all_of(o.begin(), o.end(), [](const typename object_t::value_type & el)
|
||||||
{
|
{
|
||||||
if (!has_no_children(el.second))
|
return has_no_children(el.second);
|
||||||
{
|
});
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The walk in destroy_container() may take the children of a
|
// The walk in destroy_container() may take the children of a
|
||||||
@@ -33077,18 +33101,21 @@ public:
|
|||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_iterator_range_of_null(const basic_json* obj)
|
static void throw_iterator_range_of_null(const basic_json* obj)
|
||||||
{
|
{
|
||||||
|
static_cast<void>(obj); // unused when JSON_NOEXCEPTION is defined
|
||||||
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_of_null, detail::concat("cannot construct with iterators from ", obj->type_name()), obj));
|
JSON_THROW(invalid_iterator::create(detail::exception_id::iterator_range_of_null, detail::concat("cannot construct with iterators from ", obj->type_name()), obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_incompatible_reference_type(const basic_json& obj)
|
static void throw_incompatible_reference_type(const basic_json& obj)
|
||||||
{
|
{
|
||||||
|
static_cast<void>(obj); // unused when JSON_NOEXCEPTION is defined
|
||||||
JSON_THROW(type_error::create(detail::exception_id::incompatible_reference_type, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
|
JSON_THROW(type_error::create(detail::exception_id::incompatible_reference_type, detail::concat("incompatible ReferenceType for get_ref, actual type is ", obj.type_name()), &obj));
|
||||||
}
|
}
|
||||||
|
|
||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_array_index_exceeds_size_type(size_type idx)
|
static void throw_array_index_exceeds_size_type(size_type idx)
|
||||||
{
|
{
|
||||||
|
static_cast<void>(idx); // unused when JSON_NOEXCEPTION is defined
|
||||||
JSON_THROW(std::length_error(detail::concat("array index ", std::to_string(idx), " exceeds size_type")));
|
JSON_THROW(std::length_error(detail::concat("array index ", std::to_string(idx), " exceeds size_type")));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33096,6 +33123,8 @@ public:
|
|||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_array_index_out_of_range(size_type idx, const basic_json& j)
|
static void throw_array_index_out_of_range(size_type idx, const basic_json& j)
|
||||||
{
|
{
|
||||||
|
static_cast<void>(idx); // unused when JSON_NOEXCEPTION is defined
|
||||||
|
static_cast<void>(j);
|
||||||
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), &j));
|
JSON_THROW(out_of_range::create(detail::exception_id::array_index_out_of_range, detail::concat("array index ", std::to_string(idx), " is out of range"), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -33104,6 +33133,8 @@ public:
|
|||||||
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
JSON_HEDLEY_NO_RETURN JSON_HEDLEY_NEVER_INLINE
|
||||||
static void throw_key_not_found(const KeyType& key, const basic_json& j)
|
static void throw_key_not_found(const KeyType& key, const basic_json& j)
|
||||||
{
|
{
|
||||||
|
static_cast<void>(key); // unused when JSON_NOEXCEPTION is defined
|
||||||
|
static_cast<void>(j);
|
||||||
JSON_THROW(out_of_range::create(detail::exception_id::key_not_found, detail::concat("key '", key, "' not found"), &j));
|
JSON_THROW(out_of_range::create(detail::exception_id::key_not_found, detail::concat("key '", key, "' not found"), &j));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35487,7 +35518,7 @@ public:
|
|||||||
// inline fast path for two values of the same non-owning scalar type; anything
|
// inline fast path for two values of the same non-owning scalar type; anything
|
||||||
// else falls through to the out-of-line general comparison
|
// else falls through to the out-of-line general comparison
|
||||||
#define JSON_IMPLEMENT_SCALAR_FAST(op, null_result) \
|
#define JSON_IMPLEMENT_SCALAR_FAST(op, null_result) \
|
||||||
if (lhs.m_data.m_type == rhs.m_data.m_type) \
|
if (lhs.m_data.m_type == rhs.m_data.m_type) /* NOLINT(readability/braces) */ \
|
||||||
{ \
|
{ \
|
||||||
switch (lhs.m_data.m_type) \
|
switch (lhs.m_data.m_type) \
|
||||||
{ \
|
{ \
|
||||||
|
|||||||
@@ -300,7 +300,7 @@ TEST_CASE("copy constructor and destructor across value types")
|
|||||||
REQUIRE(v.size() == 70);
|
REQUIRE(v.size() == 70);
|
||||||
|
|
||||||
std::vector<json> copy(v);
|
std::vector<json> copy(v);
|
||||||
CHECK(copy == v);
|
CHECK((copy == v));
|
||||||
|
|
||||||
// mutating the copy leaves the original alone
|
// mutating the copy leaves the original alone
|
||||||
copy[5] = "changed";
|
copy[5] = "changed";
|
||||||
|
|||||||
@@ -1234,7 +1234,7 @@ TEST_CASE("JSON pointer array index tokens match strtoull semantics")
|
|||||||
const std::string alphabet = "0123456789012345678901234567890123456789-+ ex";
|
const std::string alphabet = "0123456789012345678901234567890123456789-+ ex";
|
||||||
for (int i = 0; i < 5000; ++i)
|
for (int i = 0; i < 5000; ++i)
|
||||||
{
|
{
|
||||||
std::string token(static_cast<std::size_t>(gen() % 23), '0');
|
std::string token(gen() % 23, '0');
|
||||||
for (auto& c : token)
|
for (auto& c : token)
|
||||||
{
|
{
|
||||||
c = alphabet[gen() % alphabet.size()];
|
c = alphabet[gen() % alphabet.size()];
|
||||||
|
|||||||
@@ -1489,7 +1489,7 @@ TEST_CASE("invalid UTF-8 at every position is handled like the reference")
|
|||||||
{
|
{
|
||||||
for (const auto& pattern : patterns)
|
for (const auto& pattern : patterns)
|
||||||
{
|
{
|
||||||
check_dump_against_reference(make_string(filler, pos, pattern, length - pos), bytes <= (filler.size() == 1 ? 17 : 8));
|
check_dump_against_reference(make_string(filler, pos, pattern, length - pos), bytes <= (filler.size() == 1 ? 17u : 8u));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,12 +105,18 @@ TEST_CASE("exception messages of out-of-line throw sites")
|
|||||||
}
|
}
|
||||||
catch (const json::exception& e)
|
catch (const json::exception& e)
|
||||||
{
|
{
|
||||||
// with JSON_DIAGNOSTICS, what() names the offending value like "(/0) "; drop it
|
// with JSON_DIAGNOSTICS, what() names the offending value like "(/0) ",
|
||||||
|
// and with JSON_DIAGNOSTIC_POSITIONS its position like "(bytes 0-2) "; drop both
|
||||||
std::string msg = e.what();
|
std::string msg = e.what();
|
||||||
const auto pos = msg.find(" (/");
|
for (const char* marker :
|
||||||
if (pos != std::string::npos)
|
{" (/", " (bytes "
|
||||||
|
})
|
||||||
{
|
{
|
||||||
msg.erase(pos + 1, msg.find(") ", pos) - pos + 1);
|
const auto pos = msg.find(marker);
|
||||||
|
if (pos != std::string::npos)
|
||||||
|
{
|
||||||
|
msg.erase(pos + 1, msg.find(") ", pos) - pos + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return msg;
|
return msg;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user