您的位置:首页 > 编程语言 > C语言/C++

现代c++开发利器folly教程系列之:dynamic

2017-04-20 19:00 531 查看

一、前言

用过python、php等动态类型语言的人肯定对动态数据类型不陌生。对于定义时不确定、运行时才确定的数据类型,使用动态类型是非常方便的。c++是一门不折不扣的静态类型语言,那么是否就无缘享受“动态”类型的好处了呢?不尽然。folly为我们提供了dynamic类型,从一定程度上实现了c++中的“动态”数据类型,为什么说是“一定程度上”呢,因为dynamic类型只支持c++中的基本类型(部分)和复合类型(array和map),不支持自定义类型。你可能会把dynamic和boost::any或std::any(将在c++17中支持)进行对比,但是其实它们没有可比性,首先是它们的实现原理不同。boost::any虽然可以盛放任何类型,但是它在实现上是用继承的方式进行了类型擦除,因此在还原类型时,需要程序员自己显示的提供类型信息,这也是boost::any的缺点之一。而dynamic虽然代表的类型有限,但是dynamic本身可以记住类型,便于赋值和还原。因此,dynamic不是boost::any的替代品,更像是一种补充,如果非要类比的话,dynamic和boost::variant更为相似。

二、类型支持

如前文所述,dynamic可以盛放的类型是有限的,它可以盛放部分基本类型、字符串类型、数组类型和OBJECT(本质为map)类型,具体支持的类型如下:

enum Type {
NULLT,
ARRAY,
BOOL,
DOUBLE,
INT64,
OBJECT,
STRING,
};

可以看到,在整型支持方面,dynamic只支持最宽的整型INT64,而没有对其他整型进行细分,其实这个可以理解,最大整型都支持了,其它的类型就不在话下了,缺点无非就是浪费一点内存了。ARRAY类型就是经常使用的数组,不同的是,这里的数组的元素类型也为dynamic,同样,OBJECT类型就是一个map类型(为什么叫OBJECT呢,OBJECT的中文为对象的意思,而一个对象本质上是由一个个的key-value属性键值对构成的),这个map的key和value都为dynamic类型。

三、基本用法

1、赋值(初始化)

直接看一段应用代码:

dynamic twelve = 12; // creates a dynamic that holds an integer
// STRING类型
dynamic str = "string";
// NULL类型
dynamic nul = nullptr;
// BOOL类型
dynamic boolean = false;

// ARRAY类型
dynamic array = dynamic::array("array ", "of ", 4, " elements");
assert(array.size() == 4);
dynamic emptyArray = dynamic::array;
assert(emptyArray.empty());

// 使用dynamic::objec可以构造一个空的map
dynamic map = dynamic::object;
map["something"] = 12;
map["another_something"] = map["something"] * 2;

// 也可以在构造的时候直接初始化一个map
dynamic map2 = dynamic::object("something", 12)("another_something", 24);

可以看到,虽然dynamic在类型支持上是有限的,但是通过组合、搭配可以满足日常开发中的绝大多数场景。

除了类型之外,如果在赋值时使用了dynamic不支持的非法运算符操作,有可能会抛出folly::TypeError错误。例如:

dynamic dint = 42;

dynamic str = "foo";
dynamic anotherStr = str + "something"; // fine
dynamic thisThrows = str + dint; // 将字符串与整形相加 TypeError is raised


2、取值

dynamic最大的优点就是它可以记得自己存储的数据类型,那么如何获得类型呢?dynamic提供了类型获取api,如下:

/*
* Returns true if this dynamic is of the specified type.
*/
bool isString() const;
bool isObject() const;
bool isBool() const;
bool isNull() const;
bool isArray() const;
bool isDouble() const;
bool isInt() const;
/*
* Returns: isInt() || isDouble().
*/
bool isNumber() const;
/*
* Returns the type of this dynamic.
*/
Type type() const;

常见用法如下:

dynamic str = "my name is cy";
assert(str.isString());

