From 82667aadfff2bf0c3d248af43ab0a068ad4bd7b2 Mon Sep 17 00:00:00 2001 From: Niels Lohmann Date: Mon, 28 Sep 2026 02:10:24 +0200 Subject: [PATCH] Convert one-element tuples to arrays on every compiler to_json for std::tuple assigns a braced list, j = { std::get(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{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 --- include/nlohmann/detail/conversions/to_json.hpp | 13 +++++++------ single_include/nlohmann/json.hpp | 13 +++++++------ 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/include/nlohmann/detail/conversions/to_json.hpp b/include/nlohmann/detail/conversions/to_json.hpp index 491bb9873..672086675 100644 --- a/include/nlohmann/detail/conversions/to_json.hpp +++ b/include/nlohmann/detail/conversions/to_json.hpp @@ -471,11 +471,13 @@ inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence< j = { std::get(t)... }; } -#if JSON_BRACE_INIT_COPY_SEMANTICS -// JSON_BRACE_INIT_COPY_SEMANTICS makes a one-element braced list copy its -// element instead of wrapping it, which would serialize std::tuple{5} as 5 -// rather than [5]. Build what the default deduction builds instead: an object -// if the element is a [string, value] pair, a one-element array otherwise. +// A one-element braced list does not reliably wrap its element: with +// JSON_BRACE_INIT_COPY_SEMANTICS it copies it, which would serialize +// std::tuple{5} as 5 rather than [5], and some compilers (e.g., Apple clang +// 15 and 16) copy an element that is itself a basic_json even without it, so +// std::tuple{true} became true rather than [true]. Build what the default +// deduction builds instead: an object if the element is a [string, value] pair, +// a one-element array otherwise. template inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<0> /*unused*/) { @@ -493,7 +495,6 @@ inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence< j = BasicJsonType::array({std::move(element)}); } } -#endif template inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& /*unused*/, index_sequence<> /*unused*/) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 225b08010..dc9721b01 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -7016,11 +7016,13 @@ inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence< j = { std::get(t)... }; } -#if JSON_BRACE_INIT_COPY_SEMANTICS -// JSON_BRACE_INIT_COPY_SEMANTICS makes a one-element braced list copy its -// element instead of wrapping it, which would serialize std::tuple{5} as 5 -// rather than [5]. Build what the default deduction builds instead: an object -// if the element is a [string, value] pair, a one-element array otherwise. +// A one-element braced list does not reliably wrap its element: with +// JSON_BRACE_INIT_COPY_SEMANTICS it copies it, which would serialize +// std::tuple{5} as 5 rather than [5], and some compilers (e.g., Apple clang +// 15 and 16) copy an element that is itself a basic_json even without it, so +// std::tuple{true} became true rather than [true]. Build what the default +// deduction builds instead: an object if the element is a [string, value] pair, +// a one-element array otherwise. template inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence<0> /*unused*/) { @@ -7038,7 +7040,6 @@ inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& t, index_sequence< j = BasicJsonType::array({std::move(element)}); } } -#endif template inline void to_json_tuple_impl(BasicJsonType& j, const Tuple& /*unused*/, index_sequence<> /*unused*/)