现代c++开发利器folly教程系列之:dynamic
黑客画家 发表于3个月前
现代c++开发利器folly教程系列之:dynamic
  • 发表于 3个月前
  • 阅读 2588
  • 收藏 57
  • 点赞 2
  • 评论 8

330元/年抢阿里云香港云服务器,节省80%出海成本>>>   

一、前言

       用过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

 

共有 人打赏支持
粉丝 62
博文 56
码字总数 149650
评论 (8)
hzh62
只是这框架用了 boost,有点重!!
lniwn
效率方面有测试数据吗?
黑暗料理魔王
越来越像Python
谁杀了我的牛
这东西好像不能在windows上用啊·!
little_kid
与其这么麻烦干嘛 不用别的语言,比如说python
Apm29
kankan@南极以北
冰力
variant in dlang
颜风
其实静态类型挺好 写了一段时间JS后 突然发现
×
黑客画家
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
* 金额(元)
¥1 ¥5 ¥10 ¥20 其他金额
打赏人
留言
* 支付类型
微信扫码支付
打赏金额:
已支付成功
打赏金额: