动态数据类型 folly::dynamic

作者: , 共 3808 字 , 共阅读 0

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 arr = folly::dynamic::array(n, i, s, true);
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";

1.2. 判断和获取类型

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. 获取值

getType系列函数可以从对象中取出原始数值,若类型不一致将抛出异常:

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() &&;

asType系列函数会将试图转换成指定类型,若无法转换,将抛出异常:

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()

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 arsss1

1.7. 注意事项

  1. 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的基类。这个基类提供一个功能,如果实现了一些运算符,那么基类将自动扩展相关的运算符。比如如果定义了==符号,那么!=就被自动扩展。这样可以少写很多代码,又能最大程度地覆盖到所有运算符。

初始化的内容很多,原因是除了基本类型,还需要支持objectarray。里面还是有很多弯弯绕绕的东西,比如下面的初始化:

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.

类似文章:
编程 » folly, C++, 数据容器
由 Facebook 开发和维护的 C++库 Folly 提供folly::sorted_vector_setfolly::sorted_vector_map,是std::mapstd::set在小数据集上的优化版。代码见: https://github.com/facebook/folly/blob/master/folly/sorted_vector_types.h
编程 » folly, C++, 数据容器
由 Facebook 开发和维护的 C++库 Folly 提供folly::small_vector,代码文件地址:https://github.com/facebook/folly/blob/master/folly/small_vector.h
编程 »
一般而言,在 Python 里:
编程 » C++, 数据容器
我们知道在javascript以及Python 3.6+中,所有的dict都保留了插入顺序。但在 C++中,无论是std::map还是std::unordered_map,都没有保留插入顺序。当遍历时,std::map得到的是一个根据键值排序的有序序列,而std::unordered_map则基本是乱序。
编程 » C++, 数据容器
我们知道在javascript以及Python 3.6+中,所有的dict都保留了插入顺序。但在 C++中,无论是std::map还是std::unordered_map,都没有保留插入顺序。当遍历时,std::map得到的是一个根据键值排序的有序序列,而std::unordered_map则基本是乱序。
编程 » C++, Boost, 数据容器
Boost.Intrusive 是一个很有意思的实现,里面实现了很多侵入式容器,在特定环境下,可以大大提升性能。
编程 » C++, folly
高效程序总是尽量避免频繁触碰在堆上分配和释放内存,所以无论是std::string还是folly:fbstring都做了SSO( small string optimization )。而folly::FixedString是一个很有意思的实现,它可以把任意长度的字符串都放在堆上。代码可见https://github.com/facebook/folly/blob/master/folly/FixedString.h
编程 » C++, C++11
花括号初始化是C++11引入的一种初始化方法。
编程 » C++, folly
folly::fbstring是一个完全兼容std::string的类,可以做到无缝替换,而且性能更高。其代码参见https://github.com/facebook/folly/blob/master/folly/FBString.h
由 Facebook 开发和维护的 C++库 Folly 提供了锁folly::MicroLock,代码文件地址:https://github.com/facebook/folly/blob/master/folly/MicroLock.h
编程 » C++, 数据容器
我们知道在javascript以及Python 3.6+中,所有的dict都保留了插入顺序。但在 C++中,无论是std::map还是std::unordered_map,都没有保留插入顺序。当遍历时,std::map得到的是一个根据键值排序的有序序列,而std::unordered_map则基本是乱序。
编程 » C++, folly
folly::fbstring是一个完全兼容std::string的类,可以做到无缝替换,而且性能更高。其代码参见https://github.com/facebook/folly/blob/master/folly/FBString.h