JsonCPP源码分析——Value

embedded/2024/9/23 8:13:53/

1、Json::Value类中有一个自定义的字符串结构体,这个结构体的概念是非常重要的,设计的非常巧妙,首先介绍下这个结构体,个人理解以注释的方式添加到下面的代码中:

 class CZString {public:// 枚举,标识字符串类型的key,有没有payload,也可以理解为,字符串的首地址cstr_后面的空间是不是自己所拥有,// 因为有些移动语义会共用其它CZString对象的空间enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy };//构造,感觉国内很少用char const*,更多的用 const char*,可能写库的大佬们认为char const*,一眼就可以看出是常量指针吧 CZString(ArrayIndex index);CZString(char const* str, unsigned length, DuplicationPolicy allocate); // 拷贝构造CZString(CZString const& other);CZString(CZString&& other) noexcept; // 不抛异常// 析构~CZString();// 赋值CZString& operator=(const CZString& other);CZString& operator=(CZString&& other) noexcept;// 自定义比较规则,因为底层用map实现,map的key要求重载<bool operator<(CZString const& other) const;bool operator==(CZString const& other) const;// 返回数组indexArrayIndex index() const;// const char* c_str() const; ///< \deprecated// 返回cstr_char const* data() const;// key是string时,返回字符串长度unsigned length() const;bool isStaticString() const;private:void swap(CZString& other);// policy_是枚举值,标明,cstr_指向空间的属性// length_ 字符串的长度// 这种C语言定义结构体的方式值得学习,可以节省空间struct StringStorage {unsigned policy_ : 2;unsigned length_ : 30; // 1GB max};// 数组cstr_是nullptr, object时是非空char const* cstr_; // actually, a prefixed string, unless policy is noDup// 联合体,程序员自己解释union {ArrayIndex index_;StringStorage storage_;};};

2、Json::Value类是Jsoncpp中一个至关重要的类,该类定义了数据的存储方式,可以理解为读写都是对该类进行的。在下面的代码中记录了一些个人认为值得学习的地方(只针对博主的知识储备而言,可能大佬觉着比较简单)。

// 支持的数据类型
enum ValueType {nullValue = 0, ///< 'null' valueintValue,      ///< signed integer valueuintValue,     ///< unsigned integer valuerealValue,     ///< double valuestringValue,   ///< UTF-8 string valuebooleanValue,  ///< bool valuearrayValue,    ///< array value (ordered list)objectValue    ///< object value (collection of name/value pairs).
};
// 利用type构造函数时,当type是字符串时,先将空字符串指针赋值给string_
// 这里逻辑上可能实现了闭环,如果自己使用的话,不要将const char* 指针赋值给char*,如果程序员用返回的指针修改空间的值
// 将会使const丧失作用,会造成未定义的行为,这种bug是最难查的。
Value::Value(ValueType type) {static char const emptyString[] = ""; // 静态,提高效率switch (type) {...case stringValue:// allocated_ == false, so this is safe.value_.string_ = const_cast<char*>(static_cast<char const*>(emptyString));...
}
// 字符串的开始是字符串的长度,这种做法是值得学习的
// 下次再看到这,可以顺便复习下static_cast 和 reinterpret_cast
static inline char* duplicateAndPrefixStringValue(const char* value,unsigned int length) {// Avoid an integer overflow in the call to malloc below by limiting length// to a sane value.JSON_ASSERT_MESSAGE(length <= static_cast<unsigned>(Value::maxInt) -sizeof(unsigned) - 1U,"in Json::Value::duplicateAndPrefixStringValue(): ""length too big for prefixing");size_t actualLength = sizeof(length) + length + 1;auto newString = static_cast<char*>(malloc(actualLength));if (newString == nullptr) {throwRuntimeError("in Json::Value::duplicateAndPrefixStringValue(): ""Failed to allocate string value buffer");}*reinterpret_cast<unsigned*>(newString) = length;memcpy(newString + sizeof(unsigned), value, length);newString[actualLength - 1U] =0; // to avoid buffer over-run accidents by users laterreturn newString;
}
// 读取数据String asString() const; Int asInt() const;UInt asUInt() const;
#if defined(JSON_HAS_INT64)Int64 asInt64() const;UInt64 asUInt64() const;
#endif // if defined(JSON_HAS_INT64)LargestInt asLargestInt() const;LargestUInt asLargestUInt() const;float asFloat() const;double asDouble() const;bool asBool() const;
// 这个的作用将在下一篇博客中进行介绍
explicit operator bool() const;
// 针对数组的接口函数
Value& append(const Value& value);
Value& append(Value&& value);
bool insert(ArrayIndex index, const Value& newValue);
bool insert(ArrayIndex index, Value&& newValue);
//真正存放数据的地方
private:
union ValueHolder {LargestInt int_;LargestUInt uint_;double real_;bool bool_;char* string_; // if allocated_, ptr to { unsigned, char[] }.ObjectValues* map_;
} value_;
// value_type_是数据类型,allocated_表示是否为 string_分配内存
struct {// Really a ValueType, but types should agree for bitfield packing.unsigned int value_type_ : 8;// Unless allocated_, string_ must be null-terminated.unsigned int allocated_ : 1;
} bits_;
// 注释类,注释可以在value的上一行,行内,下一行,有些中间件{ // 是不合法的,jsoncpp还没试,感觉也不合法
class Comments {
public:Comments() = default;Comments(const Comments& that);Comments(Comments&& that) noexcept;Comments& operator=(const Comments& that);Comments& operator=(Comments&& that) noexcept;bool has(CommentPlacement slot) const;String get(CommentPlacement slot) const;void set(CommentPlacement slot, String comment);private:using Array = std::array<String, numberOfCommentPlacement>;std::unique_ptr<Array> ptr_;
};Comments comments_;// 当前Value在整体的Json中的开始偏移,是左闭右开的
ptrdiff_t start_;
ptrdiff_t limit_;

3、Path和PathArgument,个人觉得设计的非常好,可以理解为通过模版的方式动态的获取json中的值。先举个例子,代码如下:

// 现有json如下,要查找Alice的第一个或者第二个朋友
// {
//   "user": {
//    "profile": {
//      "name": "Alice",
//      "age": 30,
//      "friends": [
//        "Bob",
//        "Carol"
//      ]
//    }
//  }
// }
#include <iostream>
#include "value.h" // 假设你已经有 jsoncpp中path模块int main() {Value root = ...; // 你的 JSON 数据int index = 1; // 用户指定的索引PathArgument indexArg(index);Path path(".user.profile.friends[%]", indexArg);// 获取指定索引的朋友名字const Value& friendName = path.resolve(root);std::cout << "Friend: " << friendName.asString() << std::endl;return 0;
}

