Assume GFlags isn't present if FOLLY_HAVE_LIBGFLAGS isn't defined
[folly.git] / folly / dynamic-inl.h
index 6e2fd62a290705d5d0e7f7902e2512a9e1bf8005..08374f7bc161883e4a43380dbec748ebfe0f8492 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2015 Facebook, Inc.
+ * Copyright 2016 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -14,8 +14,7 @@
  * limitations under the License.
  */
 
-#ifndef FOLLY_DYNAMIC_INL_H_
-#define FOLLY_DYNAMIC_INL_H_
+#pragma once
 
 #include <functional>
 #include <boost/iterator/iterator_adaptor.hpp>
@@ -41,18 +40,35 @@ struct hash< ::folly::dynamic> {
 
 // This is a higher-order preprocessor macro to aid going from runtime
 // types to the compile time type system.
-#define FB_DYNAMIC_APPLY(type, apply) do {         \
-  switch ((type)) {                             \
-  case NULLT:   apply(void*);          break;   \
-  case ARRAY:   apply(Array);          break;   \
-  case BOOL:    apply(bool);           break;   \
-  case DOUBLE:  apply(double);         break;   \
-  case INT64:   apply(int64_t);        break;   \
-  case OBJECT:  apply(ObjectImpl);     break;   \
-  case STRING:  apply(fbstring);       break;   \
-  default:      CHECK(0); abort();              \
-  }                                             \
-} while (0)
+#define FB_DYNAMIC_APPLY(type, apply) \
+  do {                                \
+    switch ((type)) {                 \
+      case NULLT:                     \
+        apply(void*);                 \
+        break;                        \
+      case ARRAY:                     \
+        apply(Array);                 \
+        break;                        \
+      case BOOL:                      \
+        apply(bool);                  \
+        break;                        \
+      case DOUBLE:                    \
+        apply(double);                \
+        break;                        \
+      case INT64:                     \
+        apply(int64_t);               \
+        break;                        \
+      case OBJECT:                    \
+        apply(ObjectImpl);            \
+        break;                        \
+      case STRING:                    \
+        apply(std::string);           \
+        break;                        \
+      default:                        \
+        CHECK(0);                     \
+        abort();                      \
+    }                                 \
+  } while (0)
 
 //////////////////////////////////////////////////////////////////////
 
