Support zero-member types in NLOHMANN_DEFINE_TYPE_* macros (#4041)#5272
Open
nlohmann wants to merge 3 commits into
Open
Support zero-member types in NLOHMANN_DEFINE_TYPE_* macros (#4041)#5272nlohmann wants to merge 3 commits into
nlohmann wants to merge 3 commits into
Conversation
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type) and its 11 sibling macros produced broken code for types with no members to serialize. Invoking a variadic macro so __VA_ARGS__ is empty is only standard-conforming since C++20, so a plain __VA_OPT__ fix (as tried in #5142) breaks every pre-C++20 build under -pedantic. Instead, make all 12 macros purely variadic and dispatch on argument count using a sentinel-padded extension of the existing NLOHMANN_JSON_GET_MACRO idiom, giving full C++11-C++26 support with no feature-test gate. Verified against real GCC 16 and Clang at -std=c++11/14/17/20 with -pedantic -Werror -Wvariadic-macros: zero regressions in the existing unit-udt_macro.cpp suite plus 12 new zero-member test cases. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
Three issues surfaced on PR #5272's real CI that weren't caught by local testing against a narrower flag set: - GCC -Werror=noexcept: the four truly-empty from_json bodies (plain INTRUSIVE/NON_INTRUSIVE, with and without _WITH_DEFAULT) provably never throw but weren't declared noexcept; mark them noexcept explicitly. to_json and the derived-type from_json overloads are left alone since they genuinely can throw (object assignment / delegating to the base class's from_json). - clang-tidy bugprone-macro-parentheses: false positive on the same 8 zero-member bodies (Type/BaseType used purely as declarator types); suppressed with NOLINTNEXTLINE comments in the same style already used elsewhere in this file (see NLOHMANN_JSON_SERIALIZE_ENUM). - MSVC's traditional preprocessor doesn't fully expand NLOHMANN_JSON_CAT(prefix, NLOHMANN_JSON_TYPE_TAG(...))(...) in one pass, which broke a pre-existing one-member usage in unit-regression2.cpp with syntax errors. Wrap all 12 public dispatcher macros in an extra outer NLOHMANN_JSON_EXPAND(...), matching the pattern NLOHMANN_JSON_PASTE already uses for the same MSVC quirk. Re-verified against real GCC 16 and Clang at -std=c++11/14/17/20 with -pedantic -Werror -Wvariadic-macros -Wnoexcept, including the exact files that failed in CI (unit-udt_macro.cpp, unit-regression2.cpp), against both the modular headers and the re-amalgamated single header. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
The four zero-member ONLY_SERIALIZE test objects are only ever read (via to_json), never mutated, so mark them const per clang-tidy. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
3f8c58a to
4dcd45b
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Fixes #4041:
NLOHMANN_DEFINE_TYPE_INTRUSIVE(Type)and its 11 sibling macros (_WITH_DEFAULT,_ONLY_SERIALIZE,NON_INTRUSIVEequivalents, and theDERIVED_TYPEfamily) produced broken generated code for types with no member variables to serialize.Three prior PRs attempted this (#5142, #5099, #4994); all are stale and fail CI. This is a fresh implementation, not based on any of them:
__VA_OPT__approach breaks every pre-C++20 build (error C3861: '__VA_OPT__': identifier not foundon MSVC's C++11 test project) because it isn't gated behind a C++20 check.__VA_ARGS__is empty is only standard-conforming since C++20 — GCC/Clang reject it under-pedanticpre-C++20 regardless of what the macro body does (verified directly against real GCC 16 / Clang with this repo's actual-pedantic -Werror -Wvariadic-macrosflags). So a__VA_OPT__-only fix can, at best, only ever cover C++20+.##__VA_ARGS__GNU-extension attempt doesn't avoid this either (also verified directly) and only special-cases GCC.Instead, this PR makes all 12 macros purely variadic (no fixed leading parameter) and dispatches on total argument count — 1 vs 2+ member arguments, or 2 vs 3+ for the
DERIVED_TYPEmacros whose fixed prefix isType, BaseType— using a sentinel-padded extension of the existingNLOHMANN_JSON_GET_MACROpositional-dispatch idiom already used byNLOHMANN_JSON_PASTE1..64in this file. This gives full C++11 through C++26 support with no feature-test gate at all.WITH_NAMESvariants are intentionally out of scope, matching issue #4041 and all three prior PRs.Changes
include/nlohmann/detail/macro_scope.hpp: newNLOHMANN_JSON_CAT/NLOHMANN_JSON_TYPE_TAG/NLOHMANN_JSON_DERIVED_TYPE_TAGdispatch helpers; each of the 12 macros renamed to an_N/_2(has-members) body plus a new_1/_2(zero-member) body, with the public macro name becoming a 1-line dispatcher.single_include/nlohmann/json.hpp: re-amalgamated (make amalgamate, astyle 3.4.13).tests/src/unit-udt_macro.cpp: 12 new zero-member test cases (all 6 base macro variants + all 6 derived variants, the latter using a real base class to confirm base-class delegation still works with zero own members).docs/mkdocs/docs/features/arbitrary_types.md: new "Zero-member types" note with an example.Test plan
unit-udt_macro.cppsuite (46 cases) passes unmodified — zero regressions from the macro rename/dispatch refactor.nlohmann::jsonandnlohmann::ordered_json) pass.g++-16andclang++at-std=c++11/14/17/20with-pedantic -Werror -Wvariadic-macros(matchingcmake/gcc_flags.cmake).make check-amalgamationclean.unit-udt.cpp) still compiles/passes to rule out macro-name collisions from the new helpers.This PR description was written by Claude Code.