From b2ced0cae09fe35a693aac39ac6f7b2d6fa0ab39 Mon Sep 17 00:00:00 2001 From: Shahab Ahmed Date: Thu, 28 May 2026 15:04:47 +0500 Subject: [PATCH] fix: support zero-member types in NLOHMANN_DEFINE_TYPE_* macros --- include/nlohmann/detail/macro_scope.hpp | 8 +- single_include/nlohmann/json.hpp | 134 ++++++++++++------------ tests/src/unit-udt_macro.cpp | 33 ++++++ 3 files changed, 108 insertions(+), 67 deletions(-) diff --git a/include/nlohmann/detail/macro_scope.hpp b/include/nlohmann/detail/macro_scope.hpp index 450ba943f2..356fff68d6 100644 --- a/include/nlohmann/detail/macro_scope.hpp +++ b/include/nlohmann/detail/macro_scope.hpp @@ -394,7 +394,9 @@ void templated_json_throw(ExceptionType exception) NLOHMANN_JSON_PASTE4, \ NLOHMANN_JSON_PASTE3, \ NLOHMANN_JSON_PASTE2, \ - NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) + NLOHMANN_JSON_PASTE1, \ + NLOHMANN_JSON_PASTE0)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE0(func) #define NLOHMANN_JSON_PASTE2(func, v1) func(v1) #define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) #define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) @@ -522,7 +524,9 @@ void templated_json_throw(ExceptionType exception) NLOHMANN_JSON_DOUBLE_PASTE3, \ NLOHMANN_JSON_DOUBLE_PASTE3, \ NLOHMANN_JSON_DOUBLE_PASTE1, \ - NLOHMANN_JSON_DOUBLE_PASTE1)(__VA_ARGS__)) + NLOHMANN_JSON_DOUBLE_PASTE1, \ + NLOHMANN_JSON_DOUBLE_PASTE0)(__VA_ARGS__)) +#define NLOHMANN_JSON_DOUBLE_PASTE0(func) #define NLOHMANN_JSON_DOUBLE_PASTE3(func, v1, v2) func(v1, v2) #define NLOHMANN_JSON_DOUBLE_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_DOUBLE_PASTE3(func, v1, v2) NLOHMANN_JSON_DOUBLE_PASTE3(func, v3, v4) #define NLOHMANN_JSON_DOUBLE_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_DOUBLE_PASTE3(func, v1, v2) NLOHMANN_JSON_DOUBLE_PASTE5(func, v3, v4, v5, v6) diff --git a/single_include/nlohmann/json.hpp b/single_include/nlohmann/json.hpp index d2165e8ef5..1000ec0835 100644 --- a/single_include/nlohmann/json.hpp +++ b/single_include/nlohmann/json.hpp @@ -2768,7 +2768,9 @@ void templated_json_throw(ExceptionType exception) NLOHMANN_JSON_PASTE4, \ NLOHMANN_JSON_PASTE3, \ NLOHMANN_JSON_PASTE2, \ - NLOHMANN_JSON_PASTE1)(__VA_ARGS__)) + NLOHMANN_JSON_PASTE1, \ + NLOHMANN_JSON_PASTE0)(__VA_ARGS__)) +#define NLOHMANN_JSON_PASTE0(func) #define NLOHMANN_JSON_PASTE2(func, v1) func(v1) #define NLOHMANN_JSON_PASTE3(func, v1, v2) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE2(func, v2) #define NLOHMANN_JSON_PASTE4(func, v1, v2, v3) NLOHMANN_JSON_PASTE2(func, v1) NLOHMANN_JSON_PASTE3(func, v2, v3) @@ -2896,7 +2898,9 @@ void templated_json_throw(ExceptionType exception) NLOHMANN_JSON_DOUBLE_PASTE3, \ NLOHMANN_JSON_DOUBLE_PASTE3, \ NLOHMANN_JSON_DOUBLE_PASTE1, \ - NLOHMANN_JSON_DOUBLE_PASTE1)(__VA_ARGS__)) + NLOHMANN_JSON_DOUBLE_PASTE1, \ + NLOHMANN_JSON_DOUBLE_PASTE0)(__VA_ARGS__)) +#define NLOHMANN_JSON_DOUBLE_PASTE0(func) #define NLOHMANN_JSON_DOUBLE_PASTE3(func, v1, v2) func(v1, v2) #define NLOHMANN_JSON_DOUBLE_PASTE5(func, v1, v2, v3, v4) NLOHMANN_JSON_DOUBLE_PASTE3(func, v1, v2) NLOHMANN_JSON_DOUBLE_PASTE3(func, v3, v4) #define NLOHMANN_JSON_DOUBLE_PASTE7(func, v1, v2, v3, v4, v5, v6) NLOHMANN_JSON_DOUBLE_PASTE3(func, v1, v2) NLOHMANN_JSON_DOUBLE_PASTE5(func, v3, v4, v5, v6) @@ -3695,71 +3699,71 @@ NLOHMANN_JSON_NAMESPACE_END // SPDX-License-Identifier: MIT #ifndef INCLUDE_NLOHMANN_JSON_FWD_HPP_ - #define INCLUDE_NLOHMANN_JSON_FWD_HPP_ +#define INCLUDE_NLOHMANN_JSON_FWD_HPP_ - #include // int64_t, uint64_t - #include // map - #include // allocator - #include // string - #include // vector +#include // int64_t, uint64_t +#include // map +#include // allocator +#include // string +#include // vector - // #include +// #include - /*! - @brief namespace for Niels Lohmann - @see https://github.com/nlohmann - @since version 1.0.0 - */ - NLOHMANN_JSON_NAMESPACE_BEGIN +/*! +@brief namespace for Niels Lohmann +@see https://github.com/nlohmann +@since version 1.0.0 +*/ +NLOHMANN_JSON_NAMESPACE_BEGIN - /*! - @brief default JSONSerializer template argument +/*! +@brief default JSONSerializer template argument - This serializer ignores the template arguments and uses ADL - ([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) - for serialization. - */ - template - struct adl_serializer; - - /// a class to store JSON values - /// @sa https://json.nlohmann.me/api/basic_json/ - template class ObjectType = - std::map, - template class ArrayType = std::vector, - class StringType = std::string, class BooleanType = bool, - class NumberIntegerType = std::int64_t, - class NumberUnsignedType = std::uint64_t, - class NumberFloatType = double, - template class AllocatorType = std::allocator, - template class JSONSerializer = - adl_serializer, - class BinaryType = std::vector, // cppcheck-suppress syntaxError - class CustomBaseClass = void> - class basic_json; - - /// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document - /// @sa https://json.nlohmann.me/api/json_pointer/ - template - class json_pointer; +This serializer ignores the template arguments and uses ADL +([argument-dependent lookup](https://en.cppreference.com/w/cpp/language/adl)) +for serialization. +*/ +template +struct adl_serializer; + +/// a class to store JSON values +/// @sa https://json.nlohmann.me/api/basic_json/ +template class ObjectType = + std::map, + template class ArrayType = std::vector, + class StringType = std::string, class BooleanType = bool, + class NumberIntegerType = std::int64_t, + class NumberUnsignedType = std::uint64_t, + class NumberFloatType = double, + template class AllocatorType = std::allocator, + template class JSONSerializer = + adl_serializer, + class BinaryType = std::vector, // cppcheck-suppress syntaxError + class CustomBaseClass = void> +class basic_json; - /*! - @brief default specialization - @sa https://json.nlohmann.me/api/json/ - */ - using json = basic_json<>; +/// @brief JSON Pointer defines a string syntax for identifying a specific value within a JSON document +/// @sa https://json.nlohmann.me/api/json_pointer/ +template +class json_pointer; - /// @brief a minimal map-like container that preserves insertion order - /// @sa https://json.nlohmann.me/api/ordered_map/ - template - struct ordered_map; +/*! +@brief default specialization +@sa https://json.nlohmann.me/api/json/ +*/ +using json = basic_json<>; - /// @brief specialization that maintains the insertion order of object keys - /// @sa https://json.nlohmann.me/api/ordered_json/ - using ordered_json = basic_json; +/// @brief a minimal map-like container that preserves insertion order +/// @sa https://json.nlohmann.me/api/ordered_map/ +template +struct ordered_map; - NLOHMANN_JSON_NAMESPACE_END +/// @brief specialization that maintains the insertion order of object keys +/// @sa https://json.nlohmann.me/api/ordered_json/ +using ordered_json = basic_json; + +NLOHMANN_JSON_NAMESPACE_END #endif // INCLUDE_NLOHMANN_JSON_FWD_HPP_ @@ -5727,7 +5731,7 @@ NLOHMANN_JSON_NAMESPACE_END // #include -// JSON_HAS_CPP_17 + // JSON_HAS_CPP_17 #ifdef JSON_HAS_CPP_17 #include // optional #endif @@ -20649,10 +20653,10 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec const bool allow_exceptions = true, const bool ignore_comments = false, const bool ignore_trailing_commas = false - ) + ) { return ::nlohmann::detail::parser(std::move(adapter), - std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); + std::move(cb), allow_exceptions, ignore_comments, ignore_trailing_commas); } private: @@ -21350,8 +21354,8 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::enable_if_t < !detail::is_basic_json::value && detail::is_compatible_type::value, int > = 0 > basic_json(CompatibleType && val) noexcept(noexcept( // NOLINT(bugprone-forwarding-reference-overload,bugprone-exception-escape) - JSONSerializer::to_json(std::declval(), - std::forward(val)))) + JSONSerializer::to_json(std::declval(), + std::forward(val)))) { JSONSerializer::to_json(*this, std::forward(val)); set_parents(); @@ -22154,7 +22158,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::has_from_json::value, int > = 0 > ValueType get_impl(detail::priority_tag<0> /*unused*/) const noexcept(noexcept( - JSONSerializer::from_json(std::declval(), std::declval()))) + JSONSerializer::from_json(std::declval(), std::declval()))) { auto ret = ValueType(); JSONSerializer::from_json(*this, ret); @@ -22196,7 +22200,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::has_non_default_from_json::value, int > = 0 > ValueType get_impl(detail::priority_tag<1> /*unused*/) const noexcept(noexcept( - JSONSerializer::from_json(std::declval()))) + JSONSerializer::from_json(std::declval()))) { return JSONSerializer::from_json(*this); } @@ -22346,7 +22350,7 @@ class basic_json // NOLINT(cppcoreguidelines-special-member-functions,hicpp-spec detail::has_from_json::value, int > = 0 > ValueType & get_to(ValueType& v) const noexcept(noexcept( - JSONSerializer::from_json(std::declval(), v))) + JSONSerializer::from_json(std::declval(), v))) { JSONSerializer::from_json(*this, v); return v; diff --git a/tests/src/unit-udt_macro.cpp b/tests/src/unit-udt_macro.cpp index 7fab809212..14dab29221 100644 --- a/tests/src/unit-udt_macro.cpp +++ b/tests/src/unit-udt_macro.cpp @@ -778,8 +778,41 @@ class derived_person_only_serialize_private_3 : person_without_default_construct NLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES(derived_person_only_serialize_private_3, person_without_default_constructor_3, "json_hair_color", hair_color) }; +class empty_type +{ + public: + NLOHMANN_DEFINE_TYPE_INTRUSIVE(empty_type) + + bool operator==(const empty_type& /*unused*/) const + { + return true; + } +}; + +class empty_type_non_intrusive +{}; + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(empty_type_non_intrusive) + } // namespace persons +TEST_CASE("NLOHMANN_DEFINE_TYPE_* with zero members") +{ + SECTION("intrusive") + { + persons::empty_type v; + CHECK(json(v).dump() == "{}"); + CHECK(json::parse("{}").get() == v); + } + + SECTION("non-intrusive") + { + persons::empty_type_non_intrusive v; + CHECK(json(v).dump() == "{}"); + CHECK(json::parse("{}").get() == v); + } +} + TEST_CASE_TEMPLATE("Serialization/deserialization via NLOHMANN_DEFINE_TYPE_INTRUSIVE and NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE", Pair, // NOLINT(readability-math-missing-parentheses, bugprone-throwing-static-initialization) std::pair, std::pair,