Jsoncpp中实现了这种路径的方式来获取或者指定json中的值,JsonCpp中Path和PathArgument的定义如下:

class JSON_API PathArgument {
public:friend class Path;PathArgument();PathArgument(ArrayIndex index);PathArgument(const char* key);PathArgument(String key);private:// 表示占位符是什么类型的enum Kind { kindNone = 0, kindIndex, kindKey };String key_;ArrayIndex index_{};Kind kind_{kindNone};
};/** \brief Experimental and untested: represents a "path" to access a node.** Syntax:* - "." => root node* - ".[n]" => elements at index 'n' of root node (an array value)* - ".name" => member named 'name' of root node (an object value)* - ".name1.name2.name3"* - ".[0][1][2].name1[3]"* - ".%" => member name is provided as parameter* - ".[%]" => index is provided as parameter*/
class JSON_API Path {
public:Path(const String& path, const PathArgument& a1 = PathArgument(),const PathArgument& a2 = PathArgument(),const PathArgument& a3 = PathArgument(),const PathArgument& a4 = PathArgument(),const PathArgument& a5 = PathArgument());const Value& resolve(const Value& root) const;Value resolve(const Value& root, const Value& defaultValue) const;/// Creates the "path" to access the specified node and returns a reference on/// the node.Value& make(Value& root) const;private:using InArgs = std::vector<const PathArgument*>;using Args = std::vector<PathAArgument>;// 这个函数是实现该算法的关键,其实就是比较巧妙的字符串处理void makePath(const String& path, const InArgs& in);void addPathInArg(const String& path, const InArgs& in,InArgs::const_iterator& itInArg, PathArgument::Kind kind);static void invalidPath(const String& path, int location);Args args_;
};