dynamic integer = 123;
assert(integer.isInt());

dynamic map = dynamic::object;
assert(map.isObject());

这只是类型判断,并没有进行取值操作,取值操作同样有相应的api,如下:

/*
* Extract a value while trying to convert to the specified type.
* Throws exceptions if we cannot convert from the real type to the
* requested type.
*
* Note you can only use this to access integral types or strings,
* since arrays and objects are generally best dealt with as a
* dynamic.
*/
std::string asString() const;
double   asDouble() const;
int64_t  asInt() const;
bool     asBool() const;

/*
* Extract the value stored in this dynamic without type conversion.
*
* These will throw a TypeError if the dynamic has a different 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() &&;

从上面的注释可以清晰的看到,以get开头的api会原样提取dynamic存储的值,一旦类型不匹配,就会抛出TypeError异常,而以as开头的api带有类型转换的意思,比如dynamic本身存储了一个整型,但是可以将其as字符串的形式取出来,例如:

dynamic dint = 12345678;

auto integer = dint.getInt();//done
auto str = dint.getString();// TypeError
auto str2 = dint.asString();// done


三、遍历

1、数组遍历

数组的遍历和正常的foreach遍历是一样的:

dynamic array = dynamic::array(2, 3, "foo");

for (auto  val : array) {
doSomethingWith(val);
}


2、map遍历

OBJECT(map)的遍历稍微复杂一点,需要注意的是,不能直接遍历一个OBJECT,而是先要使用items()方法取出dynamic内部真正的map才可以,同样,如果想要单独遍历key或者value,则只要使用dynamic的keys和values方法即可:

dynamic obj = dynamic::object(2, 3)("hello", "world")("x", 4);

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);
}

关于map的查找,它提供了和stl兼容的find方法,比如:

dynamic obj = dynamic::object(2, 3)("hello", "world")("x", 4);

auto pos = obj.find("hello");
// pos->first is "hello"
// pos->second is "world"

auto pos = obj.find("no_such_key");
// pos == obj.items().end()

其实在实际应用中,OBJECT就是一个kv存储(内存),经常需要判断一个key是否在缓存中,使用上面的find方法还是比较麻烦的,我更推荐使用get_ptr方法,比如我想判断OBJECT中是否存在一个名为“age”的键值对,如果存在就取出这个age值(职位INT64类型),代码如下:

dynamic obj = dynamic::object("age", 28);
auto age_ptr = obj.get_ptr("age");
if(age_ptr){
auto age = age_ptr->asInt();
}

当OBJECT中不包含对象的key时,get_ptr将返回一个nullptr。

四、JSON序列化、反序列化

folly为dynamic提供了内置的json支持,这对于经常使用json进行序列化和反序列化的应用而言非常强大和方便(再也不用使用jsoncpp这么难用的库了),序列化和反序列化接口很简单,定义如下:

/*
* Serialize a dynamic into a json string.
*/
std::string toJson(dynamic const&);
/*
* Parse a json blob out of a range and produce a dynamic representing
* it.
*/
dynamic parseJson(StringPiece, json::serialization_opts const&);
dynamic parseJson(StringPiece);

直接看个例子:

// 先定义一个json字符串
std::string jsonDocument = R"({"key":12,"key2":[false, null, true, "yay"]})";
// 执行json反序列化,反序列化结果为dynamic
dynamic parsed = folly::parseJson(jsonDocument);
assert(parsed["key"] == 12);
assert(parsed["key2"][0] == false);
assert(parsed["key2"][1] == nullptr);

// 构建一个OBJECT
dynamic sonOfAJ = dynamic::object
("key", 12)
("key2", dynamic::array(false, nullptr, true, "yay"));

// json序列化
auto str = folly::toJson(sonOfAJ);
assert(jsonDocument.compare(str) == 0);


本系列文章

现代c++开发利器folly教程系列之:future/promise

现代c++开发利器folly教程系列之:dynamic
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  folly dynamic 教程 json c++11
相关文章推荐