diff --git a/.github/workflows/check_amalgamation.yml b/.github/workflows/check_amalgamation.yml index 670269cbb..35f3a57f7 100644 --- a/.github/workflows/check_amalgamation.yml +++ b/.github/workflows/check_amalgamation.yml @@ -67,6 +67,7 @@ jobs: python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json.json -s . python3 $TOOL_DIR/amalgamate.py -c $TOOL_DIR/config_json_fwd.json -s . + cp include/nlohmann/json_literals.hpp $INCLUDE_DIR/json_literals.hpp # the header list of the Bazel "json" target must match the files in include/ cmake -P cmake/scripts/gen_bazel_build_file.cmake diff --git a/BUILD.bazel b/BUILD.bazel index 899f9196d..03f73fa92 100644 --- a/BUILD.bazel +++ b/BUILD.bazel @@ -67,6 +67,7 @@ cc_library( "include/nlohmann/detail/value_t.hpp", "include/nlohmann/json.hpp", "include/nlohmann/json_fwd.hpp", + "include/nlohmann/json_literals.hpp", "include/nlohmann/ordered_map.hpp", "include/nlohmann/thirdparty/hedley/hedley.hpp", "include/nlohmann/thirdparty/hedley/hedley_undef.hpp", diff --git a/Makefile b/Makefile index 196f87243..ccea759af 100644 --- a/Makefile +++ b/Makefile @@ -21,6 +21,8 @@ TESTS_SRCS=$(shell find tests -type f \( -name '*.hpp' -o -name '*.cpp' -o -name # the single headers (amalgamated from the source files) AMALGAMATED_FILE=single_include/nlohmann/json.hpp AMALGAMATED_FWD_FILE=single_include/nlohmann/json_fwd.hpp +# json_literals.hpp only includes , so it is copied verbatim +AMALGAMATED_LITERALS_FILE=single_include/nlohmann/json_literals.hpp ########################################################################## @@ -29,7 +31,7 @@ AMALGAMATED_FWD_FILE=single_include/nlohmann/json_fwd.hpp # main target all: - @echo "amalgamate - amalgamate files single_include/nlohmann/json{,_fwd}.hpp from the include/nlohmann sources" + @echo "amalgamate - amalgamate files single_include/nlohmann/json{,_fwd,_literals}.hpp from the include/nlohmann sources" @echo "BUILD.bazel - regenerate the Bazel BUILD file from the include/nlohmann sources" @echo "ChangeLog.md - generate ChangeLog file" @echo "check-amalgamation - check whether sources have been amalgamated and BUILD.bazel is up to date" @@ -154,14 +156,14 @@ install_astyle: # call the Artistic Style pretty printer on all source files pretty: install_astyle - $(ASTYLE) --project=tools/astyle/.astylerc $(SRCS) $(TESTS_SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) docs/mkdocs/docs/examples/*.cpp + $(ASTYLE) --project=tools/astyle/.astylerc $(SRCS) $(TESTS_SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE) docs/mkdocs/docs/examples/*.cpp # call the Clang-Format on all source files pretty_format: for FILE in $(SRCS) $(TESTS_SRCS) $(AMALGAMATED_FILE) docs/mkdocs/docs/examples/*.cpp; do echo $$FILE; clang-format -i $$FILE; done # create single header files and pretty print -amalgamate: $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) +amalgamate: $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE) $(MAKE) pretty # call the amalgamation tool for json.hpp @@ -172,16 +174,23 @@ $(AMALGAMATED_FILE): $(SRCS) $(AMALGAMATED_FWD_FILE): $(SRCS) tools/amalgamate/amalgamate.py -c tools/amalgamate/config_json_fwd.json -s . --verbose=yes +# copy json_literals.hpp +$(AMALGAMATED_LITERALS_FILE): include/nlohmann/json_literals.hpp + cp include/nlohmann/json_literals.hpp $(AMALGAMATED_LITERALS_FILE) + # check if file single_include/nlohmann/json.hpp has been amalgamated from the nlohmann sources # Note: this target is called by Travis check-amalgamation: @mv $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~ @mv $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_FWD_FILE)~ + @mv $(AMALGAMATED_LITERALS_FILE) $(AMALGAMATED_LITERALS_FILE)~ @$(MAKE) amalgamate @diff $(AMALGAMATED_FILE) $(AMALGAMATED_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_FILE)~ $(AMALGAMATED_FILE) ; false) @diff $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_FWD_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_FWD_FILE)~ $(AMALGAMATED_FWD_FILE) ; false) + @diff $(AMALGAMATED_LITERALS_FILE) $(AMALGAMATED_LITERALS_FILE)~ || (echo "===================================================================\n Amalgamation required! Please read the contribution guidelines\n in file .github/CONTRIBUTING.md.\n===================================================================" ; mv $(AMALGAMATED_LITERALS_FILE)~ $(AMALGAMATED_LITERALS_FILE) ; false) @mv $(AMALGAMATED_FILE)~ $(AMALGAMATED_FILE) @mv $(AMALGAMATED_FWD_FILE)~ $(AMALGAMATED_FWD_FILE) + @mv $(AMALGAMATED_LITERALS_FILE)~ $(AMALGAMATED_LITERALS_FILE) @mv BUILD.bazel BUILD.bazel~ @$(MAKE) BUILD.bazel @diff BUILD.bazel BUILD.bazel~ || (echo "===================================================================\n BUILD.bazel is out of date! Please run 'make BUILD.bazel'.\n===================================================================" ; mv BUILD.bazel~ BUILD.bazel ; false) @@ -222,7 +231,7 @@ json.tar.xz: # We use `-X` to make the resulting ZIP file reproducible, see # . include.zip: BUILD.bazel - zip -9 --recurse-paths -X include.zip $(SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) BUILD.bazel MODULE.bazel meson.build LICENSE.MIT + zip -9 --recurse-paths -X include.zip $(SRCS) $(AMALGAMATED_FILE) $(AMALGAMATED_FWD_FILE) $(AMALGAMATED_LITERALS_FILE) BUILD.bazel MODULE.bazel meson.build LICENSE.MIT # Create the files for a release and add signatures and hashes. release: include.zip json.tar.xz @@ -231,10 +240,12 @@ release: include.zip json.tar.xz gpg --armor --detach-sig include.zip gpg --armor --detach-sig $(AMALGAMATED_FILE) gpg --armor --detach-sig $(AMALGAMATED_FWD_FILE) + gpg --armor --detach-sig $(AMALGAMATED_LITERALS_FILE) gpg --armor --detach-sig json.tar.xz cp $(AMALGAMATED_FILE) release_files cp $(AMALGAMATED_FWD_FILE) release_files - mv $(AMALGAMATED_FILE).asc $(AMALGAMATED_FWD_FILE).asc json.tar.xz json.tar.xz.asc include.zip include.zip.asc release_files + cp $(AMALGAMATED_LITERALS_FILE) release_files + mv $(AMALGAMATED_FILE).asc $(AMALGAMATED_FWD_FILE).asc $(AMALGAMATED_LITERALS_FILE).asc json.tar.xz json.tar.xz.asc include.zip include.zip.asc release_files cd release_files ; shasum -a 256 json.hpp include.zip json.tar.xz > hashes.txt diff --git a/cmake/ci.cmake b/cmake/ci.cmake index f7695c36e..a001a05ae 100644 --- a/cmake/ci.cmake +++ b/cmake/ci.cmake @@ -373,9 +373,10 @@ file(GLOB_RECURSE INDENT_FILES set(include_dir ${PROJECT_SOURCE_DIR}/single_include/nlohmann) set(tool_dir ${PROJECT_SOURCE_DIR}/tools/amalgamate) add_custom_target(ci_test_amalgamation - COMMAND rm -fr ${include_dir}/json.hpp~ ${include_dir}/json_fwd.hpp~ + COMMAND rm -fr ${include_dir}/json.hpp~ ${include_dir}/json_fwd.hpp~ ${include_dir}/json_literals.hpp~ COMMAND cp ${include_dir}/json.hpp ${include_dir}/json.hpp~ COMMAND cp ${include_dir}/json_fwd.hpp ${include_dir}/json_fwd.hpp~ + COMMAND cp ${include_dir}/json_literals.hpp ${include_dir}/json_literals.hpp~ COMMAND ${Python3_EXECUTABLE} -mvenv venv_astyle COMMAND venv_astyle/bin/pip3 --quiet install -r ${CMAKE_SOURCE_DIR}/tools/astyle/requirements.txt @@ -383,10 +384,12 @@ add_custom_target(ci_test_amalgamation COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json.json -s . COMMAND ${Python3_EXECUTABLE} ${tool_dir}/amalgamate.py -c ${tool_dir}/config_json_fwd.json -s . + COMMAND cp ${PROJECT_SOURCE_DIR}/include/nlohmann/json_literals.hpp ${include_dir}/json_literals.hpp COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=none ${include_dir}/json.hpp ${include_dir}/json_fwd.hpp COMMAND diff ${include_dir}/json.hpp~ ${include_dir}/json.hpp COMMAND diff ${include_dir}/json_fwd.hpp~ ${include_dir}/json_fwd.hpp + COMMAND diff ${include_dir}/json_literals.hpp~ ${include_dir}/json_literals.hpp COMMAND venv_astyle/bin/astyle --project=tools/astyle/.astylerc --suffix=orig ${INDENT_FILES} COMMAND for FILE in `find . -name '*.orig'`\; do false \; done diff --git a/docs/mkdocs/docs/api/macros/index.md b/docs/mkdocs/docs/api/macros/index.md index bf773b5c4..9d2636eb2 100644 --- a/docs/mkdocs/docs/api/macros/index.md +++ b/docs/mkdocs/docs/api/macros/index.md @@ -28,6 +28,7 @@ header. See also the [macro overview page](../../features/macros.md). - [**JSON_HAS_RANGES**](json_has_ranges.md) - control `std::ranges` support - [**JSON_HAS_STD_FORMAT**](json_has_std_format.md) - control `std::format`/`std::formatter` support - [**JSON_HAS_THREE_WAY_COMPARISON**](json_has_three_way_comparison.md) - control 3-way comparison support +- [**JSON_NO_AUTOMATIC_UDLS**](json_no_automatic_udls.md) - do not include the user-defined string literals (UDLs) automatically - [**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_SKIP_UNSUPPORTED_COMPILER_CHECK**](json_skip_unsupported_compiler_check.md) - do not warn about unsupported compilers diff --git a/docs/mkdocs/docs/api/macros/json_no_automatic_udls.md b/docs/mkdocs/docs/api/macros/json_no_automatic_udls.md new file mode 100644 index 000000000..acb6a356e --- /dev/null +++ b/docs/mkdocs/docs/api/macros/json_no_automatic_udls.md @@ -0,0 +1,68 @@ +# JSON_NO_AUTOMATIC_UDLS + +```cpp +#define JSON_NO_AUTOMATIC_UDLS +``` + +When defined, `` does not include ``, so the user-defined string +literals [`operator""_json`](../operator_literal_json.md) and +[`operator""_json_pointer`](../operator_literal_json_pointer.md) are not declared. Include +`` in the files that use them. + +The literals are ordinary inline functions whose bodies call the parser, so every translation unit that includes them +instantiates the parser — even if it never parses anything itself. Defining `JSON_NO_AUTOMATIC_UDLS` for a whole project +avoids this cost in translation units that do not parse (e.g., ones that only define types and conversions or pass +`json` values around) and reduces their compile time. + +## Default definition + +By default, `#!cpp JSON_NO_AUTOMATIC_UDLS` is not defined, and `` includes +``. + +```cpp +#undef JSON_NO_AUTOMATIC_UDLS +``` + +## Notes + +!!! info "Header ``" + + The header includes `` itself and places the literals according to + [`JSON_USE_GLOBAL_UDLS`](json_use_global_udls.md). It is part of the multi-header sources (`include/nlohmann`) + and of the single-header sources (`single_include/nlohmann`), next to `json.hpp`. + +!!! info "C++ modules" + + The `nlohmann.json` [module](../../features/modules.md) always exports the literals, regardless of this macro. + +## Examples + +??? example + + The code below includes the library without the literals and adds them in a single translation unit. + + ```cpp + // compiled with -DJSON_NO_AUTOMATIC_UDLS for the whole project + #include + + // this file uses the literals, so it includes them explicitly + #include + + int main() + { + auto j = R"({"foo": 42})"_json; + return j.at("/foo"_json_pointer) == 42 ? 0 : 1; + } + ``` + + Without the include of ``, the code would fail to compile. + +## 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. diff --git a/docs/mkdocs/docs/api/macros/json_use_global_udls.md b/docs/mkdocs/docs/api/macros/json_use_global_udls.md index 3110d4662..0b055640e 100644 --- a/docs/mkdocs/docs/api/macros/json_use_global_udls.md +++ b/docs/mkdocs/docs/api/macros/json_use_global_udls.md @@ -15,7 +15,7 @@ The default value is `1`. #define JSON_USE_GLOBAL_UDLS 1 ``` -When the macro is not defined, the library will define it to its default value. +When the macro is not defined, the library behaves as if it were defined to its default value. ## Notes @@ -32,6 +32,11 @@ 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_AUTOMATIC_UDLS`](json_no_automatic_udls.md) is defined, the literals are only declared where + `` is included; this macro then applies to that header. + ## Examples ??? example "Example 1: Default behavior" @@ -92,6 +97,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_AUTOMATIC_UDLS`](json_no_automatic_udls.md) - do not include the user-defined string literals automatically - [:simple-cmake: JSON_GlobalUDLs](../../integration/cmake.md#json_globaludls) - CMake option to control the macro ## Version history diff --git a/docs/mkdocs/docs/api/operator_literal_json.md b/docs/mkdocs/docs/api/operator_literal_json.md index babce5799..a909d8165 100644 --- a/docs/mkdocs/docs/api/operator_literal_json.md +++ b/docs/mkdocs/docs/api/operator_literal_json.md @@ -18,7 +18,9 @@ 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 declared in header +``, which `` includes unless +[`JSON_NO_AUTOMATIC_UDLS`](macros/json_no_automatic_udls.md) is defined. ## Parameters @@ -59,6 +61,8 @@ Linear. ## See also - [Creating JSON values](../features/creating_values.md) - the article on creating JSON values +- [JSON_NO_AUTOMATIC_UDLS](macros/json_no_automatic_udls.md) - do not include the user-defined string literals + automatically ## Version history diff --git a/docs/mkdocs/docs/api/operator_literal_json_pointer.md b/docs/mkdocs/docs/api/operator_literal_json_pointer.md index e1b729467..0494439aa 100644 --- a/docs/mkdocs/docs/api/operator_literal_json_pointer.md +++ b/docs/mkdocs/docs/api/operator_literal_json_pointer.md @@ -17,7 +17,9 @@ 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 declared in header +``, which `` includes unless +[`JSON_NO_AUTOMATIC_UDLS`](macros/json_no_automatic_udls.md) is defined. ## Parameters @@ -58,6 +60,8 @@ Linear. ## See also - [json_pointer](json_pointer/index.md) - type to represent JSON Pointers +- [JSON_NO_AUTOMATIC_UDLS](macros/json_no_automatic_udls.md) - do not include the user-defined string literals + automatically ## Version history diff --git a/docs/mkdocs/docs/features/macros.md b/docs/mkdocs/docs/features/macros.md index 2bc8a4a5b..e4a628c3e 100644 --- a/docs/mkdocs/docs/features/macros.md +++ b/docs/mkdocs/docs/features/macros.md @@ -83,6 +83,15 @@ When defined, default parse and serialize functions for enums are excluded and h See [full documentation of `JSON_DISABLE_ENUM_SERIALIZATION`](../api/macros/json_disable_enum_serialization.md). +## `JSON_NO_AUTOMATIC_UDLS` + +When defined, `` does not include `` with the user-defined string literals +`operator""_json` and `operator""_json_pointer`. This reduces the compile time of translation units that do not use +them, because the literals instantiate the parser in every translation unit that includes them. Include +`` where the literals are needed. + +See [full documentation of `JSON_NO_AUTOMATIC_UDLS`](../api/macros/json_no_automatic_udls.md). + ## `JSON_NO_IO` When defined, headers ``, ``, ``, ``, and `` are not included and parse functions diff --git a/docs/mkdocs/docs/features/modules.md b/docs/mkdocs/docs/features/modules.md index 6708f1d81..32e9ba7fd 100644 --- a/docs/mkdocs/docs/features/modules.md +++ b/docs/mkdocs/docs/features/modules.md @@ -40,6 +40,9 @@ Only the following symbols are exported from `nlohmann.json`: - `nlohmann::literals::json_literals::operator""_json` - `nlohmann::literals::json_literals::operator""_json_pointer` +The module always exports the two user-defined string literals, even if +[`JSON_NO_AUTOMATIC_UDLS`](../api/macros/json_no_automatic_udls.md) is defined when building it. + Additionally, the following `nlohmann::detail` symbols are exported, solely to work around an MSVC compilation issue ([#3970](https://github.com/nlohmann/json/issues/3970)). They are implementation details, not part of the public API, and should not be used directly: diff --git a/docs/mkdocs/docs/integration/index.md b/docs/mkdocs/docs/integration/index.md index 2bbaa8604..99bf088b6 100644 --- a/docs/mkdocs/docs/integration/index.md +++ b/docs/mkdocs/docs/integration/index.md @@ -15,4 +15,7 @@ Clang). You can further use file [`single_include/nlohmann/json_fwd.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_fwd.hpp) -for forward declarations. +for forward declarations, and file +[`single_include/nlohmann/json_literals.hpp`](https://github.com/nlohmann/json/blob/develop/single_include/nlohmann/json_literals.hpp) +for the user-defined string literals if you define +[`JSON_NO_AUTOMATIC_UDLS`](../api/macros/json_no_automatic_udls.md). diff --git a/docs/mkdocs/mkdocs.yml b/docs/mkdocs/mkdocs.yml index 70537bd27..21fad832a 100644 --- a/docs/mkdocs/mkdocs.yml +++ b/docs/mkdocs/mkdocs.yml @@ -293,6 +293,7 @@ nav: - 'JSON_HAS_STATIC_RTTI': api/macros/json_has_static_rtti.md - 'JSON_HAS_STD_FORMAT': api/macros/json_has_std_format.md - 'JSON_HAS_THREE_WAY_COMPARISON': api/macros/json_has_three_way_comparison.md + - 'JSON_NO_AUTOMATIC_UDLS': api/macros/json_no_automatic_udls.md - '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 diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 9c35cc3b7..0d524c710 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -915,7 +915,3 @@ void templated_json_throw(ExceptionType exception) #ifndef JSON_DISABLE_ENUM_SERIALIZATION #define JSON_DISABLE_ENUM_SERIALIZATION 0 #endif - -#ifndef JSON_USE_GLOBAL_UDLS - #define JSON_USE_GLOBAL_UDLS 1 -#endif diff --git a/include/nlohmann/detail/macro_unscope.hpp b/include/nlohmann/detail/macro_unscope.hpp index a73951a22..d2675d3f1 100644 --- a/include/nlohmann/detail/macro_unscope.hpp +++ b/include/nlohmann/detail/macro_unscope.hpp @@ -25,7 +25,6 @@ #undef JSON_INLINE_VARIABLE #undef JSON_NO_UNIQUE_ADDRESS #undef JSON_DISABLE_ENUM_SERIALIZATION -#undef JSON_USE_GLOBAL_UDLS #ifndef JSON_TEST_KEEP_MACROS #undef JSON_CATCH diff --git a/include/nlohmann/json.hpp b/include/nlohmann/json.hpp index 03574f5bf..cb08777bc 100644 --- a/include/nlohmann/json.hpp +++ b/include/nlohmann/json.hpp @@ -6480,55 +6480,6 @@ std::string format_as(const NLOHMANN_BASIC_JSON_TPL& j) return j.dump(); } -inline namespace literals -{ -inline namespace json_literals -{ - -/// @brief user-defined string literal for JSON values -/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/ -JSON_HEDLEY_NON_NULL(1) -#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) - inline nlohmann::json operator""_json(const char* s, std::size_t n) -#else - // GCC 4.8 requires a space between "" and suffix - inline nlohmann::json operator"" _json(const char* s, std::size_t n) -#endif -{ - return nlohmann::json::parse(s, s + n); -} - -#if defined(__cpp_char8_t) -JSON_HEDLEY_NON_NULL(1) -inline nlohmann::json operator""_json(const char8_t* s, std::size_t n) -{ - return nlohmann::json::parse(reinterpret_cast(s), - reinterpret_cast(s) + n); -} -#endif - -/// @brief user-defined string literal for JSON pointer -/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/ -JSON_HEDLEY_NON_NULL(1) -#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) - inline nlohmann::json::json_pointer operator""_json_pointer(const char* s, std::size_t n) -#else - // GCC 4.8 requires a space between "" and suffix - inline nlohmann::json::json_pointer operator"" _json_pointer(const char* s, std::size_t n) -#endif -{ - return nlohmann::json::json_pointer(std::string(s, n)); -} - -#if defined(__cpp_char8_t) -inline nlohmann::json::json_pointer operator""_json_pointer(const char8_t* s, std::size_t n) -{ - return nlohmann::json::json_pointer(std::string(reinterpret_cast(s), n)); -} -#endif - -} // namespace json_literals -} // namespace literals NLOHMANN_JSON_NAMESPACE_END /////////////////////// @@ -6663,17 +6614,6 @@ struct formatter // NOLINT(cert-dcl58-c } // namespace std -#if JSON_USE_GLOBAL_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) - #else - // GCC 4.8 requires a space between "" and suffix - 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) - #endif -#endif - #include // End of GCC diagnostic pragmas for C++ modules support @@ -6681,4 +6621,13 @@ struct formatter // NOLINT(cert-dcl58-c #pragma GCC diagnostic pop #endif +// The user-defined string literals are in a separate header, because their +// bodies instantiate the parser in every translation unit that includes them. +// Define JSON_NO_AUTOMATIC_UDLS to include only +// where needed. +#ifndef JSON_NO_AUTOMATIC_UDLS + // NOLINTNEXTLINE(misc-header-include-cycle): json_literals.hpp includes this header + #include +#endif + #endif // INCLUDE_NLOHMANN_JSON_HPP_ diff --git a/include/nlohmann/json_literals.hpp b/include/nlohmann/json_literals.hpp new file mode 100644 index 000000000..a865b6c82 --- /dev/null +++ b/include/nlohmann/json_literals.hpp @@ -0,0 +1,78 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +#ifndef INCLUDE_NLOHMANN_JSON_LITERALS_HPP_ +#define INCLUDE_NLOHMANN_JSON_LITERALS_HPP_ + +#include // size_t +#include // string + +// NOLINTNEXTLINE(misc-header-include-cycle): json.hpp includes this header at its end +#include + +// This header is included at the end of unless +// JSON_NO_AUTOMATIC_UDLS is defined, and can be included on its own after that. +// Either way, the library's internal macros are no longer defined here (and the +// amalgamation inlines macro_scope.hpp only once), so only standard and public +// macros may be used below. + +// declares the literal operator for the given suffix; GCC 4.8 requires a space +// between "" and the suffix, which newer compilers deprecate (CWG 2521) +#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9) + #define NLOHMANN_JSON_LITERAL_OPERATOR(suffix) operator""##suffix +#else + #define NLOHMANN_JSON_LITERAL_OPERATOR(suffix) operator"" suffix +#endif + +NLOHMANN_JSON_NAMESPACE_BEGIN +inline namespace literals +{ +inline namespace json_literals +{ + +/// @brief user-defined string literal for JSON values +/// @sa https://json.nlohmann.me/api/operator_literal_json/ +inline nlohmann::json NLOHMANN_JSON_LITERAL_OPERATOR(_json)(const char* s, std::size_t n) +{ + return nlohmann::json::parse(s, s + n); +} + +#if defined(__cpp_char8_t) +inline nlohmann::json operator""_json(const char8_t* s, std::size_t n) +{ + return nlohmann::json::parse(reinterpret_cast(s), + reinterpret_cast(s) + n); +} +#endif + +/// @brief user-defined string literal for JSON pointer +/// @sa https://json.nlohmann.me/api/operator_literal_json_pointer/ +inline nlohmann::json::json_pointer NLOHMANN_JSON_LITERAL_OPERATOR(_json_pointer)(const char* s, std::size_t n) +{ + return nlohmann::json::json_pointer(std::string(s, n)); +} + +#if defined(__cpp_char8_t) +inline nlohmann::json::json_pointer operator""_json_pointer(const char8_t* s, std::size_t n) +{ + return nlohmann::json::json_pointer(std::string(reinterpret_cast(s), n)); +} +#endif + +} // namespace json_literals +} // namespace literals +NLOHMANN_JSON_NAMESPACE_END + +#if !defined(JSON_USE_GLOBAL_UDLS) || JSON_USE_GLOBAL_UDLS + using nlohmann::literals::json_literals::NLOHMANN_JSON_LITERAL_OPERATOR(_json); // NOLINT(misc-unused-using-decls,google-global-names-in-headers) + using nlohmann::literals::json_literals::NLOHMANN_JSON_LITERAL_OPERATOR(_json_pointer); //NOLINT(misc-unused-using-decls,google-global-names-in-headers) +#endif + +#undef NLOHMANN_JSON_LITERAL_OPERATOR + +#endif // INCLUDE_NLOHMANN_JSON_LITERALS_HPP_ diff --git a/meson.build b/meson.build index a2d6e31a2..2800957ec 100644 --- a/meson.build +++ b/meson.build @@ -15,6 +15,7 @@ nlohmann_json_multiple_headers = declare_dependency( if not meson.is_subproject() install_headers('single_include/nlohmann/json.hpp', subdir: 'nlohmann') install_headers('single_include/nlohmann/json_fwd.hpp', subdir: 'nlohmann') +install_headers('single_include/nlohmann/json_literals.hpp', subdir: 'nlohmann') pkgc = import('pkgconfig') pkgc.generate(name: 'nlohmann_json', diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index ef98ba71d..cf94c53a9 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -3329,10 +3329,6 @@ void templated_json_throw(ExceptionType exception) #define JSON_DISABLE_ENUM_SERIALIZATION 0 #endif -#ifndef JSON_USE_GLOBAL_UDLS - #define JSON_USE_GLOBAL_UDLS 1 -#endif - #if JSON_HAS_THREE_WAY_COMPARISON #include // partial_ordering #endif @@ -33409,55 +33405,6 @@ std::string format_as(const NLOHMANN_BASIC_JSON_TPL& j) return j.dump(); } -inline namespace literals -{ -inline namespace json_literals -{ - -/// @brief user-defined string literal for JSON values -/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json/ -JSON_HEDLEY_NON_NULL(1) -#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) - inline nlohmann::json operator""_json(const char* s, std::size_t n) -#else - // GCC 4.8 requires a space between "" and suffix - inline nlohmann::json operator"" _json(const char* s, std::size_t n) -#endif -{ - return nlohmann::json::parse(s, s + n); -} - -#if defined(__cpp_char8_t) -JSON_HEDLEY_NON_NULL(1) -inline nlohmann::json operator""_json(const char8_t* s, std::size_t n) -{ - return nlohmann::json::parse(reinterpret_cast(s), - reinterpret_cast(s) + n); -} -#endif - -/// @brief user-defined string literal for JSON pointer -/// @sa https://json.nlohmann.me/api/basic_json/operator_literal_json_pointer/ -JSON_HEDLEY_NON_NULL(1) -#if !defined(JSON_HEDLEY_GCC_VERSION) || JSON_HEDLEY_GCC_VERSION_CHECK(4,9,0) - inline nlohmann::json::json_pointer operator""_json_pointer(const char* s, std::size_t n) -#else - // GCC 4.8 requires a space between "" and suffix - inline nlohmann::json::json_pointer operator"" _json_pointer(const char* s, std::size_t n) -#endif -{ - return nlohmann::json::json_pointer(std::string(s, n)); -} - -#if defined(__cpp_char8_t) -inline nlohmann::json::json_pointer operator""_json_pointer(const char8_t* s, std::size_t n) -{ - return nlohmann::json::json_pointer(std::string(reinterpret_cast(s), n)); -} -#endif - -} // namespace json_literals -} // namespace literals NLOHMANN_JSON_NAMESPACE_END /////////////////////// @@ -33592,17 +33539,6 @@ struct formatter // NOLINT(cert-dcl58-c } // namespace std -#if JSON_USE_GLOBAL_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) - #else - // GCC 4.8 requires a space between "" and suffix - 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) - #endif -#endif - // #include // __ _____ _____ _____ // __| | __| | | | JSON for Modern C++ @@ -33631,7 +33567,6 @@ struct formatter // NOLINT(cert-dcl58-c #undef JSON_INLINE_VARIABLE #undef JSON_NO_UNIQUE_ADDRESS #undef JSON_DISABLE_ENUM_SERIALIZATION -#undef JSON_USE_GLOBAL_UDLS #ifndef JSON_TEST_KEEP_MACROS #undef JSON_CATCH @@ -33824,4 +33759,93 @@ struct formatter // NOLINT(cert-dcl58-c #pragma GCC diagnostic pop #endif +// The user-defined string literals are in a separate header, because their +// bodies instantiate the parser in every translation unit that includes them. +// Define JSON_NO_AUTOMATIC_UDLS to include only +// where needed. +#ifndef JSON_NO_AUTOMATIC_UDLS +// NOLINTNEXTLINE(misc-header-include-cycle): json_literals.hpp includes this header +// #include +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +#ifndef INCLUDE_NLOHMANN_JSON_LITERALS_HPP_ +#define INCLUDE_NLOHMANN_JSON_LITERALS_HPP_ + +#include // size_t +#include // string + +// NOLINTNEXTLINE(misc-header-include-cycle): json.hpp includes this header at its end +// #include + + +// This header is included at the end of unless +// JSON_NO_AUTOMATIC_UDLS is defined, and can be included on its own after that. +// Either way, the library's internal macros are no longer defined here (and the +// amalgamation inlines macro_scope.hpp only once), so only standard and public +// macros may be used below. + +// declares the literal operator for the given suffix; GCC 4.8 requires a space +// between "" and the suffix, which newer compilers deprecate (CWG 2521) +#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9) + #define NLOHMANN_JSON_LITERAL_OPERATOR(suffix) operator""##suffix +#else + #define NLOHMANN_JSON_LITERAL_OPERATOR(suffix) operator"" suffix +#endif + +NLOHMANN_JSON_NAMESPACE_BEGIN +inline namespace literals +{ +inline namespace json_literals +{ + +/// @brief user-defined string literal for JSON values +/// @sa https://json.nlohmann.me/api/operator_literal_json/ +inline nlohmann::json NLOHMANN_JSON_LITERAL_OPERATOR(_json)(const char* s, std::size_t n) +{ + return nlohmann::json::parse(s, s + n); +} + +#if defined(__cpp_char8_t) +inline nlohmann::json operator""_json(const char8_t* s, std::size_t n) +{ + return nlohmann::json::parse(reinterpret_cast(s), + reinterpret_cast(s) + n); +} +#endif + +/// @brief user-defined string literal for JSON pointer +/// @sa https://json.nlohmann.me/api/operator_literal_json_pointer/ +inline nlohmann::json::json_pointer NLOHMANN_JSON_LITERAL_OPERATOR(_json_pointer)(const char* s, std::size_t n) +{ + return nlohmann::json::json_pointer(std::string(s, n)); +} + +#if defined(__cpp_char8_t) +inline nlohmann::json::json_pointer operator""_json_pointer(const char8_t* s, std::size_t n) +{ + return nlohmann::json::json_pointer(std::string(reinterpret_cast(s), n)); +} +#endif + +} // namespace json_literals +} // namespace literals +NLOHMANN_JSON_NAMESPACE_END + +#if !defined(JSON_USE_GLOBAL_UDLS) || JSON_USE_GLOBAL_UDLS + using nlohmann::literals::json_literals::NLOHMANN_JSON_LITERAL_OPERATOR(_json); // NOLINT(misc-unused-using-decls,google-global-names-in-headers) + using nlohmann::literals::json_literals::NLOHMANN_JSON_LITERAL_OPERATOR(_json_pointer); //NOLINT(misc-unused-using-decls,google-global-names-in-headers) +#endif + +#undef NLOHMANN_JSON_LITERAL_OPERATOR + +#endif // INCLUDE_NLOHMANN_JSON_LITERALS_HPP_ + +#endif + #endif // INCLUDE_NLOHMANN_JSON_HPP_ diff --git a/single_include/nlohmann/json_literals.hpp b/single_include/nlohmann/json_literals.hpp new file mode 100644 index 000000000..a865b6c82 --- /dev/null +++ b/single_include/nlohmann/json_literals.hpp @@ -0,0 +1,78 @@ +// __ _____ _____ _____ +// __| | __| | | | JSON for Modern C++ +// | | |__ | | | | | | version 3.12.0 +// |_____|_____|_____|_|___| https://github.com/nlohmann/json +// +// SPDX-FileCopyrightText: 2013-2026 Niels Lohmann +// SPDX-License-Identifier: MIT + +#ifndef INCLUDE_NLOHMANN_JSON_LITERALS_HPP_ +#define INCLUDE_NLOHMANN_JSON_LITERALS_HPP_ + +#include // size_t +#include // string + +// NOLINTNEXTLINE(misc-header-include-cycle): json.hpp includes this header at its end +#include + +// This header is included at the end of unless +// JSON_NO_AUTOMATIC_UDLS is defined, and can be included on its own after that. +// Either way, the library's internal macros are no longer defined here (and the +// amalgamation inlines macro_scope.hpp only once), so only standard and public +// macros may be used below. + +// declares the literal operator for the given suffix; GCC 4.8 requires a space +// between "" and the suffix, which newer compilers deprecate (CWG 2521) +#if !defined(__GNUC__) || defined(__clang__) || __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 9) + #define NLOHMANN_JSON_LITERAL_OPERATOR(suffix) operator""##suffix +#else + #define NLOHMANN_JSON_LITERAL_OPERATOR(suffix) operator"" suffix +#endif + +NLOHMANN_JSON_NAMESPACE_BEGIN +inline namespace literals +{ +inline namespace json_literals +{ + +/// @brief user-defined string literal for JSON values +/// @sa https://json.nlohmann.me/api/operator_literal_json/ +inline nlohmann::json NLOHMANN_JSON_LITERAL_OPERATOR(_json)(const char* s, std::size_t n) +{ + return nlohmann::json::parse(s, s + n); +} + +#if defined(__cpp_char8_t) +inline nlohmann::json operator""_json(const char8_t* s, std::size_t n) +{ + return nlohmann::json::parse(reinterpret_cast(s), + reinterpret_cast(s) + n); +} +#endif + +/// @brief user-defined string literal for JSON pointer +/// @sa https://json.nlohmann.me/api/operator_literal_json_pointer/ +inline nlohmann::json::json_pointer NLOHMANN_JSON_LITERAL_OPERATOR(_json_pointer)(const char* s, std::size_t n) +{ + return nlohmann::json::json_pointer(std::string(s, n)); +} + +#if defined(__cpp_char8_t) +inline nlohmann::json::json_pointer operator""_json_pointer(const char8_t* s, std::size_t n) +{ + return nlohmann::json::json_pointer(std::string(reinterpret_cast(s), n)); +} +#endif + +} // namespace json_literals +} // namespace literals +NLOHMANN_JSON_NAMESPACE_END + +#if !defined(JSON_USE_GLOBAL_UDLS) || JSON_USE_GLOBAL_UDLS + using nlohmann::literals::json_literals::NLOHMANN_JSON_LITERAL_OPERATOR(_json); // NOLINT(misc-unused-using-decls,google-global-names-in-headers) + using nlohmann::literals::json_literals::NLOHMANN_JSON_LITERAL_OPERATOR(_json_pointer); //NOLINT(misc-unused-using-decls,google-global-names-in-headers) +#endif + +#undef NLOHMANN_JSON_LITERAL_OPERATOR + +#endif // INCLUDE_NLOHMANN_JSON_LITERALS_HPP_ diff --git a/src/modules/json.cppm b/src/modules/json.cppm index 15207535f..a32b97603 100644 --- a/src/modules/json.cppm +++ b/src/modules/json.cppm @@ -18,6 +18,7 @@ module; // See: https://github.com/nlohmann/json/issues/5103 #include +#include export module nlohmann.json; diff --git a/tests/src/unit-no_automatic_udls.cpp b/tests/src/unit-no_automatic_udls.cpp new file mode 100644 index 000000000..1a708da50 --- /dev/null +++ b/tests/src/unit-no_automatic_udls.cpp @@ -0,0 +1,108 @@ +// __ _____ _____ _____ +// __| | __| | | | 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 + +// This translation unit checks JSON_NO_AUTOMATIC_UDLS, which keeps +// from including and thereby +// leaves out the user-defined string literals operator""_json and +// operator""_json_pointer (see #5294), and that including +// afterwards brings them back. +#define JSON_NO_AUTOMATIC_UDLS 1 + +#include "doctest_compatibility.h" + +#include +#include + +#include +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 + using json_udl_t = decltype(operator""_json(std::declval(), std::size_t())); + + template + using json_pointer_udl_t = decltype(operator""_json_pointer(std::declval(), std::size_t())); +#else + // GCC 4.8 requires a space between "" and suffix + template + using json_udl_t = decltype(operator"" _json(std::declval(), std::size_t())); + + template + using json_pointer_udl_t = decltype(operator"" _json_pointer(std::declval(), std::size_t())); +#endif + +template +using has_json_udl = nlohmann::detail::is_detected; + +template +using has_json_pointer_udl = nlohmann::detail::is_detected; +} // 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::value); + CHECK_FALSE(has_json_pointer_udl::value); + + // nlohmann::literals::json_literals + CHECK_FALSE(has_json_udl::value); + CHECK_FALSE(has_json_pointer_udl::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 + +TEST_CASE("JSON_NO_AUTOMATIC_UDLS with ") +{ +#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); + } +}