大家好,欢迎来到IT知识分享网。
一、前言
用过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
转载于:https://my.oschina.net/fileoptions/blog/883002
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/23350.html