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;
// 返回数组index
ArrayIndex 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' value
intValue, ///< signed integer value
uintValue, ///< unsigned integer value
realValue, ///< double value
stringValue, ///< UTF-8 string value
booleanValue, ///< bool value
arrayValue, ///< 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 later
return 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++专栏中有介绍。