Repository navigation
Conversation
This comment was marked as outdated.
This comment was marked as outdated.
38a5640 to
d10514d
Compare
|
Hi @nlohmann |
|
Thanks for tackling this! Overall this looks good and does exactly what #5318 asked for. A few minor comments below. The claimed win is realI compiled the allocation-counting program from #5318 against
CBOR and MessagePack now sit at the same allocation level as UBJSON and BSON. The extracted helper bodies are byte-for-byte the originals, and the Comments1. Missing SPDX header on the new test file
2. It was added in d10514d ("fixes for failing ci"), but nothing on the CBOR/MessagePack path calls it — the template overload routes through 3. The unconstrained template overloads may not be needed, and are undocumented
So the templates' only added capability (key types with an explicit-only conversion to 4. Worth noting the source-compatibility nuance in the PR description Previously keys went through I think the new behavior is the correct one, but since it's the convention here to state API impact: no public API break ( 5. Nit Double blank line after the OptionalThere's no regression test guarding the allocation count, so this could silently regress later. Given it needs a global Reviewed with the help of Claude Code. |
|
Hi @nlohmann |
This comment was marked as resolved.
This comment was marked as resolved.
e6a0fbf to
f6de4f0
Compare
|
Hi @nlohmann |
|
Hi @nlohmann |
nlohmann
left a comment
There was a problem hiding this comment.
Thanks a lot, and sorry for the delay.
Two issues I would like you to address:
- A
static_assertonstd::is_convertible<object_t::key_type, string_t>in the two key loops, so a rejected key type produces one readable line instead of a template wall. - One sentence in
docs/mkdocs/docs/api/basic_json/object_t.mdstating thatobject_t::key_typemust be convertible tostring_t. The page already implies it by describing the type asObjectType<StringType, ...>, but the new tests make it a contract, so let's write it down.
Minor: the key in tests/src/custom_object_key_type.hpp carries a c_str() that no CBOR/MsgPack test uses. That's what UBJSON would need - it calls el.first.size() and .c_str() on the key directly - and in fact this key type still doesn't compile with to_ubjson. Pre-existing and out of scope here, but it means "custom key types are supported" isn't uniformly true yet. Either drop the unused c_str() or leave a comment saying why it's there; the UBJSON gap can be a follow-up issue.
This comment was marked as resolved.
This comment was marked as resolved.
nlohmann
left a comment
There was a problem hiding this comment.
Automated review findings — posted via Claude Code on behalf of @nlohmann.
Two issues found with high confidence:
- The
static_assertadded tosingle_include/nlohmann/json.hppguardingobject_t::key_typeconvertibility isn't mirrored in the modularinclude/header it's generated from. Sincesingle_includeis produced frominclude/viamake amalgamate, this diff will fail thecheck_amalgamationCI job, and the modular-header build silently loses the static_assert. - The new
custom_object_key_type.hpptest fixture is missing asize()method to match itsc_str()shim, making it incompatible with UBJSON's key-writing path (verified by compilingto_ubjsonagainst it) despite the comment claiming broader serializer compatibility.
See inline comments below for exact locations.
abba9c3 to
61a73f2
Compare
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
2222a44 to
ce5b4cb
Compare
ce5b4cb to
3cf92d8
Compare
|
Please rebase to the latest develop to resolve conflicts. |
|
@alexprabhat99 Are you willing to continue working on this? |
29c5ed0 to
f3984d7
Compare
ff849d1 to
a1bd525
Compare
a1bd525 to
9dc4a85
Compare
nlohmann
left a comment
There was a problem hiding this comment.
Automated review — posted via Claude Code on behalf of @nlohmann.
Thanks for the rebase! All points from the previous rounds are addressed: the static_asserts are in both include/ and single_include/, object_t.md documents the convertibility requirement, the test header has the SPDX banner, and the template overloads are gone in favor of const string_t& (consistent with the BSON writer). make amalgamate produces no diff, and unit-cbor / unit-msgpack pass locally (C++11 and C++20 for the new test cases, full suites under C++11).
Only nits remain, see inline. One more: please mention the last_child() change in the PR description, since it fixes a develop build break (unit-custom-object-type.cpp fails to compile since #5762 because no_key_compare_map has no rbegin()) that is unrelated to #5318.
|
FYI: I opened #5767 to fix develop. |
d81b0e7 to
9907208
Compare
nlohmann
left a comment
There was a problem hiding this comment.
Thanks for the update, @alexprabhat99! The approach is sound: writing keys through write_cbor_string / write_msgpack_string with the object as diagnostics context keeps the error behavior of the old check_utf8 + write_cbor(el.first) pair without the temporary basic_json. The static_asserts are now in both headers, and dropping the rbegin() change was right, since #5767 fixed that on develop separately.
Here is what is left before this can go in.
1. Rebase onto develop, which now has a second key-writing path (blocking)
The branch conflicts with develop in binary_writer.hpp, single_include/nlohmann/json.hpp, and tests/src/unit-msgpack.cpp. Most of that comes from #5781 ("Stop binary writers overflowing the stack on deep values"). It added write_cbor_iterative and write_msgpack_iterative, which take over below detail::recursion_depth_limit() (128 levels). Each has its own copy of the old key handling:
if (error_handler == error_handler_t::strict)
{
check_utf8(current.object_it->first, *current.value);
}
write_cbor(current.object_it->first); // still a temporary basic_jsonThe same pattern is in write_msgpack_iterative with write_msgpack(...). Please convert both to the new helpers during the rebase:
write_cbor_string(current.object_it->first, *current.value);Without that, every key below level 128 still goes through a temporary basic_json. That is what this PR removes, and the new tests wouldn't notice. It would be good to extend both tests with a value nested deeper than 128 objects, using the custom key type, so the iterative path gets a round trip too.
2. size() and c_str() in the test key type: my earlier advice was wrong
In the last two rounds I said UBJSON reads object keys through el.first.size() and el.first.c_str(). That was incorrect, sorry for the extra churn. On this PR's base and on develop, write_ubjson passes keys through sanitize_utf8_for_write(el.first, j, storage), like the other writers. The only place the library touches a key's own members is data() in exceptions.hpp, for JSON_DIAGNOSTICS. Please remove size(), c_str(), and their comment, and keep data() with its comment (see inline). Please also don't reuse this fixture for UBJSON in this PR; that path has a separate problem with non-string_t keys that I'll handle elsewhere.
3. CI
The failures on the current run aren't caused by this PR. ci_clang_tidy, for example, fails with no member named 'rbegin' in no_key_compare_map in unit-custom-object-type.cpp, the develop breakage fixed by #5767 and #5779. CI on develop is green again, so a rebase should give a clean run. DCO is fine.
Will you continue?
Are you willing and able to continue working on this? Rebasing is the main remaining work. If you don't have time soon, you could tick "Allow edits by maintainers" on this PR (it is currently off) so we can do the rebase for you, keeping your authorship and sign-off. Either way is fine, just let us know.
This comment was written by Claude Code on behalf of @nlohmann.
… keys Signed-off-by: alexprabhat99 <alexpbara@gmail.com>
UBJSON and BJData access object keys through size() and c_str() directly, so the key type now provides both and the comment says why. Signed-off-by: alexprabhat99 <alexpbara@gmail.com>
Nothing in the library calls size() or c_str() on an object key, so the test key type only keeps data(), which JSON_DIAGNOSTICS needs. The CBOR and MessagePack custom key tests now also nest objects deeper than detail::recursion_depth_limit(), so keys written by write_cbor_iterative and write_msgpack_iterative are covered as well. Signed-off-by: alexprabhat99 <alexpbara@gmail.com>
aeb8839 to
5a1ca73
Compare
|
Thanks a lot! FYI I found an unrelated bug so there will be a follow-up PR. |
#5798) The custom object key tests from #5328 instantiate a second basic_json specialization in unit-cbor.cpp and unit-msgpack.cpp. This pushed unit-msgpack.cpp.obj past 65535 sections in the clang (MinGW) jobs of the Windows workflow, and linking test-msgpack_cpp11 fails with "relocation truncated to fit: IMAGE_REL_AMD64_REL32 against `.rdata'". The GNU linker keeps the section an associative COMDAT section belongs to in 16 bits (x_associated in include/coff/internal.h), although big object files store 32 bits. In larger objects it therefore ties the jump tables of inline functions to the wrong function and discards them together with that function's duplicate. Move the four tests unchanged into unit-custom-object-key-type.cpp and document the limit in windows.yml. Signed-off-by: Niels Lohmann <mail@nlohmann.me>
* Restore v3.12.0 support for custom object key types Custom object_t types whose key_type is not string_t compiled with v3.12.0 for several APIs that unreleased changes broke: - to_bson failed for every custom key type (#5553 kept a const string_t* to the key); the nested entry's header is now written where the entry is found. - Copying deep values (and parse, merge_patch, update, insert) required operator== on keys (#5389); keys without one are now paired via find(). - to_cbor/to_msgpack required an implicit conversion to string_t (#5746, #5328); keys without one go through a temporary basic_json again. - at() required a conversion to string_t for its error message (#5727); other keys are passed to concat() unchanged again. The new unit-custom-object-key-type.cpp covers five key types with different capabilities. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Use the with_object_t alias for the custom object key test types Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Avoid floating-point equality in custom key type test GCC with -Werror=float-equal rejects comparing the double value with ==. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Write the head of nested BSON elements in one helper Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Suppress bugprone-return-const-ref-from-parameter in key_for_message The reference is only passed to concat() within the full-expression that holds the key, like the similar helpers in binary_writer.hpp. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Make the value of the custom test key types private clang-tidy (cppcoreguidelines-non-private-member-variables-in-classes) rejects the protected member; the derived key types use a protected accessor instead. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Pass keys with data() and size() unchanged into the at() miss message key_for_message() converted every key that string_t can be constructed from, so a miss on a string_t or string_view key copied it before concat() copied it again. Keys that concat() can append through data() and size() are now passed through; only other keys (string literals, key types that just convert to string_t) are converted. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me>
This PR optimizes CBOR and MessagePack serialization by avoiding the construction of a temporary basic_json object for every object key during serialization.
Previously, object keys were passed through write_cbor()/write_msgpack(), which expected a BasicJsonType and therefore implicitly constructed a temporary basic_json for each key. This introduced unnecessary heap allocations during serialization.
This change factors out the string serialization logic into dedicated helper functions and serializes object keys directly. The implementation preserves compatibility with custom object key types while eliminating the extra basic_json allocations for the common case where object_t::key_type is string_t.
A regression test has been added to verify compatibility with custom object key types.
Fixes #5318
Compatibility note: This does not change the public API (detail::binary_writer is internal). For custom object_t::key_type implementations, CBOR and MessagePack object keys are now required to be implicitly convertible to string_t. Previously, a custom key type handled only through to_json could be serialized through the generic JSON serializer, potentially producing non-string map keys that the library's own CBOR/MessagePack readers would reject.