diff --git a/docs/mkdocs/docs/community/quality_assurance.md b/docs/mkdocs/docs/community/quality_assurance.md index afef80ff8..d55d32c16 100644 --- a/docs/mkdocs/docs/community/quality_assurance.md +++ b/docs/mkdocs/docs/community/quality_assurance.md @@ -135,6 +135,9 @@ Note: Some modern features (like C++20 ranges or filesystem support) may be disa headers are included. - [x] On Windows, the library is compiled with `` being included to detect and avoid common bugs (see [`unit-windows_h.cpp`](https://github.com/nlohmann/json/blob/develop/tests/src/unit-windows_h.cpp)). +- [x] The library is compiled with Qt's keyword macros (`slots`, `signals`, `emit`, `foreach`, `forever`) being defined + to make sure it does not use them as identifiers (see + [`unit-qt_keywords.cpp`](https://github.com/nlohmann/json/blob/develop/tests/src/unit-qt_keywords.cpp)). - [x] The library is compiled with exceptions disabled to support alternative means of error handling. ## Stable public API diff --git a/include/nlohmann/detail/json_pointer.hpp b/include/nlohmann/detail/json_pointer.hpp index 63ab49f7d..cc80e2451 100644 --- a/include/nlohmann/detail/json_pointer.hpp +++ b/include/nlohmann/detail/json_pointer.hpp @@ -525,7 +525,7 @@ class json_pointer {} }; - std::vector slots; + std::vector table; std::size_t used = 0; static std::size_t hash_of(std::size_t parent, const string_t& token) noexcept @@ -540,19 +540,19 @@ class json_pointer void grow() { - std::vector old(slots.size() * 2); - old.swap(slots); - const std::size_t mask = slots.size() - 1; + std::vector old(table.size() * 2); + old.swap(table); + const std::size_t mask = table.size() - 1; for (const auto& s : old) { if (s.token != nullptr) { std::size_t i = s.hash & mask; - while (slots[i].token != nullptr) + while (table[i].token != nullptr) { i = (i + 1) & mask; } - slots[i] = s; + table[i] = s; } } } @@ -565,7 +565,7 @@ class json_pointer // start with the empty prefix only prefix_tree() - : slots(16) + : table(16) , parent(1, 0) , is_array(1, false) {} @@ -575,20 +575,20 @@ class json_pointer std::size_t add_child(std::size_t id, const string_t& reference_token) { const std::size_t hash = hash_of(id, reference_token); - const std::size_t mask = slots.size() - 1; + const std::size_t mask = table.size() - 1; std::size_t i = hash & mask; - while (slots[i].token != nullptr) + while (table[i].token != nullptr) { - if (slots[i].hash == hash && slots[i].parent == id && *slots[i].token == reference_token) + if (table[i].hash == hash && table[i].parent == id && *table[i].token == reference_token) { - return slots[i].id; + return table[i].id; } i = (i + 1) & mask; } // new prefix const std::size_t child = parent.size(); - slots[i] = slot(hash, id, &reference_token, child); + table[i] = slot(hash, id, &reference_token, child); if (reference_token == "0") { is_array[id] = true; @@ -597,7 +597,7 @@ class json_pointer is_array.push_back(false); // keep the load factor below 1/2 - if (++used * 2 > slots.size()) + if (++used * 2 > table.size()) { grow(); } diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index 4debe551f..8a7fd4208 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -21497,7 +21497,7 @@ class json_pointer {} }; - std::vector slots; + std::vector table; std::size_t used = 0; static std::size_t hash_of(std::size_t parent, const string_t& token) noexcept @@ -21512,19 +21512,19 @@ class json_pointer void grow() { - std::vector old(slots.size() * 2); - old.swap(slots); - const std::size_t mask = slots.size() - 1; + std::vector old(table.size() * 2); + old.swap(table); + const std::size_t mask = table.size() - 1; for (const auto& s : old) { if (s.token != nullptr) { std::size_t i = s.hash & mask; - while (slots[i].token != nullptr) + while (table[i].token != nullptr) { i = (i + 1) & mask; } - slots[i] = s; + table[i] = s; } } } @@ -21537,7 +21537,7 @@ class json_pointer // start with the empty prefix only prefix_tree() - : slots(16) + : table(16) , parent(1, 0) , is_array(1, false) {} @@ -21547,20 +21547,20 @@ class json_pointer std::size_t add_child(std::size_t id, const string_t& reference_token) { const std::size_t hash = hash_of(id, reference_token); - const std::size_t mask = slots.size() - 1; + const std::size_t mask = table.size() - 1; std::size_t i = hash & mask; - while (slots[i].token != nullptr) + while (table[i].token != nullptr) { - if (slots[i].hash == hash && slots[i].parent == id && *slots[i].token == reference_token) + if (table[i].hash == hash && table[i].parent == id && *table[i].token == reference_token) { - return slots[i].id; + return table[i].id; } i = (i + 1) & mask; } // new prefix const std::size_t child = parent.size(); - slots[i] = slot(hash, id, &reference_token, child); + table[i] = slot(hash, id, &reference_token, child); if (reference_token == "0") { is_array[id] = true; @@ -21569,7 +21569,7 @@ class json_pointer is_array.push_back(false); // keep the load factor below 1/2 - if (++used * 2 > slots.size()) + if (++used * 2 > table.size()) { grow(); } diff --git a/tests/src/unit-qt_keywords.cpp b/tests/src/unit-qt_keywords.cpp new file mode 100644 index 000000000..1d0efaa2d --- /dev/null +++ b/tests/src/unit-qt_keywords.cpp @@ -0,0 +1,62 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ (supporting code) +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +#include "doctest_compatibility.h" + +// Qt defines its keywords as macros (see qtmetamacros.h and qforeach.h) unless +// QT_NO_KEYWORDS is defined, and it is common to include Qt headers before this +// library; it must therefore not use these words as identifiers (see #5865) +#define slots +#define signals public +#define emit +#define Q_FOREACH(variable, container) for (variable : container) +#define foreach Q_FOREACH +#define forever for (;;) + +#include +using nlohmann::json; + +TEST_CASE("Qt keyword macros") +{ + SECTION("parse and dump") + { + const json j = json::parse(R"({"a": [1, 2.5, "x", null, true]})"); + CHECK(j.dump() == R"({"a":[1,2.5,"x",null,true]})"); + } + + SECTION("flatten and unflatten") + { + const json j = {{"a", {{"b", {1, 2}}, {"c", "d"}}}, {"e", nullptr}}; + CHECK(j.flatten().unflatten() == j); + } + + SECTION("JSON pointer") + { + json j = {{"a", {{"b", {1, 2}}}}}; + CHECK(j.at(json::json_pointer("/a/b/1")) == 2); + j[json::json_pointer("/a/c")] = 3; + CHECK(j.contains(json::json_pointer("/a/c"))); + } + + SECTION("binary formats") + { + const json j = {{"a", {1, 2, 3}}, {"b", "c"}}; + CHECK(json::from_cbor(json::to_cbor(j)) == j); + CHECK(json::from_msgpack(json::to_msgpack(j)) == j); + CHECK(json::from_ubjson(json::to_ubjson(j)) == j); + CHECK(json::from_bjdata(json::to_bjdata(j)) == j); + CHECK(json::from_bson(json::to_bson(j)) == j); + } + + SECTION("patch and diff") + { + const json source = {{"a", 1}, {"b", {1, 2}}}; + const json target = {{"a", 2}, {"c", "d"}}; + CHECK(source.patch(json::diff(source, target)) == target); + } +}