mirror of
https://github.com/nlohmann/json.git
synced 2026-10-02 15:05:16 +00:00
* Add JSON_DISABLE_TUPLE_REFERENCE_CONVERSION to fix std::tuple conversions basic_json can be constructed from std::tuple<json&>, which it turns into a one-element array. Because of this, std::tuple picks its converting constructor that converts the whole source tuple instead of the element-wise one. As a result, std::tuple<const json&> built from std::forward_as_tuple(j) binds to a temporary (a compile error with libc++, a dangling reference with other standard libraries), and std::tuple<json> built the same way holds [j] instead of a copy of j. The new opt-in macro JSON_DISABLE_TUPLE_REFERENCE_CONVERSION (CMake option JSON_DisableTupleReferenceConversion) removes the conversion from a one-element tuple holding a reference to the same basic_json type, so std::tuple converts element-wise. It is off by default, so existing behavior is unchanged. It does not change any function body and therefore is not part of the ABI tag. Fixes #2226 Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Convert one-element tuples to arrays on every compiler to_json for std::tuple assigns a braced list, j = { std::get<Idx>(t)... }. With a single element that is itself a basic_json, Apple clang 15 and 16 treat j = {x} as a copy of x, so std::tuple<json>{true} became true instead of [true]. The macOS jobs (Xcode 15.1, 16.1) failed the new checks in unit-disable-tuple-reference-conversion and unit-regression2. The one-element overload that already handles JSON_BRACE_INIT_COPY_SEMANTICS builds the array (or object, for a [string, value] element) explicitly, the same way the initializer-list constructor does. Use it unconditionally. The output is unchanged on compilers that already wrapped the element. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Skip json reference tuple tests on clang < 4 and GCC < 5 ci_test_compilers_gcc_old (4.8) and ci_test_compilers_clang (3.4) could not compile the new tuple tests. Creating a std::tuple of basic_json references, e.g. std::forward_as_tuple(j), makes these compilers instantiate basic_json's conversion operator for libstdc++'s internal tuple bases, which fails hard. This happens with and without JSON_DISABLE_TUPLE_REFERENCE_CONVERSION, so it is a limitation of these compilers, not of the new option. Tested with the CI images: clang 3.4 to 3.9 and GCC 4.8 and 4.9 fail, clang 4, 5, and 6 and GCC 5 and 6 compile all cases. Skip only the checks that create such tuples; the is_constructible checks still run. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
94 lines
3.4 KiB
C++
94 lines
3.4 KiB
C++
// __ _____ _____ _____
|
|
// __| | __| | | | JSON for Modern C++ (supporting code)
|
|
// | | |__ | | | | | | version 3.12.0
|
|
// |_____|_____|_____|_|___| https://github.com/nlohmann/json
|
|
//
|
|
// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann <https://nlohmann.me>
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
#include "doctest_compatibility.h"
|
|
|
|
// This file tests the opt-in JSON_DISABLE_TUPLE_REFERENCE_CONVERSION, so it
|
|
// defines the macro itself rather than relying on a -D flag, and runs in every
|
|
// build.
|
|
#ifdef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
|
#undef JSON_DISABLE_TUPLE_REFERENCE_CONVERSION
|
|
#endif
|
|
|
|
#define JSON_DISABLE_TUPLE_REFERENCE_CONVERSION 1
|
|
|
|
#include <nlohmann/json.hpp>
|
|
using nlohmann::json;
|
|
using nlohmann::ordered_json;
|
|
|
|
#include <string>
|
|
#include <tuple>
|
|
#include <type_traits>
|
|
#include <utility>
|
|
|
|
// clang before 4 and GCC before 5 cannot create a std::tuple of basic_json
|
|
// references at all, with or without JSON_DISABLE_TUPLE_REFERENCE_CONVERSION:
|
|
// the tuple constructors make them instantiate basic_json's conversion operator
|
|
// for libstdc++'s internal tuple bases, which fails hard
|
|
#if (defined(__clang__) && __clang_major__ < 4) || (!defined(__clang__) && defined(__GNUC__) && __GNUC__ < 5)
|
|
#define SKIP_TESTS_FOR_JSON_REFERENCE_TUPLES
|
|
#endif
|
|
|
|
TEST_CASE("JSON_DISABLE_TUPLE_REFERENCE_CONVERSION")
|
|
{
|
|
SECTION("json is not constructible from a one-element tuple of a json reference")
|
|
{
|
|
CHECK_FALSE(std::is_constructible<json, std::tuple<json&>>::value);
|
|
CHECK_FALSE(std::is_constructible<json, std::tuple<const json&>>::value);
|
|
CHECK_FALSE(std::is_constructible < json, std::tuple < json && >>::value);
|
|
CHECK_FALSE(std::is_constructible<json, const std::tuple<json&>&>::value);
|
|
CHECK_FALSE(std::is_constructible<ordered_json, std::tuple<ordered_json&>>::value);
|
|
}
|
|
|
|
#ifndef SKIP_TESTS_FOR_JSON_REFERENCE_TUPLES
|
|
SECTION("issue #2226 - tuple<const json&> from tuple<json&> keeps the reference")
|
|
{
|
|
json j = true;
|
|
const std::tuple<const json&> tup(std::forward_as_tuple(j));
|
|
CHECK(&std::get<0>(tup) == &j);
|
|
}
|
|
|
|
SECTION("tuple<json> from tuple<json&> copies the element")
|
|
{
|
|
const json j = {{"key", "value"}};
|
|
const std::tuple<json> t1(std::forward_as_tuple(j));
|
|
CHECK(std::get<0>(t1) == j);
|
|
|
|
json j2 = "text";
|
|
const std::tuple<json> t2(std::forward_as_tuple(std::move(j2)));
|
|
CHECK(std::get<0>(t2) == "text");
|
|
}
|
|
#endif
|
|
|
|
SECTION("other tuple conversions are not affected")
|
|
{
|
|
const json j = true;
|
|
|
|
// one-element tuple holding a json value
|
|
CHECK(json(std::make_tuple(j)) == json::array({true}));
|
|
|
|
// tuples with more than one element, even when holding references
|
|
int i = 1;
|
|
#ifndef SKIP_TESTS_FOR_JSON_REFERENCE_TUPLES
|
|
CHECK(json(std::forward_as_tuple(i, j)) == json::array({1, true}));
|
|
CHECK(json(std::forward_as_tuple(j, j)) == json::array({true, true}));
|
|
#endif
|
|
|
|
// one-element tuples holding references to other types
|
|
std::string s = "text";
|
|
CHECK(json(std::forward_as_tuple(s)) == json::array({"text"}));
|
|
CHECK(json(std::forward_as_tuple(i)) == json::array({1}));
|
|
|
|
#ifndef SKIP_TESTS_FOR_JSON_REFERENCE_TUPLES
|
|
// a reference to a different basic_json specialization
|
|
ordered_json oj = true;
|
|
CHECK(json(std::forward_as_tuple(oj)) == json::array({true}));
|
|
#endif
|
|
}
|
|
}
|