folly::dynamic
提供类似于C++
的动态类型。和std::any
可以容纳任意类型不一样,folly::dynamic
只支持保存以下几种类型:
enum Type {
NULLT, // nullptr_t
ARRAY, // std::vector<folly::dynamic>
BOOL, // bool
DOUBLE, // double
INT64, // int64_t
OBJECT, // 类似于 std::unordered_map<folly::dynamic, folly::dynamic>
STRING, // std::string
};
因为类型少,基本所有数据都是inline
保存的,这使得它的速度非常快。
1、使用方法
在https://my.oschina.net/fileoptions/blog/883002基础上整理。
1.1、初始化
folly::dynamic n = nullptr;
folly::dynamic b = true;
folly::dynamic f = 1.2;
folly::dynamic i = 1;
folly::dynamic o = folly::dynamic::object(b, f)(f, arr);
folly::dynamic s = "abc";
folly::dynamic arr = folly::dynamic::array(n, i, s, o, true);
1.2、判断和获取类型
is{Type}
系列函数可用于判断是否指定类型:
bool isString() const;
bool isObject() const;
bool isBool() const;
bool isNull() const;
bool isArray() const;
bool isDouble() const;
bool isInt() const;
bool isNumber() const; // Returns: isInt() || isDouble().
Type type() const; // return Type
1.3、获取值
get{Type}
系列函数可以从对象中取出原始数值,若类型不一致将抛出异常:
const std::string& getString() const&;
double getDouble() const&;
int64_t getInt() const&;
bool getBool() const&;
std::string& getString() &;
double& getDouble() &;
int64_t& getInt() &;
bool& getBool() &;
std::string&& getString() &&;
double getDouble() &&;
int64_t getInt() &&;
bool getBool() &&;
as{Type}
系列函数会将试图转换成指定类型,若无法转换,将抛出异常:
std::string asString() const;
double asDouble() const;
int64_t asInt() const;
bool asBool() const;
1.4、数组和字典遍历
数组的遍历和std::vector
一致,支持iterator
,因此下面都是合法的:
for (auto item : arr) {
// ...
}
通过下标随机访问,比如arr[10]
也是可以的。std::vector
的 API 如push_back
, size
, resize
等都是支持的。
字典的遍历则和Python
类似,需要通过items()
,keys()
和values()
来确定遍历的具体内容:
for (auto pair : obj.items()) {
// Key is pair.first, value is pair.second
processKey(pair.first);
processValue(pair.second);
}
// 单独遍历key
for (auto key : obj.keys()) {
processKey(key);
}
// 单独遍历value
for (auto value : obj.values()) {
processValue(value);
}
然后也支持obj[key]
访问,std::unordered_map
的 API 如obj.at(key)
,obj.find(key)
等也都是支持的。
如果要判断 key 存在,可以用:obj.find(key) != obj.items().end()
。还有个方法是auto pos = obj.get_ptr(key)
,然后判断pos != nullptr
,可以通过pos->getString()
来获取到值。
1.5、计算
这个类的强大之处在于,它和 Python 一样,支持dynamic
对象之间以及和普通类型之间的计算:
dynamic b1 = s < "xyz";
dynamic s1 = s + "abc";
dynamic i1 = i - 1;
dynamic is = s + i; // 抛出异常。
当运算不满足类型要求时,计算将抛出异常。这也和 Python 一致。
1.6、JSON 导入和导出
folly::dynamic
的强大之处在于它支持 JSON 的导入和导出:
folly::dynamic arr = folly::dynamic::array(1, "123", true);
std::string json = toJson(arr);
folly::dynamic imported = parseJson(json); // will equal arr
1.7、注意事项
obj = obj[key]
会导致obj == nullptr
。这涉及到 C++的内存管理。可以用:folly::dynamic tmp = obj[key]; obj = key;
来代替。
2、代码分析
总地来说,folly::dynamic
的实现不算特别难懂。
2.1、内存结构
内存结构很简单,除了类型,就是共享数据区。
struct dynamic {
Type type_; // enum
union Data {
std::nullptr_t nul;
std::vector<dynamic> array;
bool boolean;
double doubl;
int64_t integer;
std::string string;
aligned_storage_for_t<F14NodeMap<int, int>> objectBuffer;
} u_;
};
唯一可以说的是最后一个aligned_storage_for_t<F14NodeMap<int, int>> objectBuffer;
,这个目标是保存字典数据。F14NodeMap
是 folly 提供的一个更高效的std::unordered_map
。这里用了F14NodeMap<int, int>
,而不是F14NodeMap<dynamic, dynamic>
,因为后者还未能定义(?)。
2.2、基类和初始化
boost::operators<dynamic>
是dynamic
的基类。这个基类提供一个功能,如果实现了一些运算符,那么基类将自动扩展相关的运算符。比如如果定义了==
符号,那么!=
就被自动扩展。这样可以少写很多代码,又能最大程度地覆盖到所有运算符。
初始化的内容很多,原因是除了基本类型,还需要支持object
和array
。里面还是有很多弯弯绕绕的东西,比如下面的初始化:
ObjectMaker() : val_(dynamic::object) {} // dynamic val_;
ObjectMaker dynamic::object() {
return ObjectMaker();
}
咋一看,还以为出现循环调用,但其实第一行的val_(dynamic::object)
调用的是下面这个:
inline dynamic::dynamic(ObjectMaker (*)()) : type_(OBJECT) {
new (getAddress<ObjectImpl>()) ObjectImpl();
}
未完待续。。。
Q. E. D.