尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

nlohmann/json 的 operator[] 访问 const 对象不存在的键触发运行时断言怎么避免

nlohmann/json 的 operator[] 访问 const 对象不存在的键触发运行时断言怎么避免 nlohmann/json 的 operator[] 访问 const 对象不存在的键触发运行时断言怎么避免【免费下载链接】jsonJSON for Modern C项目地址: https://gitcode.com/GitHub_Trending/js/json在 nlohmann/jsonJSON for Modern C中如果一个const nlohmann::json对象用operator[]访问一个不存在的键debug 构建下会触发运行时断言并中止程序。官方文档明确const 重载对缺失键的访问是未定义行为库用 运行时断言 来捕获这类客户端代码里的 bug而不是替你插入键值。要避免这个断言正确做法是把 unchecked 访问换成 checked 访问at或先用contains确认键存在再访问如果只想改变断言行为本身也可以在包含头文件前重定义JSON_ASSERT(x)宏。如何确认遇到的就是这条断言运行时断言文档给出的最小复现代码#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.如果你的程序在 debug 构建下打印出形如Assertion failed: ... function operator[]的信息并且触发点是j[some_key]、j是const、键在对象里不存在那就属于这一条。根本原因const 与非 const 重载行为不同operator[]的 API 文档对两种重载的说明如下非 const 重载键不存在时会静默地把该键插入对象并填入null值再返回引用——所以非 const 对象上j[missing]不会触发断言副作用是对象被多写入了一个null键。const 重载没有插入语义键不存在时行为是未定义行为并且被运行时断言保护。unchecked access 文档的汇总表给出了完整判定场景非 const 值const 值访问已存在的对象键返回已有值的引用返回已有值的 const 引用访问不存在的对象键返回新插入的null值的引用未定义行为debug 模式下触发运行时断言访问非法数组下标数组补null扩容未定义行为debug 模式下触发运行时断言注意两条结论如果变量意外是const比如参数按 const 引用传递、成员函数是 const 成员代码从写入语义悄悄变成读语义同一句j[key]就会从插入变成断言。检查变量是否真的是 const 是排查的第一步。数组下标同理const 数组用越界下标访问也是未定义行为并触发断言。主路径改为 checked 访问官方文档在 运行时断言 页面给出的直接建议是不确定对象里是否有某个元素时用带范围检查的at函数或先调用contains函数再访问。两种写法都只适用于读取可能缺失的键这一目标。先 contains 再 operator[]contains文档给出了一条可以直接依赖的保证如果j.contains(x)返回true则调用j[x]是安全的。contains对非对象类型和不存在键都只返回false不抛异常。组合官方文档中的值得到这样的访问模式#include iostream #include nlohmann/json.hpp using json nlohmann::json; int main() { const json j {{key, value}}; if (j.contains(missing)) { auto v j[missing]; std::cout v \n; } else { std::cout key not found \n; } }键不存在时走 else 分支operator[]根本不会被执行断言无从触发。这是官方推荐的最直接改法。用 at 做 checked 访问并处理异常at文档说明at带范围检查键不存在时抛out_of_range.403对非对象类型用at访问键则抛type_error.304。官方示例来自 at 的 const 示例#include iostream #include nlohmann/json.hpp using json nlohmann::json; int main() { // create JSON object const json object { {the good, il buono}, {the bad, il cattivo}, {the ugly, il brutto} }; // output element with key the ugly std::cout object.at(the ugly) \n; // exception type_error.304 try { // use at() on a non-object type const json str I am a string; std::cout str.at(the good) \n; } catch (const json::type_error e) { std::cout e.what() \n; } // exception out_of_range.401 try { // try to read from a nonexisting key std::cout object.at(the fast) \n; } catch (const json::out_of_range) { std::cout out of range \n; } }文档示例的输出il brutto [json.exception.type_error.304] cannot use at() with string out of range这样缺失键不再表现为程序中止而是可捕获的json::out_of_range异常便于按业务逻辑处理。checked access 文档还提到定义JSON_DIAGNOSTICS开启扩展诊断后异常信息会额外给出键缺失的位置如(/hobbies) array index 3 is out of range排查时可用。可选分支改变断言本身的行为上面的改法消除了未定义行为如果只是想让断言不出现而不改访问逻辑还有两个文档给出的开关但注意它们只是改变断言行为不改变访问本身是未定义行为这一事实。定义NDEBUG断言文档说明运行时断言可通过定义预处理器宏NDEBUG关闭release 构建默认如此。也就是说 release 构建里同样的代码不会打印断言、直接落入未定义行为。重定义JSON_ASSERT(x)JSON_ASSERT 文档说明该宏的默认定义是#define JSON_ASSERT(x) assert(x)可在包含json.hpp头文件之前定义来改变断言行为。文档示例#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[]需要留意文档给出的限制把JSON_ASSERT定义为不调用std::abort的代码可能使库处于未定义状态。所以关闭断言或改成不中止的行为只是移除保护不是修复contains/at改法仍是唯一消除未定义行为的路径。验证与边界修改后的验证方式直接对应上面两条主路径contains写法编译运行后键缺失时程序正常走到 else 分支打印提示信息不再出现Assertion failed: ... function operator[]键存在时正常输出值。at写法键缺失时异常被捕获并打印out_of_range相关信息如示例输出中的out of range程序不再中止对非对象类型调用时得到type_error.304也可被捕获。边界情况按文档限定const 数组越界下标访问同样是未定义行为加断言at对数组下标抛out_of_range.401见 checked access 文档。contains对非对象类型恒返回false不抛异常可以安全地在任意json值上调用。非 const 对象上operator[]访问缺失键不会断言但会向对象写入null键如果业务上不希望产生这个副作用同样应改用containsat。相关文档可继续参考unchecked access、checked access、runtime assertions 以及 JSON_ASSERT 宏。【免费下载链接】jsonJSON for Modern C项目地址: https://gitcode.com/GitHub_Trending/js/json创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表