4、value.h中还针对当前的数据结构设计了迭代器(说到迭代器,就要想到必备的几大要素,类型,指针,引用,距离等),异常处理等,下面是异常处理的代码:

class JSON_API Exception : public std::exception {
public:Exception(String msg);~Exception() noexcept override;char const* what() const noexcept override;protected:String msg_;
};
class JSON_API RuntimeError : public Exception {
public:RuntimeError(String const& msg);
};

5、这个模块中还涉及到一些数值计算(补码反码)、跨平台编程(宏)、定义不返回的函数[[noreturn]]、告诉编译器直接计算当表达式的值(constexpr)、不抛出异常(noexcept),只能通过显示转换(explicit)等技巧,这些在C++专栏中有介绍。


http://www.ppmy.cn/embedded/105563.html

相关文章

关于edge浏览器登陆CSDN安全验证不跳出验证码

前言 也就是最近这几天才出现这个问题&#xff0c;以前用edge浏览器登陆csdn时即使需要安全验证也能正常弹出验证码&#xff0c;现在根本没反应。 正文 我用edge浏览器登陆时&#xff0c;显示如下界面&#xff0c;就卡住不动了。 起初我以为是我浏览器可能设置了拦截的问题…

深度学习dataset等概念

DATASET 自定义 Dataset 类必须实现三个函数&#xff1a;init、len 和 getitem。 __init__ init 函数在实例化 Dataset 对象时运行一次 __len__ len 函数返回数据集中样本的数量。 __getitem__ getitem 函数加载并返回给定索引 idx 处的数据集样本。 DATALOADER 是在data…

数据结构--排序实现--C语言

文章目录 常见的排序算法一、插入排序1、直接插入排序&#xff1a;2、希尔排序 (缩小增量排序) 二、选择排序1、直接选择排序:2、堆排序 三、交换排序1、冒泡排序 2、快速排序快速排序优化 1、hoare版本2、挖坑法3、前后指针法 四、归并排序1、归并排序 五、非比较排序1、计数排…

【论文阅读】QUEEN: Query Unlearning against Model Extraction(2024)

摘要 Model extraction attacks(模型提取攻击) currently pose a non-negligible threat(不可忽视的威胁) to the security(安全性) and privacy(隐私性) of deep learning models. By querying the model with a small dataset(通过小数据集查询模型) and using th…

数据线性结构

一、线性表 优点&#xff1a;可以很快速的找到内存地址 查询&#xff0c;修改快 缺点&#xff1a;在中间部分新增&#xff0c;删除部时需要移动后续的元素 像java中的stream流的过滤等操作都是新建立一个集合有序插入返回&#xff0c;空间换时间 java中list下标为什么要从0开…

CAS带来的ABA问题以及解决方案

CAS带来的ABA问题 线程1和线程2同时对变量i 1 进行操作&#xff0c;线程1对i进行了两次操作&#xff0c;先将i1 写出&#xff0c;后又进行了i-1并写出&#xff0c;线程2过来读的时候与原来值1仍然是相等的&#xff0c;虽然值仍然相等&#xff0c;但是i的值却发生过改变&#x…

动态爱心绘制:基于 turtle 库的实现

本文将介绍如何使用 Python 中的 turtle 库绘制一组动态移动的爱心形状。通过这段代码&#xff0c;我们可以在屏幕上看到多颗不同颜色、大小的爱心&#xff0c;随着时间随机移动&#xff0c;产生浪漫的动态效果。 1. 项目结构 该程序由一个 Heart 类和一些辅助函数组成。Hear…

投资 - 什么是空中成交

在成交中有个专业名词叫“空中成交”&#xff0c; “空中成交”简单的说就是交易所下属会员【证券公司&#xff0c;具有席位资格投资公司】向交易所的成交捏合处理主机发送买卖交易单时&#xff0c;这些交易单还没有经过交易所对证券公司等行情始终端发送就已经捏合处理成交。这…