ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

JSON for Modern C++ 宏(Macros)完全配置指南:从运行时断言到类型序列化宏速查

JSON for Modern C++ 宏(Macros)完全配置指南:从运行时断言到类型序列化宏速查 JSON for Modern C 宏Macros完全配置指南从运行时断言到类型序列化宏速查【免费下载链接】jsonJSON for Modern C项目地址: https://gitcode.com/GitHub_Trending/js/jsonnlohmann/json即 “JSON for Modern C”的很多行为并非只能靠 API 调用控制而是可以在包含json.hpp头文件之前通过定义预处理器宏来整体定制。本文以官方 API 文档中的 Macros 索引页为骨架系统梳理这些宏的用途、默认值、底层实现与典型使用场景从控制运行时断言、异常开关与诊断信息到强制指定 C 标准、管理内联命名空间再到用宏一行生成类型的序列化代码。读完本文你将能根据嵌入式、高安全性、ABI 隔离等不同需求准确挑选并配置所需宏同时理解这些配置在头文件中的真实作用位置。使用前提宏必须在包含头文件之前定义这些宏的生效时机非常一致在包含nlohmann/json.hpp或single_include单头文件版本之前用#define定义。核心实现在 include/nlohmann/detail/macro_scope.hpp 中头文件会先检查#if !defined(...)仅当宏尚未定义时才补上默认值。例如 JSON_ASSERT 的默认定义就是通过这种 可被外部覆盖 的模式提供的// allow overriding assert #if !defined(JSON_ASSERT) #include cassert // assert #define JSON_ASSERT(x) assert(x) #endif因此先定义后包含 是这些宏生效的唯一前提宏在库内部使用不会泄漏污染用户代码多数在库头文件结束时被解除。官方还把完整清单汇总在了 API Macros 总览与特性说明页可对照查阅。按作用域可把全部宏划分为以下类别类别涉及宏运行时断言JSON_ASSERT(x)异常与诊断JSON_TRY_USER/JSON_CATCH_USER(e)/JSON_THROW_USER(e)、JSON_NOEXCEPTION、JSON_DIAGNOSTICS、JSON_DIAGNOSTIC_POSITIONS语言标准探测JSON_HAS_CPP_11/14/17/20及JSON_HAS_CPP_23等、JSON_HAS_FILESYSTEM、JSON_HAS_EXPERIMENTAL_FILESYSTEM、JSON_HAS_RANGES、JSON_HAS_STD_FORMAT、JSON_HAS_THREE_WAY_COMPARISON平台裁剪JSON_NO_IO、JSON_SKIP_UNSUPPORTED_COMPILER_CHECK、JSON_USE_GLOBAL_UDLS版本与命名空间JSON_SKIP_LIBRARY_VERSION_CHECK、NLOHMANN_JSON_VERSION_MAJOR/MINOR/PATCH、NLOHMANN_JSON_NAMESPACE、NLOHMANN_JSON_NAMESPACE_BEGIN/END、NLOHMANN_JSON_NAMESPACE_NO_VERSION类型转换行为JSON_BRACE_INIT_COPY_SEMANTICS、JSON_DISABLE_ENUM_SERIALIZATION、JSON_USE_IMPLICIT_CONVERSIONS比较行为JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON序列化代码生成NLOHMANN_JSON_SERIALIZE_ENUM(_STRICT)及NLOHMANN_DEFINE_(DERIVED_)TYPE_*全家桶下文逐一展开。运行时断言JSON_ASSERT#define JSON_ASSERT(x) /* value */JSON_ASSERT(x)控制库中所有运行时断言执行什么代码参数x是一个标量类型表达式。默认定义为assert(x)见 macro_scope.hpp因此直接使用默认值时库的断言遵循标准assert语义编译期定义NDEBUG即可关闭库内部用大量断言守护不变量例如在const对象上用operator[]访问不存在的 key 会触发断言并终止程序避免未定义行为。完整参考见 JSON_ASSERT 文档与运行时断言特性页。默认行为示例对以下const对象访问缺失的 key 会触发标准断言并输出到 stderr#include nlohmann/json.hpp using json nlohmann::json; int main() { const json j {{key, value}}; auto v j[missing]; }典型输出为Assertion failed: (m_value.object-find(key) ! m_value.object-end()), function operator[], file json.hpp, line 2144.用户自定义断言行为重新定义JSON_ASSERT即可替换报告方式例如输出当前函数名后调用std::abort#include cstdio #include cstdlib #define JSON_ASSERT(x) if(!(x)){fprintf(stderr, assertion error in %s\n, __FUNCTION__); std::abort();} #include nlohmann/json.hpp using json nlohmann::json; int main() { const json j {{key, value}}; auto v j[missing]; }输出为assertion error in operator[]注意如果把宏定义为不调用std::abort的代码断言失败后库可能处于未定义状态该宏在库内部使用完即被解除不会影响库外代码。此宏自 3.9.0 版本起提供。异常控制族捕获、抛出、关闭与诊断JSON_TRY_USER / JSON_CATCH_USER / JSON_THROW_USER库内部大量使用try/catch/throw但从不直接书写标准关键字而是统一经由三个可替换宏详见 JSON_THROW_USER 系列文档#define JSON_TRY_USER #define JSON_CATCH_USER(exception) #define JSON_THROW_USER(exception)其中JSON_CATCH_USER与JSON_THROW_USER各接收一个异常参数。典型用途是把异常体系整体替换为自定义异常、或者在一个不允许抛异常但需要记录错误路径的框架中拦截异常。注意捕获/抛出宏会同时改变库内basic_json的异常行为替换后应保证错误状态仍能被外部感知。JSON_NOEXCEPTION整体关闭异常#define JSON_NOEXCEPTION定义后库内的try被替换为if (true)、catch被替换为if (false)、throw被替换为std::abort()从而在完全不启用异常例如部分嵌入式/裸机环境、-fno-exceptions编译时也能通过编译。默认不定义该宏。#define JSON_NOEXCEPTION 1 #include nlohmann/json.hpp // ...相同效果可直接使用编译器选项-fno-exceptions。已知限制MSVC 在关闭异常时拿不到异常的what()字符串。该宏自 2.1.0 提供详见 JSON_NOEXCEPTION 文档。JSON_DIAGNOSTICS开启扩展诊断信息#define JSON_DIAGNOSTICS /* 1 开启 / 0 关闭默认 */默认值为0默认定义可在 abi_macros.hpp 中确认未定义时库会自动补成默认值。设为1后异常消息中会附带一个JSON Pointer精确指出引发异常的值的位置。例如没有诊断信息时类型不匹配的异常难以定位开启后消息形如[json.exception.type_error.302] type must be string, but is number并附带JSON Pointer如/address/housenumber指明是哪个字段类型不对官方示例见 diagnostics_extended.cpp 与 diagnostics_standard.cpp 的对比输出。需要权衡的是开启JSON_DIAGNOSTICS会使每个 JSON 值对象增大一个指针并带来额外运行时开销。该宏也可由 CMake 选项JSON_Diagnostics默认OFF统一控制——但注意该 CMake 选项只在从源码构建库时生效。自 3.11.0 起宏的值会被编码进命名空间因此允许代码库不同编译单元使用不同取值而不会违反 ODROne Definition Rule即便如此仍建议全库一致取值以最大化互操作性。详见 JSON_DIAGNOSTICS 文档与 JSON_Diagnostic_Positions 相关 CMake 文档。JSON_DIAGNOSTIC_POSITIONS访问元素字节位置开启JSON_DIAGNOSTIC_POSITIONS后basic_json会新增 start_pos() 与 end_pos() 两个成员函数。若该值由parse产生可用它们查询该值在输入字符串中的字节区间异常也会利用这些位置来帮助定位解析错误示例见 diagnostic_positions_exception.cpp。默认关闭同样可通过 CMake 选项JSON_Diagnostic_Positions默认OFF控制。详见 JSON_DIAGNOSTIC_POSITIONS 文档。语言标准与特性探测覆盖族库目标标准是 C11但对 C17 之后的特性std::string_view、std::filesystem、std::ranges、std::format、三路比较等做了自动探测。当编译器对标准的实现不够完整、导致探测错误时可用以下宏强制指定。JSON_HAS_CPP_11 / 14 / 17 / 20含 23/26通过#define JSON_HAS_CPP_17之类可无条件假定某标准启用覆盖库的内部检测。典型用途是修正只实现部分标准特性的编译器。参考 JSON_HAS_CPP_11 文档。JSON_HAS_FILESYSTEM / JSON_HAS_EXPERIMENTAL_FILESYSTEMC17 下库提供与std::filesystem::path的互转。由于filesystem支持不均衡库默认尝试探测JSON_HAS_FILESYSTEM对应标准filesystemJSON_HAS_EXPERIMENTAL_FILESYSTEM对应experimental/filesystem。如需强制指定其一定义为1即可见 JSON_HAS_FILESYSTEM 文档。JSON_HAS_RANGES / JSON_HAS_STD_FORMAT / JSON_HAS_THREE_WAY_COMPARISONJSON_HAS_RANGES控制是否启用std::ranges相关适配JSON_HAS_STD_FORMAT控制format/std::formatter格式化支持对应std::format示例见 std_formatter.c20.cppJSON_HAS_THREE_WAY_COMPARISON控制三路比较支持见 operator_spaceship 示例。各自的完整用法见 JSON_HAS_RANGES、JSON_HAS_STD_FORMAT、JSON_HAS_THREE_WAY_COMPARISON 文档。平台与安全裁剪JSON_NO_IO去除对 C I/O 头文件的依赖定义后库不再包含cstdio、ios、iosfwd、istream、ostream等头文件并剔除依赖它们的parse从流/文件解析等函数。这对禁止 I/O 的高安全环境如 Intel SGX 飞地至关重要。参考 JSON_NO_IO 文档。JSON_SKIP_UNSUPPORTED_COMPILER_CHECK跳过编译器检测默认情况下库检测到已知不完整支持 C11 的编译器会产生编译错误。定义本宏后该检查被跳过可在未完全支持 C11 的编译器上尝试编译前提是不使用它不支持的库特性。参考 JSON_SKIP_UNSUPPORTED_COMPILER_CHECK 文档。JSON_USE_GLOBAL_UDLS用户定义字面量的命名空间位置定义为1默认时operator_json与operator_json_pointer被放入全局命名空间否则位于nlohmann::literals::json_literals。当项目已有同名 UDL 冲突时可将其置入受限命名空间。参考 JSON_USE_GLOBAL_UDLS 文档。库版本与内联命名空间宏JSON_SKIP_LIBRARY_VERSION_CHECK若同一编译单元通过不同路径混入了多个版本的头文件库默认会发出警告定义本宏即可跳过版本一致性检查。参考 JSON_SKIP_LIBRARY_VERSION_CHECK 文档。NLOHMANN_JSON_VERSION_MAJOR / MINOR / PATCH这三个宏由库自行定义内容符合 语义化版本 2.0.0 中确认。参考 NLOHMANN_JSON_VERSION_MAJOR 文档。NLOHMANN_JSON_NAMESPACE / _BEGIN / _END / _NO_VERSION从 3.11.0 起nlohmann命名空间下还有一个随版本变化的内联 ABI 命名空间默认结构见 namespace 结构文档。相关宏用于#define NLOHMANN_JSON_NAMESPACE /* 值为 nlohmann 完整命名空间名含内联 ABI 部分 */NLOHMANN_JSON_NAMESPACE展开为命名空间全名可用它替代直接写nlohmann在代码中输出或做宏拼接NLOHMANN_JSON_NAMESPACE_BEGIN/NLOHMANN_JSON_NAMESPACE_END打开/关闭该命名空间典型用于为第三方类型添加to_json/from_json特化时进入正确的命名空间NLOHMANN_JSON_NAMESPACE_NO_VERSION定义为1时去掉内联命名空间的版本分量。后两者的默认定义形如namespace nlohmann { inline namespace json_abi_v3_12_0 {} // namespace json_abi_v3_12_0 } // namespace nlohmann命名空间宏的意义在于 ABI 隔离当同一个二进制中混入不同配置例如部分代码开启JSON_DIAGNOSTICS、部分未开启时宏取值被编码进命名空间从而生成不同的符号名避免 ODR 违规。相关示例与用法见 NLOHMANN_JSON_NAMESPACE 文档、NAMESPACE_BEGIN/END 文档、NAMESPACE_NO_VERSION 文档及命名空间特性页。类型转换行为开关JSON_USE_IMPLICIT_CONVERSIONS默认隐式转换开启1。若希望所有取值必须显式调用getT()可定义为0关闭从而避免隐式operator ValueType引发的意外类型转换。参考 JSON_USE_IMPLICIT_CONVERSIONS 文档。JSON_BRACE_INIT_COPY_SEMANTICS定义为1时json j{value};这种单元素花括号初始化被视为对value的拷贝/移动而不是把 value 包进单元素数组默认0保持既有语义。它修正的是 C 列表初始化带来的歧义。参考 JSON_BRACE_INIT_COPY_SEMANTICS 文档。JSON_DISABLE_ENUM_SERIALIZATION定义后库不再为枚举类型自动生成默认的序列化/反序列化函数默认把枚举当整数处理必须由用户显式提供例如配合NLOHMANN_JSON_SERIALIZE_ENUM。参考 JSON_DISABLE_ENUM_SERIALIZATION 文档。比较行为JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON解析回调产生的 discarded 值在比较语义上有历史遗留问题。该宏默认0discarded 值不与自己相等定义为1则恢复旧行为discarded discarded。旧行为已被弃用。参考 JSON_USE_LEGACY_DISCARDED_VALUE_COMPARISON 文档。序列化/反序列化宏这一族宏是索引页篇幅最大、也最常被实战使用的部分全部围绕用 JSON 对象字段名映射 C 成员名展开。具体展开后的等价手写代码官方在任意类型转换页 arbitrary_types.md 有详细讨论。枚举NLOHMANN_JSON_SERIALIZE_ENUM(_STRICT)默认枚举会被序列化为整数一旦枚举被重排或插入新值旧 JSON 反序列化结果可能错位或未定义。该宏允许为每个枚举值指定稳定的 JSON 表示#define NLOHMANN_JSON_SERIALIZE_ENUM(type, conversion...)type为枚举类型名conversion为(枚举值, JSON 值)的成对列表。宏会在枚举所在命名空间内生成如下两个函数templatetypename BasicJsonType inline void to_json(BasicJsonType j, const type e); templatetypename BasicJsonType inline void from_json(const BasicJsonType j, type e);底层实现见 macro_scope.hpp它用static_assert校验类型确实是枚举然后把转换表放进静态数组to_json用std::find_if查找首个匹配的枚举值——因此列表靠前的转换优先。例如让Color::red序列化为red同时允许从rot反序列化回Color::red就需把第二组映射放在后面作备选完整示例见 nlohmann_json_serialize_enum_2.cpp。两个重要行为当getENUM_TYPE()遇到未定义的 JSON 值时默认回落到第一个指定的转换——因此应把兜底映射放在首位若同一枚举或 JSON 值出现在多个转换中取从上往下第一个命中。NLOHMANN_JSON_SERIALIZE_ENUM_STRICT是严格变体输入未在映射表中定义时抛异常而不是回落到第一个映射。两者分别见 枚举序列化文档与严格变体文档更多背景在枚举转换特性页。仓库测试用例位于 tests/src/unit-udt.cpp。类的三大变体INTRUSIVE / NON_INTRUSIVE / ONLY_SERIALIZE对普通类class/struct库提供侵入式与非侵入式两类宏每类又有三种语义变体// 1. 普通版反序列化用 at()缺 key 抛 out_of_range.403 // 2. WITH_DEFAULT反序列化用 value()缺 key 回落默认值 // 3. ONLY_SERIALIZE只生成 to_json不生成 from_json // —— 侵入式宏写在类体内部可访问 private 成员—— #define NLOHMANN_DEFINE_TYPE_INTRUSIVE(type, member...) #define NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT(type, member...) #define NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE(type, member...) // —— 非侵入式宏写在类外、所在命名空间内只能访问 public 成员—— #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(type, member...) #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(type, member...) #define NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE(type, member...)选择对照如下需要访问 private 成员只需序列化反序列化允许缺字段推荐宏是否否NLOHMANN_DEFINE_TYPE_INTRUSIVE是否是NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT是是不适用NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE否否否NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE否否是NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT否是不适用NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_ONLY_SERIALIZE要点说明可对照侵入式/非侵入式两篇完整文档 intrusive / non-intrusive参数第一个是类型名其后是以逗号分隔的成员名列表当前实现上限为 63 个成员超出需手写to_json/from_json缺字段处理普通版from_json内部走at()key 缺失抛out_of_range.403WITH_DEFAULT版内部默认构造一个同类型对象把其成员值作为默认值传给value()填充缺失字段等价展开见 nlohmann_define_type_intrusive_with_default_explicit.cppONLY_SERIALIZE版适用于无默认构造函数、只需输出 JSON 的类型这两类宏只会生成对象式键名JSON一成员一 key若需要把成员按位置序列化成 JSON 数组没有现成宏须手写to_json/from_json自行构造/解析json::array()侵入式宏在类内生成friend void to_json/from_json因此能访问私有成员非侵入式要求被列成员为 public宏展开源码见 macro_scope.hpp 中 NLOHMANN_DEFINE_TYPE_INTRUSIVE 族仓库内对应宏与等价手写示例文件位于 docs/mkdocs/docs/examples如nlohmann_define_type_intrusive_macro.cpp、nlohmann_define_type_non_intrusive_explicit.cpp等并有unit-udt_macro.cpp等测试覆盖。派生类与自定义字段名DERIVED / WITH_NAMES 系列针对继承场景与字段名定制索引页还列出两组扩展派生类宏nlohmann_define_derived_type.mdNLOHMANN_DEFINE_DERIVED_TYPE_INTRUSIVE、_WITH_DEFAULT、_ONLY_SERIALIZE及对应的_NON_INTRUSIVE三个变体用于同时序列化基类与派生类成员自定义字段名宏nlohmann_define_type_with_names.md..._WITH_NAMES变体允许把 JSON key 映射为不同于 C 成员名的字符串同样覆盖侵入式/非侵入式、派生类以及_WITH_DEFAULT、_ONLY_SERIALIZE的组合例如宏语义NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_NAMES侵入式key 用自定义名NLOHMANN_DEFINE_TYPE_INTRUSIVE_WITH_DEFAULT_WITH_NAMES侵入式 允许缺字段 自定义名NLOHMANN_DEFINE_TYPE_INTRUSIVE_ONLY_SERIALIZE_WITH_NAMES侵入式仅序列化自定义名NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_NAMES及_WITH_DEFAULT_/_ONLY_SERIALIZE_组合非侵入式等价变体NLOHMANN_DEFINE_DERIVED_TYPE_*_WITH_NAMES派生类等价变体完整 18 个成员宏的清单、签名与历史版本NLOHMANN_DEFINE_TYPE_*族 3.9.0 起引入WITH_DEFAULT3.11.0、ONLY_SERIALIZE3.11.3 等均可回到 API Macros 索引页逐项点击查阅。选型建议小结只想更安全定义JSON_ASSERT自定义断言日志或JSON_USE_IMPLICIT_CONVERSIONS0让取值全部显式化不想要异常/I-OJSON_NOEXCEPTION或-fno-exceptionsJSON_NO_IO适合安全飞地与资源受限平台排错困难开JSON_DIAGNOSTICS异常携带 JSON Pointer与JSON_DIAGNOSTIC_POSITIONS解析位置接受体积/性能开销编译器探测不准按需定义JSON_HAS_CPP_17、JSON_HAS_FILESYSTEM等强制覆盖多版本/多配置共存理解并善用NLOHMANN_JSON_NAMESPACE_*的 ABI 内联命名空间机制希望类型零样板接入 JSON先按是否需要 private、是否允许缺字段、是否只需写出三个问题在对照表中选定基础变体再按是否派生类、是否需要别名 key决定是否叠加DERIVED与WITH_NAMES。所有宏的官方逐项文档均可从 API Macros 总览出发继续深入配套示例源码位于 docs/mkdocs/docs/examples宏实现可研读 macro_scope.hpp 与 abi_macros.hpp。【免费下载链接】jsonJSON for Modern C项目地址: https://gitcode.com/GitHub_Trending/js/json创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表