| // __ _____ _____ _____ | |
| // __| | __| | | | 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_BRACE_INIT_COPY_SEMANTICS, so it defines the | |
| // macro itself rather than relying on a -D flag, and runs in every build. | |
| #ifdef JSON_BRACE_INIT_COPY_SEMANTICS | |
| #undef JSON_BRACE_INIT_COPY_SEMANTICS | |
| #endif | |
| #define JSON_BRACE_INIT_COPY_SEMANTICS 1 | |
| #include <nlohmann/json.hpp> | |
| using nlohmann::json; | |
| #include <array> | |
| #include <list> | |
| #include <map> | |
| #include <string> | |
| #include <tuple> | |
| #include <utility> | |
| #include <vector> | |
| #define STRINGIZE_EX(x) #x | |
| #define STRINGIZE(x) STRINGIZE_EX(x) | |
| TEST_CASE("JSON_BRACE_INIT_COPY_SEMANTICS") | |
| { | |
| SECTION("the macro is part of the ABI tag") | |
| { | |
| const std::string ns = STRINGIZE(NLOHMANN_JSON_NAMESPACE); | |
| // other tags may come before it, e.g. json_abi_ldvcmp_bics | |
| CHECK(ns.find("_bics") != std::string::npos); | |
| } | |
| SECTION("single-element brace initialization copies the element (#5074)") | |
| { | |
| json const j_obj = {{"key", "value"}, {"num", 42}}; | |
| json const j_arr = {1, 2, 3}; | |
| // object: brace init copies instead of wrapping | |
| json const j1{j_obj}; | |
| CHECK(j1.is_object()); | |
| CHECK(j1 == j_obj); | |
| // array: brace init copies instead of wrapping | |
| json const j2{j_arr}; | |
| CHECK(j2.is_array()); | |
| CHECK(j2.size() == 3); | |
| CHECK(j2 == j_arr); | |
| // this applies to any single element, not only to JSON values | |
| json const j3{true}; | |
| CHECK(j3.is_boolean()); | |
| json const j4{42}; | |
| CHECK(j4.is_number_integer()); | |
| json const j5 = {1}; | |
| CHECK(j5 == 1); | |
| json const j6 = {"text"}; | |
| CHECK(j6 == "text"); | |
| json const j7 = {{1, 2}}; | |
| CHECK(j7 == json::array({1, 2})); | |
| } | |
| SECTION("single-element brace initialization copies a pair-shaped array value (#5662)") | |
| { | |
| // a JSON value that happens to be a 2-element array whose first | |
| // element is a string must still be copied, not turned into an | |
| // object; only a braced list written in the source, such as the | |
| // inner {"key", "value"} of {{"key", "value"}}, describes an object | |
| json const pair_shaped = json::array({"key", 42}); | |
| json const j1{pair_shaped}; | |
| CHECK(j1.is_array()); | |
| CHECK(j1 == pair_shaped); | |
| json const j2 = {pair_shaped}; | |
| CHECK(j2.is_array()); | |
| CHECK(j2 == pair_shaped); | |
| // the same holds for an rvalue of the same shape | |
| json const j3{json::array({"key", 42})}; | |
| CHECK(j3.is_array()); | |
| CHECK(j3 == pair_shaped); | |
| } | |
| SECTION("what the macro does not change") | |
| { | |
| // lists with more than one element are unaffected | |
| json const j1 = {1, 2}; | |
| CHECK(j1.is_array()); | |
| CHECK(j1.size() == 2); | |
| // a single [string, value] pair still describes an object | |
| json const j2 = {{"key", "value"}}; | |
| CHECK(j2.is_object()); | |
| CHECK(j2["key"] == "value"); | |
| // json::array() always creates an array | |
| json const j3 = json::array({1}); | |
| CHECK(j3.is_array()); | |
| CHECK(j3.size() == 1); | |
| CHECK(j3[0] == 1); | |
| json const j_obj = {{"key", "value"}}; | |
| json const j4 = json::array({j_obj}); | |
| CHECK(j4.is_array()); | |
| CHECK(j4.size() == 1); | |
| CHECK(j4[0] == j_obj); | |
| } | |
| SECTION("conversions build the same values as without the macro") | |
| { | |
| SECTION("one-element std::tuple") | |
| { | |
| json const j1 = std::tuple<int> {5}; | |
| CHECK(j1.dump() == "[5]"); | |
| CHECK(std::get<0>(j1.get<std::tuple<int>>()) == 5); | |
| json const j2 = std::tuple<std::string> {"text"}; | |
| CHECK(j2.dump() == "[\"text\"]"); | |
| CHECK(std::get<0>(j2.get<std::tuple<std::string>>()) == "text"); | |
| json const j3 = std::tuple<json> {json::array({1, 2})}; | |
| CHECK(j3.dump() == "[[1,2]]"); | |
| // as without the macro, a [string, value] pair becomes an object | |
| // member (see the known limitation documented for std::pair) | |
| json const j4 = std::tuple<std::pair<std::string, int>> {{"a", 1}}; | |
| CHECK(j4.dump() == "{\"a\":1}"); | |
| } | |
| SECTION("tuples with more elements") | |
| { | |
| json const j1 = std::tuple<int, std::string> {1, "a"}; | |
| CHECK(j1.dump() == "[1,\"a\"]"); | |
| json const j2 = std::tuple<> {}; | |
| CHECK(j2.dump() == "[]"); | |
| } | |
| SECTION("one-element containers") | |
| { | |
| json const j1 = std::vector<int> {1}; | |
| CHECK(j1.dump() == "[1]"); | |
| CHECK(j1.get<std::vector<int>>() == std::vector<int> {1}); | |
| std::array<int, 1> const arr = {{1}}; | |
| json const j2 = arr; | |
| CHECK(j2.dump() == "[1]"); | |
| json const j3 = std::list<std::string> {"a"}; | |
| CHECK(j3.dump() == "[\"a\"]"); | |
| json const j4 = std::map<std::string, int> {{"a", 1}}; | |
| CHECK(j4.dump() == "{\"a\":1}"); | |
| json const j5 = std::map<int, int> {{1, 2}}; | |
| CHECK(j5.dump() == "[[1,2]]"); | |
| } | |
| SECTION("std::pair") | |
| { | |
| json const j = std::pair<int, int> {1, 2}; | |
| CHECK(j.dump() == "[1,2]"); | |
| CHECK((j.get<std::pair<int, int>>() == std::pair<int, int> {1, 2})); | |
| } | |
| SECTION("items()") | |
| { | |
| json j_obj = {{"key", 1}}; | |
| for (const auto& el : j_obj.items()) | |
| { | |
| json const j = el; | |
| CHECK(j.dump() == "{\"key\":1}"); | |
| } | |
| } | |
| } | |
| } |