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>
This commit is contained in:
Niels Lohmann
2026-09-28 21:23:52 +02:00
parent fc03b9912e
commit dc4a70e7b4
11 changed files with 167 additions and 4 deletions
+1
View File
@@ -30,6 +30,7 @@ header. See also the [macro overview page](../../features/macros.md).
- [**JSON_HAS_THREE_WAY_COMPARISON**](json_has_three_way_comparison.md) - control 3-way comparison support
- [**JSON_NO_IO**](json_no_io.md) - switch off functions relying on certain C++ I/O headers
- [**JSON_NO_THREAD_LOCAL**](json_no_thread_local.md) - switch off the use of `thread_local` storage
- [**JSON_NO_UDLS**](json_no_udls.md) - leave out the user-defined string literals (UDLs) to reduce compile time
- [**JSON_SKIP_UNSUPPORTED_COMPILER_CHECK**](json_skip_unsupported_compiler_check.md) - do not warn about unsupported compilers
- [**JSON_USE_GLOBAL_UDLS**](json_use_global_udls.md) - place user-defined string literals (UDLs) into the global namespace
- [**JSON_USE_SIMDUTF**](json_use_simdutf.md) - use the simdutf library to accelerate UTF-8 validation
@@ -0,0 +1,61 @@
# JSON_NO_UDLS
```cpp
#define JSON_NO_UDLS
```
When defined, the user-defined string literals [`operator""_json`](../operator_literal_json.md) and
[`operator""_json_pointer`](../operator_literal_json_pointer.md) are not defined, neither in the namespace
`nlohmann::literals::json_literals` nor in the global namespace (regardless of
[`JSON_USE_GLOBAL_UDLS`](json_use_global_udls.md)).
The literals are ordinary inline functions whose bodies call the parser, so every translation unit that includes the
library instantiates the parser — even if it never parses anything itself. Defining `JSON_NO_UDLS` avoids this and
reduces the compile time of such translation units (e.g., ones that only define types and conversions or pass `json`
values around). Everything else in the library is unaffected; use [`parse`](../basic_json/parse.md) and the
[`json_pointer`](../json_pointer/json_pointer.md) constructor instead of the literals.
## Default definition
By default, `#!cpp JSON_NO_UDLS` is not defined.
```cpp
#undef JSON_NO_UDLS
```
## Notes
!!! info "Per translation unit"
The macro only removes declarations, so it can be defined for some translation units and not for others. Code that
uses `_json` or `_json_pointer` fails to compile when `JSON_NO_UDLS` is defined.
## Examples
??? example
The code below leaves out the user-defined string literals and uses `parse` and the `json_pointer` constructor
instead.
```cpp
#define JSON_NO_UDLS 1
#include <nlohmann/json.hpp>
int main()
{
// auto j = R"({"foo": 42})"_json; // This line would fail to compile
auto j = nlohmann::json::parse(R"({"foo": 42})");
auto p = nlohmann::json::json_pointer("/foo");
return j.at(p) == 42 ? 0 : 1;
}
```
## See also
- [`operator""_json`](../operator_literal_json.md)
- [`operator""_json_pointer`](../operator_literal_json_pointer.md)
- [`JSON_USE_GLOBAL_UDLS`](json_use_global_udls.md) - place user-defined string literals (UDLs) into the global namespace
## Version history
- Added in version 3.13.0.
@@ -32,6 +32,10 @@ When the macro is not defined, the library will define it to its default value.
[`JSON_GlobalUDLs`](../../integration/cmake.md#json_globaludls) (`ON` by default) which defines
`JSON_USE_GLOBAL_UDLS` accordingly.
!!! info "Leaving out the literals"
If [`JSON_NO_UDLS`](json_no_udls.md) is defined, the literals are not defined at all and this macro has no effect.
## Examples
??? example "Example 1: Default behavior"
@@ -92,6 +96,7 @@ When the macro is not defined, the library will define it to its default value.
- [`operator""_json`](../operator_literal_json.md)
- [`operator""_json_pointer`](../operator_literal_json_pointer.md)
- [`JSON_NO_UDLS`](json_no_udls.md) - leave out the user-defined string literals entirely
- [:simple-cmake: JSON_GlobalUDLs](../../integration/cmake.md#json_globaludls) - CMake option to control the macro
## Version history
@@ -18,7 +18,8 @@ using namespace nlohmann;
```
This is suggested to ease migration to the next major version release of the library. See
[`JSON_USE_GLOBAL_UDLS`](macros/json_use_global_udls.md#notes) for details.
[`JSON_USE_GLOBAL_UDLS`](macros/json_use_global_udls.md#notes) for details. The operator is not defined if
[`JSON_NO_UDLS`](macros/json_no_udls.md) is defined.
## Parameters
@@ -59,6 +60,7 @@ Linear.
## See also
- [Creating JSON values](../features/creating_values.md) - the article on creating JSON values
- [JSON_NO_UDLS](macros/json_no_udls.md) - leave out the user-defined string literals
## Version history
@@ -17,7 +17,8 @@ using namespace nlohmann::literals::json_literals;
using namespace nlohmann;
```
This is suggested to ease migration to the next major version release of the library. See
[`JSON_USE_GLOBAL_UDLS`](macros/json_use_global_udls.md#notes) for details.
[`JSON_USE_GLOBAL_UDLS`](macros/json_use_global_udls.md#notes) for details. The operator is not defined if
[`JSON_NO_UDLS`](macros/json_no_udls.md) is defined.
## Parameters
@@ -58,6 +59,7 @@ Linear.
## See also
- [json_pointer](json_pointer/index.md) - type to represent JSON Pointers
- [JSON_NO_UDLS](macros/json_no_udls.md) - leave out the user-defined string literals
## Version history
+8
View File
@@ -99,6 +99,14 @@ same values and the same comparisons.
See [full documentation of `JSON_NO_THREAD_LOCAL`](../api/macros/json_no_thread_local.md).
## `JSON_NO_UDLS`
When defined, the user-defined string literals `operator""_json` and `operator""_json_pointer` are left out entirely. This
reduces the compile time of translation units that do not use them, because the literals would otherwise instantiate
the parser in every translation unit that includes the library.
See [full documentation of `JSON_NO_UDLS`](../api/macros/json_no_udls.md).
## `JSON_PRECISE_STREAM_POSITION`
When defined to `1`, [`operator>>`](../api/operator_gtgt.md) and non-strict
+1
View File
@@ -296,6 +296,7 @@ nav:
- 'JSON_NOEXCEPTION': api/macros/json_noexception.md
- 'JSON_NO_IO': api/macros/json_no_io.md
- 'JSON_NO_THREAD_LOCAL': api/macros/json_no_thread_local.md
- 'JSON_NO_UDLS': api/macros/json_no_udls.md
- 'JSON_PRECISE_STREAM_POSITION': api/macros/json_precise_stream_position.md
- 'JSON_SKIP_LIBRARY_VERSION_CHECK': api/macros/json_skip_library_version_check.md
- 'JSON_SKIP_UNSUPPORTED_COMPILER_CHECK': api/macros/json_skip_unsupported_compiler_check.md
+3 -1
View File
@@ -6423,6 +6423,7 @@ std::string format_as(const NLOHMANN_BASIC_JSON_TPL& j)
return j.dump();
}
#ifndef JSON_NO_UDLS
inline namespace literals
{
inline namespace json_literals
@@ -6472,6 +6473,7 @@ inline nlohmann::json::json_pointer operator""_json_pointer(const char8_t* s, st
} // namespace json_literals
} // namespace literals
#endif // JSON_NO_UDLS
NLOHMANN_JSON_NAMESPACE_END
///////////////////////
@@ -6606,7 +6608,7 @@ struct formatter<nlohmann::NLOHMANN_BASIC_JSON_TPL, char> // NOLINT(cert-dcl58-c
} // namespace std
#if JSON_USE_GLOBAL_UDLS
#if JSON_USE_GLOBAL_UDLS && !defined(JSON_NO_UDLS)
#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0)
using nlohmann::literals::json_literals::operator""_json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
using nlohmann::literals::json_literals::operator""_json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
+3 -1
View File
@@ -32504,6 +32504,7 @@ std::string format_as(const NLOHMANN_BASIC_JSON_TPL& j)
return j.dump();
}
#ifndef JSON_NO_UDLS
inline namespace literals
{
inline namespace json_literals
@@ -32553,6 +32554,7 @@ inline nlohmann::json::json_pointer operator""_json_pointer(const char8_t* s, st
} // namespace json_literals
} // namespace literals
#endif // JSON_NO_UDLS
NLOHMANN_JSON_NAMESPACE_END
///////////////////////
@@ -32687,7 +32689,7 @@ struct formatter<nlohmann::NLOHMANN_BASIC_JSON_TPL, char> // NOLINT(cert-dcl58-c
} // namespace std
#if JSON_USE_GLOBAL_UDLS
#if JSON_USE_GLOBAL_UDLS && !defined(JSON_NO_UDLS)
#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0)
using nlohmann::literals::json_literals::operator""_json; // NOLINT(misc-unused-using-decls,google-global-names-in-headers)
using nlohmann::literals::json_literals::operator""_json_pointer; //NOLINT(misc-unused-using-decls,google-global-names-in-headers)
+2
View File
@@ -32,6 +32,7 @@ using NLOHMANN_JSON_NAMESPACE::ordered_json;
using NLOHMANN_JSON_NAMESPACE::ordered_map;
using NLOHMANN_JSON_NAMESPACE::to_string;
#ifndef JSON_NO_UDLS
inline namespace literals
{
inline namespace json_literals
@@ -40,6 +41,7 @@ inline namespace json_literals
using NLOHMANN_JSON_NAMESPACE::literals::json_literals::operator""_json_pointer;
} // namespace json_literals
} // namespace literals
#endif
// Note: the following nlohmann::detail symbols must be exported due to
// an MSVC bug failing to compile without these symbols visible (ticket #3970)
+77
View File
@@ -0,0 +1,77 @@
// __ _____ _____ _____
// __| | __| | | | 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_UDLS, which leaves out the user-defined
// string literals operator""_json and operator""_json_pointer entirely (see
// #5294), while the rest of the library keeps working.
#define JSON_NO_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_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).
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()));
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_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_udls_probe>::value);
CHECK_FALSE(has_json_pointer_udl<nlohmann::no_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));
}
}