* Store maps with enum keys as objects (opt-in) Maps with enum keys, such as std::map<E, T>, are stored as arrays of [key, value] pairs, because enums are not convertible to the string type of object keys - even if NLOHMANN_JSON_SERIALIZE_ENUM maps them to strings (#4378). The new JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS macro stores them as objects instead, converting each key with the enum's to_json. It applies to any map-like type with enum keys (std::map with any comparator, std::unordered_map, ...). A key that does not convert to a string throws type_error.302, and two keys converting to the same string throw the new type_error.318, rather than losing an entry. The macro changes the output of inline functions, so it is part of the ABI tag (_ekmo). Reading needs no macro: std::map and std::unordered_map with enum keys are now also read from objects, converting each key with the enum's from_json. That input was rejected before, and arrays of pairs are still read, so data written either way can be read. This supersedes #4531, which first proposed storing these maps as objects. Co-authored-by: Muhammad Amir bin Mohamad Ghazaly <amirghaz@umich.edu> Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Keep multimaps with enum keys as arrays of pairs With JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS, is_enum_keyed_map also matched std::multimap and std::unordered_multimap. Storing them as objects throws type_error.318 as soon as a key occurs twice, which is the normal case for a multimap, so such values could no longer be serialized at all once the macro was enabled, although they are stored losslessly as arrays of [key, value] pairs without it. Exclude maps with non-unique keys from is_enum_keyed_map. They are detected by insert(value_type) returning an iterator rather than a pair<iterator, bool>. Map-like types without such an insert() are still treated as before. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Move the default enum-keyed map tests out of unit-conversions.cpp The Windows clang 20.1.8 job (MinGW, Debug) failed to link test-conversions_cpp17 with "relocation truncated to fit: IMAGE_REL_AMD64_REL32 against .rdata": the object file of unit-conversions.cpp was already close to the limit, and the new "maps with enum keys" test case pushed it over. windows.yml asks to keep these objects small by splitting test files. Move the test case unchanged into unit-enum_keyed_maps_default.cpp, with the three enums it needs. It still honors a -D flag for JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPS, as before. unit-conversions.cpp is back to its state on develop. Signed-off-by: Niels Lohmann <mail@nlohmann.me> * Build the enum-keyed map test object instead of parsing it ci_test_diagnostic_positions failed in unit-enum_keyed_maps_default.cpp: with JSON_DIAGNOSTIC_POSITIONS, a parsed value adds its byte range to the exception message ("(bytes 0-7) type must be array, but is object"), so the exact-message checks did not match. Build the object in memory, like unit-custom-array-type.cpp does. Signed-off-by: Niels Lohmann <mail@nlohmann.me> --------- Signed-off-by: Niels Lohmann <mail@nlohmann.me> Co-authored-by: Muhammad Amir bin Mohamad Ghazaly <amirghaz@umich.edu>
4.9 KiB
nlohmann Namespace
The 3.11.0 release introduced an inline namespace to allow different parts of a codebase to safely use different versions of the JSON library as long as they never exchange instances of library types.
Structure
The complete default namespace name is derived as follows:
- The root namespace is always
nlohmann. - The inline namespace starts with
json_abiand is followed by several optional ABI tags according to the value of these ABI-affecting macros, in order:JSON_DIAGNOSTICSdefined non-zero appends_diag.JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISONdefined non-zero appends_ldvcmp.JSON_DIAGNOSTIC_POSITIONSdefined non-zero appends_dp.JSON_BRACE_INIT_COPY_SEMANTICSdefined non-zero appends_bics.JSON_PRECISE_STREAM_POSITIONdefined non-zero appends_psp.JSON_STRICT_NUL_HANDLINGdefined non-zero appends_snul.JSON_STRICT_BINARY_UTF8defined non-zero appends_sbu8.JSON_USE_OBJECTS_FOR_ENUM_KEYED_MAPSdefined non-zero appends_ekmo.
- The inline namespace ends with the suffix
_vfollowed by the 3 components of the version number separated by underscores. To omit the version component, see Disabling the version component below.
For example, the namespace name for version 3.11.2 with JSON_DIAGNOSTICS defined to 1 is:
nlohmann::json_abi_diag_v3_11_2
Purpose
Several incompatibilities have been observed. Amongst the most common ones is linking code compiled with different
definitions of JSON_DIAGNOSTICS. This is illustrated in the diagram below.
graph
json["<strong>nlohmann_json (v3.10.5)</strong><br>JSON_DIAGNOSTICS=0"]
json_diag["<strong>nlohmann_json (v3.10.5)</strong><br>JSON_DIAGNOSTICS=1"]
library["<strong>some library</strong>"]
app["<strong>application</strong>"]
library --> json
app --> json_diag
app --> library
In releases prior to 3.11.0, mixing any version of the JSON library with different JSON_DIAGNOSTICS settings would
result in a crashing application. If some_library never passes instances of JSON library types to the application,
this scenario became safe in version 3.11.0 and above due to the inline namespace yielding distinct symbol names.
Limitations
Neither the compiler nor the linker will issue as much as a warning when translation units – intended to be linked together and that include different versions and/or configurations of the JSON library – exchange and use library types.
There is an exception when forward declarations are used (i.e., when including json_fwd.hpp) in which case the linker
may complain about undefined references.
Disabling the version component
Different versions are not necessarily ABI-incompatible, but the project does not actively track changes in the ABI and recommends that all parts of a codebase exchanging library types be built with the same version. Users can, at their own risk, disable the version component of the inline namespace, allowing different versions – but not configurations – to be used in cases where the linker would otherwise output undefined reference errors.
To do so, define NLOHMANN_JSON_NAMESPACE_NO_VERSION to 1.
This applies to version 3.11.2 and above only; versions 3.11.0 and 3.11.1 can apply the technique described in the next
section to emulate the effect of the NLOHMANN_JSON_NAMESPACE_NO_VERSION macro.
!!! danger "Use at your own risk"
Disabling the namespace version component and mixing ABI-incompatible versions will result in crashes or incorrect
behavior. You have been warned!
Disabling the inline namespace completely
When interoperability with code using a pre-3.11.0 version of the library is required, users can, at their own risk
restore the old namespace layout by redefining
NLOHMANN_JSON_NAMESPACE_BEGIN, NLOHMANN_JSON_NAMESPACE_END as
follows:
#define NLOHMANN_JSON_NAMESPACE_BEGIN namespace nlohmann {
#define NLOHMANN_JSON_NAMESPACE_END }
!!! danger "Use at your own risk"
Overriding the namespace and mixing ABI-incompatible versions will result in crashes or incorrect behavior. You
have been warned!
Version history
- Introduced inline namespace (
json_v3_11_0[_abi-tag]*) in version 3.11.0. - Changed structure of inline namespace in version 3.11.2.