Files
json/tests/src/unit-no_automatic_udls.cpp
Niels Lohmann 6fc0d501f3 Move the user-defined string literals to <nlohmann/json_literals.hpp> and add JSON_NO_AUTOMATIC_UDLS (#5610)
* Add JSON_NO_UDLS to leave out the user-defined string literals

The bodies of operator""_json and operator""_json_pointer call the
parser, so every translation unit including the library instantiates it,
even if it never parses anything. Defining JSON_NO_UDLS leaves the
literals out entirely, which saves 15-35% compile time for such
translation units (#5294). Nothing changes if the macro is not defined.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Mention JSON_NO_UDLS in the list of exported module symbols

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Move the user-defined string literals to <nlohmann/json_literals.hpp>

Following the review in #5294, the literals now live in their own header
instead of being removed entirely: <nlohmann/json.hpp> includes it at the
end unless JSON_NO_AUTOMATIC_UDLS (renamed from JSON_NO_UDLS) is defined,
so a project can opt out globally and include the header only where the
literals are used.

The header only uses public and standard macros, because the library's
internal macros are undefined at the end of json.hpp and the amalgamation
inlines macro_scope.hpp only once. For the same reason, the library no
longer defines and undefines JSON_USE_GLOBAL_UDLS, so a user's definition
is still visible to the header. The single-header copy is identical to
the multi-header one, as it only includes <nlohmann/json.hpp>. The module
always exports the literals.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Fix CI: include cycle, GCC 4.8 literal operator spacing, and global UDLs off in the JSON_NO_AUTOMATIC_UDLS test

- Suppress clang-tidy misc-header-include-cycle on the intentional mutual
  include of json.hpp and json_literals.hpp.
- Use operator"" _json with a space for GCC 4.8 in the test's detection
  aliases, as the header does.
- Only test the global literal operators when JSON_USE_GLOBAL_UDLS is on.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

* Declare the literal operators through a local macro

The GCC 4.8 spacing condition was repeated for both operator definitions
and the global using-declarations. NLOHMANN_JSON_LITERAL_OPERATOR(suffix)
now selects operator""##suffix or operator"" suffix in one place and is
undefined at the end of json_literals.hpp.

Suggested by gregmarr in review.

Signed-off-by: Niels Lohmann <mail@nlohmann.me>

---------

Signed-off-by: Niels Lohmann <mail@nlohmann.me>
2026-09-30 20:08:17 +02:00

109 lines
3.8 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
// This translation unit checks JSON_NO_AUTOMATIC_UDLS, which keeps
// <nlohmann/json.hpp> from including <nlohmann/json_literals.hpp> and thereby
// leaves out the user-defined string literals operator""_json and
// operator""_json_pointer (see #5294), and that including
// <nlohmann/json_literals.hpp> afterwards brings them back.
#define JSON_NO_AUTOMATIC_UDLS 1
#include "doctest_compatibility.h"
#include <cstddef>
#include <utility>
#include <nlohmann/json.hpp>
using json = nlohmann::json;
// An argument type whose associated namespace is the library namespace, so
// argument-dependent lookup of a literal operator called by its function name
// also searches the inline namespaces nlohmann::literals::json_literals.
NLOHMANN_JSON_NAMESPACE_BEGIN
struct no_automatic_udls_probe
{
operator const char* () const // NOLINT(google-explicit-constructor,hicpp-explicit-conversions)
{
return "";
}
};
NLOHMANN_JSON_NAMESPACE_END
namespace
{
// The calls below use a dependent argument, so a literal operator that is not
// declared at all is a substitution failure rather than a hard error: lookup is
// deferred to the point of instantiation, where it considers the declarations
// visible from here (the global using-declarations of JSON_USE_GLOBAL_UDLS) plus
// argument-dependent lookup (the literals in the library namespace).
#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9)
template<typename T>
using json_udl_t = decltype(operator""_json(std::declval<T>(), std::size_t()));
template<typename T>
using json_pointer_udl_t = decltype(operator""_json_pointer(std::declval<T>(), std::size_t()));
#else
// GCC 4.8 requires a space between "" and suffix
template<typename T>
using json_udl_t = decltype(operator"" _json(std::declval<T>(), std::size_t()));
template<typename T>
using json_pointer_udl_t = decltype(operator"" _json_pointer(std::declval<T>(), std::size_t()));
#endif
template<typename T>
using has_json_udl = nlohmann::detail::is_detected<json_udl_t, T>;
template<typename T>
using has_json_pointer_udl = nlohmann::detail::is_detected<json_pointer_udl_t, T>;
} // namespace
TEST_CASE("JSON_NO_AUTOMATIC_UDLS")
{
SECTION("literals are not declared")
{
// global namespace (JSON_USE_GLOBAL_UDLS defaults to 1)
CHECK_FALSE(has_json_udl<const char*>::value);
CHECK_FALSE(has_json_pointer_udl<const char*>::value);
// nlohmann::literals::json_literals
CHECK_FALSE(has_json_udl<nlohmann::no_automatic_udls_probe>::value);
CHECK_FALSE(has_json_pointer_udl<nlohmann::no_automatic_udls_probe>::value);
}
SECTION("the rest of the library keeps working")
{
const json j = json::parse(R"({"foo": {"bar": 42}})");
CHECK(j.dump() == R"({"foo":{"bar":42}})");
const json::json_pointer ptr("/foo/bar");
CHECK(j.at(ptr) == 42);
CHECK(j.contains(ptr));
}
}
// the literals can still be added where they are needed
#include <nlohmann/json_literals.hpp>
TEST_CASE("JSON_NO_AUTOMATIC_UDLS with <nlohmann/json_literals.hpp>")
{
#if !defined(JSON_USE_GLOBAL_UDLS) || JSON_USE_GLOBAL_UDLS
SECTION("global namespace")
{
CHECK("[1,2]"_json == json({1, 2}));
CHECK("/a/0"_json_pointer == json::json_pointer("/a/0"));
}
#endif
SECTION("nlohmann::literals::json_literals")
{
using namespace nlohmann::literals::json_literals; // NOLINT(google-build-using-namespace)
CHECK(R"({"a":[42]})"_json.at("/a/0"_json_pointer) == 42);
}
}