@@ -91,14 +107,17 @@ namespace detail {
   > {
     typedef int64_t type;
   };
-  template<class T>
+  template <>
+  struct ConversionHelper<float> {
+    typedef double type;
+  };
+  template <class T>
   struct ConversionHelper<
-    T,
-    typename std::enable_if<
-      (!std::is_integral<T>::value || std::is_same<T,bool>::value) &&
-      !std::is_same<T,std::nullptr_t>::value
-    >::type
-  > {
+      T,
+      typename std::enable_if<
+          (!std::is_integral<T>::value || std::is_same<T, bool>::value) &&
+          !std::is_same<T, float>::value &&
+          !std::is_same<T, std::nullptr_t>::value>::type> {
     typedef T type;
   };
   template<class T>
@@ -189,9 +208,19 @@ private:
   dynamic val_;
 };
 
+inline void dynamic::array(EmptyArrayTag) {}
+
+template <class... Args>
+inline dynamic dynamic::array(Args&& ...args) {
+  return dynamic(Array{std::forward<Args>(args)...}, PrivateTag());
+}
+
 // This looks like a case for perfect forwarding, but our use of
 // std::initializer_list for constructing dynamic arrays makes it less
 // functional than doing this manually.
+
+// TODO(ott, 10300209): When the initializer_list constructor is gone,
+// simplify this.
 inline dynamic::ObjectMaker dynamic::object() { return ObjectMaker(); }
 inline dynamic::ObjectMaker dynamic::object(dynamic&& a, dynamic&& b) {
   return ObjectMaker(std::move(a), std::move(b));
@@ -246,6 +275,12 @@ struct dynamic::const_value_iterator
 
 //////////////////////////////////////////////////////////////////////
 
+inline dynamic::dynamic(void (*)(EmptyArrayTag))
+  : type_(ARRAY)
+{
+  new (&u_.array) Array();
+}
+
 inline dynamic::dynamic(ObjectMaker (*)())
   : type_(OBJECT)
 {
@@ -255,37 +290,31 @@ inline dynamic::dynamic(ObjectMaker (*)())
 inline dynamic::dynamic(StringPiece s)
   : type_(STRING)
 {
-  new (&u_.string) fbstring(s.data(), s.size());
+  new (&u_.string) std::string(s.data(), s.size());
 }
 
 inline dynamic::dynamic(char const* s)
   : type_(STRING)
 {
-  new (&u_.string) fbstring(s);
+  new (&u_.string) std::string(s);
 }
 
 inline dynamic::dynamic(std::string const& s)
   : type_(STRING)
 {
-  new (&u_.string) fbstring(s);
+  new (&u_.string) std::string(s);
 }
 
-inline dynamic::dynamic(fbstring const& s)
-  : type_(STRING)
-{
-  new (&u_.string) fbstring(s);
-}
-
-inline dynamic::dynamic(fbstring&& s)
-  : type_(STRING)
-{
-  new (&u_.string) fbstring(std::move(s));
+inline dynamic::dynamic(std::string&& s) : type_(STRING) {
+  new (&u_.string) std::string(std::move(s));
 }
 
 inline dynamic::dynamic(std::initializer_list<dynamic> il)
-  : type_(ARRAY)
-{
-  new (&u_.array) Array(il.begin(), il.end());
+    : dynamic(Array(std::move(il)), PrivateTag()) {}
+
+inline dynamic& dynamic::operator=(std::initializer_list<dynamic> il) {
+  (*this) = dynamic(Array(std::move(il)), PrivateTag());
+  return *this;
 }
 
 inline dynamic::dynamic(ObjectMaker&& maker)
@@ -366,7 +395,9 @@ inline dynamic::IterableProxy<dynamic::const_item_iterator> dynamic::items()
   return &(get<ObjectImpl>());
 }
 
-inline bool dynamic::isString() const { return get_nothrow<fbstring>(); }
+inline bool dynamic::isString() const {
+  return get_nothrow<std::string>();
+}
 inline bool dynamic::isObject() const { return get_nothrow<ObjectImpl>(); }
 inline bool dynamic::isBool()   const { return get_nothrow<bool>(); }
 inline bool dynamic::isArray()  const { return get_nothrow<Array>(); }
@@ -379,29 +410,49 @@ inline dynamic::Type dynamic::type() const {
   return type_;
 }
 
-inline fbstring dynamic::asString() const { return asImpl<fbstring>(); }
-inline double   dynamic::asDouble() const { return asImpl<double>(); }
-inline int64_t  dynamic::asInt()    const { return asImpl<int64_t>(); }
-inline bool     dynamic::asBool()   const { return asImpl<bool>(); }
+inline std::string dynamic::asString() const {
+  return asImpl<std::string>();
+}
+inline double dynamic::asDouble() const {
+  return asImpl<double>();
+}
+inline int64_t dynamic::asInt() const {
+  return asImpl<int64_t>();
+}
+inline bool dynamic::asBool() const {
+  return asImpl<bool>();
+}
 
-inline const fbstring& dynamic::getString() const& { return get<fbstring>(); }
+inline const std::string& dynamic::getString() const& {
+  return get<std::string>();
+}
 inline double          dynamic::getDouble() const& { return get<double>(); }
 inline int64_t         dynamic::getInt()    const& { return get<int64_t>(); }
 inline bool            dynamic::getBool()   const& { return get<bool>(); }
 
-inline fbstring& dynamic::getString() & { return get<fbstring>(); }
+inline std::string& dynamic::getString()& {
+  return get<std::string>();
+}
 inline double&   dynamic::getDouble() & { return get<double>(); }
 inline int64_t&  dynamic::getInt()    & { return get<int64_t>(); }
 inline bool&     dynamic::getBool()   & { return get<bool>(); }
 
-inline fbstring dynamic::getString() && { return std::move(get<fbstring>()); }
+inline std::string dynamic::getString()&& {
+  return std::move(get<std::string>());
+}
 inline double   dynamic::getDouble() && { return get<double>(); }
 inline int64_t  dynamic::getInt()    && { return get<int64_t>(); }
 inline bool     dynamic::getBool()   && { return get<bool>(); }
 
-inline const char* dynamic::data()  const& { return get<fbstring>().data();  }
-inline const char* dynamic::c_str() const& { return get<fbstring>().c_str(); }
-inline StringPiece dynamic::stringPiece() const { return get<fbstring>(); }
+inline const char* dynamic::data() const& {
+  return get<std::string>().data();
+}
+inline const char* dynamic::c_str() const& {
+  return get<std::string>().c_str();
+}
+inline StringPiece dynamic::stringPiece() const {
+  return get<std::string>();
+}
 
 template<class T>
 struct dynamic::CompareOp {
@@ -417,7 +468,7 @@ struct dynamic::CompareOp<dynamic::ObjectImpl> {
 
 inline dynamic& dynamic::operator+=(dynamic const& o) {
   if (type() == STRING && o.type() == STRING) {
-    *getAddress<fbstring>() += *o.getAddress<fbstring>();
+    *getAddress<std::string>() += *o.getAddress<std::string>();
     return *this;
   }
   *this = detail::numericOp<std::plus>(*this, o);
@@ -512,6 +563,41 @@ template<class K, class V> inline void dynamic::insert(K&& key, V&& val) {
   rv.first->second = std::forward<V>(val);
 }
 
+inline void dynamic::update(const dynamic& mergeObj) {
+  if (!isObject() || !mergeObj.isObject()) {
+    throw TypeError("object", type(), mergeObj.type());
+  }
+
+  for (const auto& pair : mergeObj.items()) {
+    (*this)[pair.first] = pair.second;
+  }
+}
+
+inline void dynamic::update_missing(const dynamic& mergeObj1) {
+  if (!isObject() || !mergeObj1.isObject()) {
+    throw TypeError("object", type(), mergeObj1.type());
+  }
+
+  // Only add if not already there
+  for (const auto& pair : mergeObj1.items()) {
+    if ((*this).find(pair.first) == (*this).items().end()) {
+      (*this)[pair.first] = pair.second;
+    }
+  }
+}
+
+inline dynamic dynamic::merge(
+    const dynamic& mergeObj1,
+    const dynamic& mergeObj2) {
+
+  // No checks on type needed here because they are done in update_missing
+  // Note that we do update_missing here instead of update() because
+  // it will prevent the extra writes that would occur with update()
+  auto ret = mergeObj2;
+  ret.update_missing(mergeObj1);
+  return ret;
+}
+
 inline std::size_t dynamic::erase(dynamic const& key) {
   auto& obj = get<ObjectImpl>();
   return obj.erase(key);
@@ -556,45 +642,47 @@ inline dynamic::const_item_iterator dynamic::erase(const_item_iterator first,
 }
 
 inline void dynamic::resize(std::size_t sz, dynamic const& c) {
-  auto& array = get<Array>();
-  array.resize(sz, c);
+  auto& arr = get<Array>();
+  arr.resize(sz, c);
 }
 
 inline void dynamic::push_back(dynamic const& v) {
-  auto& array = get<Array>();
-  array.push_back(v);
+  auto& arr = get<Array>();
+  arr.push_back(v);
 }
 
 inline void dynamic::push_back(dynamic&& v) {
-  auto& array = get<Array>();
-  array.push_back(std::move(v));
+  auto& arr = get<Array>();
+  arr.push_back(std::move(v));
 }
 
 inline void dynamic::pop_back() {
-  auto& array = get<Array>();
-  array.pop_back();
+  auto& arr = get<Array>();
+  arr.pop_back();
 }
 
 //////////////////////////////////////////////////////////////////////
 
-template<class T> struct dynamic::TypeInfo {
-  static char const name[];
-  static Type const type;
-};
+inline dynamic::dynamic(Array&& r, PrivateTag) : type_(ARRAY) {
+  new (&u_.array) Array(std::move(r));
+}
 
-#define FB_DEC_TYPE(T)                                      \
-  template<> char const dynamic::TypeInfo<T>::name[];       \
-  template<> dynamic::Type const dynamic::TypeInfo<T>::type
+#define FOLLY_DYNAMIC_DEC_TYPEINFO(T, str, val) \
+  template <> struct dynamic::TypeInfo<T> { \
+    static constexpr const char* name = str; \
+    static constexpr dynamic::Type type = val; \
+  }; \
+  //
 
-FB_DEC_TYPE(void*);
-FB_DEC_TYPE(bool);
-FB_DEC_TYPE(fbstring);
-FB_DEC_TYPE(dynamic::Array);
-FB_DEC_TYPE(double);
-FB_DEC_TYPE(int64_t);
-FB_DEC_TYPE(dynamic::ObjectImpl);
+FOLLY_DYNAMIC_DEC_TYPEINFO(void*,               "null",    dynamic::NULLT)
+FOLLY_DYNAMIC_DEC_TYPEINFO(bool,                "boolean", dynamic::BOOL)
+FOLLY_DYNAMIC_DEC_TYPEINFO(std::string, "string", dynamic::STRING)
+FOLLY_DYNAMIC_DEC_TYPEINFO(dynamic::Array,      "array",   dynamic::ARRAY)
+FOLLY_DYNAMIC_DEC_TYPEINFO(double,              "double",  dynamic::DOUBLE)
+FOLLY_DYNAMIC_DEC_TYPEINFO(int64_t,             "int64",   dynamic::INT64)
+FOLLY_DYNAMIC_DEC_TYPEINFO(dynamic::ObjectImpl, "object",  dynamic::OBJECT)
 
-#undef FB_DEC_TYPE
+#undef FOLLY_DYNAMIC_DEC_TYPEINFO
 
 template<class T>
 T dynamic::asImpl() const {
@@ -602,7 +690,8 @@ T dynamic::asImpl() const {
   case INT64:    return to<T>(*get_nothrow<int64_t>());
   case DOUBLE:   return to<T>(*get_nothrow<double>());
   case BOOL:     return to<T>(*get_nothrow<bool>());
-  case STRING:   return to<T>(*get_nothrow<fbstring>());
+  case STRING:
+    return to<T>(*get_nothrow<std::string>());
   default:
     throw TypeError("int/double/bool/string", type());
   }
@@ -650,8 +739,11 @@ template<> struct dynamic::GetAddrImpl<int64_t> {
 template<> struct dynamic::GetAddrImpl<double> {
   static double* get(Data& d) noexcept { return &d.doubl; }
 };
-template<> struct dynamic::GetAddrImpl<fbstring> {
-  static fbstring* get(Data& d) noexcept { return &d.string; }
+template <>
+struct dynamic::GetAddrImpl<std::string> {
+  static std::string* get(Data& d) noexcept {
+    return &d.string;
+  }
 };
 template<> struct dynamic::GetAddrImpl<dynamic::ObjectImpl> {
   static_assert(sizeof(ObjectImpl) <= sizeof(Data::objectBuffer),
@@ -690,6 +782,16 @@ struct dynamic::PrintImpl {
     out << t;
   }
 };
+// Otherwise, null, being (void*)0, would print as 0.
+template <>
+struct dynamic::PrintImpl<void*> {
+  static void print(dynamic const& /* d */,
+                    std::ostream& out,
+                    void* const& nul) {
+    DCHECK_EQ((void*)0, nul);
+    out << "null";
+  }
+};
 template<>
 struct dynamic::PrintImpl<dynamic::ObjectImpl> {
   static void print(dynamic const& d,
@@ -739,7 +841,7 @@ class FormatValue<dynamic> {
       FormatValue<int64_t>(val_.asInt()).format(arg, cb);
       break;
     case dynamic::STRING:
-      FormatValue<fbstring>(val_.asString()).format(arg, cb);
+      FormatValue<std::string>(val_.asString()).format(arg, cb);
       break;
     case dynamic::DOUBLE:
       FormatValue<double>(val_.asDouble()).format(arg, cb);
@@ -748,7 +850,7 @@ class FormatValue<dynamic> {
       FormatValue(val_.at(arg.splitIntKey())).format(arg, cb);
       break;
     case dynamic::OBJECT:
-      FormatValue(val_.at(arg.splitKey().toFbstring())).format(arg, cb);
+      FormatValue(val_.at(arg.splitKey().toString())).format(arg, cb);
       break;
     }
   }
@@ -805,5 +907,3 @@ class FormatValue<detail::DefaultValueWrapper<dynamic, V>> {
 }  // namespaces
 
 #undef FB_DYNAMIC_APPLY
-
-#endif