Use std::nullptr_t in dynamic
[folly.git] / folly / DynamicConverter.h
index 275d6d936e60a46ab3d9cff51ecdb5b67d881395..9ab4f73ac324b3befc8d2395136ac4e308b08a57 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright 2012 Facebook, Inc.
+ * Copyright 2017 Facebook, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
 
 // @author Nicholas Ormrod <njormrod@fb.com>
 
-#ifndef DYNAMIC_CONVERTER_H
-#define DYNAMIC_CONVERTER_H
+#pragma once
 
-#include "folly/dynamic.h"
+#include <folly/dynamic.h>
 namespace folly {
   template <typename T> T convertTo(const dynamic&);
+  template <typename T> dynamic toDynamic(const T&);
 }
 
 /**
@@ -29,7 +29,9 @@ namespace folly {
  *
  * Example:
  *
- *   dynamic d = { { 1, 2, 3 }, { 4, 5 } }; // a vector of vector of int
+ *   dynamic d = dynamic::array(
+ *       dynamic::array(1, 2, 3),
+ *       dynamic::array(4, 5)); // a vector of vector of int
  *   auto vvi = convertTo<fbvector<fbvector<int>>>(d);
  *
  * See docs/DynamicConverter.md for supported types and customization
@@ -37,9 +39,10 @@ namespace folly {
 
 
 #include <type_traits>
+#include <iterator>
 #include <boost/iterator/iterator_adaptor.hpp>
 #include <boost/mpl/has_xxx.hpp>
-#include "folly/Likely.h"
+#include <folly/Likely.h>
 
 namespace folly {
 
@@ -49,28 +52,28 @@ namespace folly {
 namespace dynamicconverter_detail {
 
 BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type);
-BOOST_MPL_HAS_XXX_TRAIT_DEF(key_type);
-BOOST_MPL_HAS_XXX_TRAIT_DEF(mapped_type);
 BOOST_MPL_HAS_XXX_TRAIT_DEF(iterator);
+BOOST_MPL_HAS_XXX_TRAIT_DEF(mapped_type);
 
-template <typename T> struct map_container_has_correct_types
-  : std::is_same<std::pair<typename std::add_const<typename T::key_type>::type,
-                           typename T::mapped_type>,
-                 typename T::value_type> {};
+template <typename T> struct iterator_class_is_container {
+  typedef std::reverse_iterator<typename T::iterator> some_iterator;
+  enum { value = has_value_type<T>::value &&
+              std::is_constructible<T, some_iterator, some_iterator>::value };
+};
 
-template <typename T> struct class_is_container {
-  struct dummy {};
+template <typename T>
+using class_is_container = typename
+  std::conditional<
+    has_iterator<T>::value,
+    iterator_class_is_container<T>,
+    std::false_type
+  >::type;
+
+template <typename T> struct class_is_range {
   enum { value = has_value_type<T>::value &&
-                 has_iterator<T>::value &&
-                 std::is_constructible<T, dummy, dummy>::value };
+                 has_iterator<T>::value };
 };
 
-template <typename T> struct container_is_map
-  : std::conditional<
-      has_key_type<T>::value && has_mapped_type<T>::value,
-      map_container_has_correct_types<T>,
-      std::false_type
-    >::type {};
 
 template <typename T> struct is_container
   : std::conditional<
@@ -79,13 +82,19 @@ template <typename T> struct is_container
       std::false_type
     >::type {};
 
-template <typename T> struct is_map_container
+template <typename T> struct is_range
   : std::conditional<
-      is_container<T>::value,
-      container_is_map<T>,
+      std::is_class<T>::value,
+      class_is_range<T>,
       std::false_type
     >::type {};
 
+template <typename T> struct is_map
+  : std::integral_constant<
+      bool,
+      is_range<T>::value && has_mapped_type<T>::value
+    > {};
+
 } // namespace dynamicconverter_detail
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -105,16 +114,33 @@ template <typename T> struct is_map_container
 
 namespace dynamicconverter_detail {
 
-template <typename F, typename S>
-inline void
-derefToCache(std::pair<F, S>* mem, const dynamic::const_item_iterator& it) {
-  new (mem) std::pair<F, S>(convertTo<F>(it->first), convertTo<S>(it->second));
-}
+template<typename T>
+struct Dereferencer {
+  static inline void derefToCache(
+      T* /* mem */, const dynamic::const_item_iterator& /* it */) {
+    throw TypeError("array", dynamic::Type::OBJECT);
+  }
 
-template <typename T>
-inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
-  new (mem) T(convertTo<T>(*it));
-}
+  static inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
+    new (mem) T(convertTo<T>(*it));
+  }
+};
+
+template<typename F, typename S>
+struct Dereferencer<std::pair<F, S>> {
+  static inline void
+  derefToCache(std::pair<F, S>* mem, const dynamic::const_item_iterator& it) {
+    new (mem) std::pair<F, S>(
+        convertTo<F>(it->first), convertTo<S>(it->second)
+    );
+  }
+
+  // Intentional duplication of the code in Dereferencer
+  template <typename T>
+  static inline void derefToCache(T* mem, const dynamic::const_iterator& it) {
+    new (mem) T(convertTo<T>(*it));
+  }
+};
 
 template <typename T, typename It>
 class Transformer : public boost::iterator_adaptor<
@@ -137,7 +163,7 @@ class Transformer : public boost::iterator_adaptor<
   ttype& dereference() const {
     if (LIKELY(!valid_)) {
       cache_.~ttype();
-      derefToCache(&cache_, this->base_reference());
+      Dereferencer<ttype>::derefToCache(&cache_, this->base_reference());
       valid_ = true;
     }
     return cache_;
@@ -150,7 +176,7 @@ public:
 
 // conversion factory
 template <typename T, typename It>
-static inline std::move_iterator<Transformer<T, It>>
+inline std::move_iterator<Transformer<T, It>>
 conversionIterator(const It& it) {
   return std::make_move_iterator(Transformer<T, It>(it));
 }
@@ -160,13 +186,14 @@ conversionIterator(const It& it) {
 ///////////////////////////////////////////////////////////////////////////////
 // DynamicConverter specializations
 
-template <typename T, typename Enable = void> struct DynamicConverter;
-
 /**
  * Each specialization of DynamicConverter has the function
- *     'static T convert(const dynamic& d);'
+ *     'static T convert(const dynamic&);'
  */
 
+// default - intentionally unimplemented
+template <typename T, typename Enable = void> struct DynamicConverter;
+
 // boolean
 template <>
 struct DynamicConverter<bool> {
@@ -181,7 +208,17 @@ struct DynamicConverter<T,
     typename std::enable_if<std::is_integral<T>::value &&
                             !std::is_same<T, bool>::value>::type> {
   static T convert(const dynamic& d) {
-    return static_cast<T>(d.asInt());
+    return folly::to<T>(d.asInt());
+  }
+};
+
+// enums
+template <typename T>
+struct DynamicConverter<T,
+                        typename std::enable_if<std::is_enum<T>::value>::type> {
+  static T convert(const dynamic& d) {
+    using type = typename std::underlying_type<T>::type;
+    return static_cast<T>(DynamicConverter<type>::convert(d));
   }
 };
 
@@ -190,7 +227,7 @@ template <typename T>
 struct DynamicConverter<T,
     typename std::enable_if<std::is_floating_point<T>::value>::type> {
   static T convert(const dynamic& d) {
-    return static_cast<T>(d.asDouble());
+    return folly::to<T>(d.asDouble());
   }
 };
 
@@ -206,7 +243,7 @@ struct DynamicConverter<folly::fbstring> {
 template <>
 struct DynamicConverter<std::string> {
   static std::string convert(const dynamic& d) {
-    return d.asString().toStdString();
+    return d.asString();
   }
 };
 
@@ -225,53 +262,108 @@ struct DynamicConverter<std::pair<F,S>> {
   }
 };
 
-// map containers
+// containers
 template <typename C>
 struct DynamicConverter<C,
     typename std::enable_if<
-      dynamicconverter_detail::is_map_container<C>::value>::type> {
+      dynamicconverter_detail::is_container<C>::value>::type> {
   static C convert(const dynamic& d) {
-    if (LIKELY(d.isObject())) {
+    if (d.isArray()) {
+      return C(dynamicconverter_detail::conversionIterator<C>(d.begin()),
+               dynamicconverter_detail::conversionIterator<C>(d.end()));
+    } else if (d.isObject()) {
       return C(dynamicconverter_detail::conversionIterator<C>
                  (d.items().begin()),
                dynamicconverter_detail::conversionIterator<C>
                  (d.items().end()));
-    } else if (d.isArray()) {
-      return C(dynamicconverter_detail::conversionIterator<C>(d.begin()),
-               dynamicconverter_detail::conversionIterator<C>(d.end()));
     } else {
       throw TypeError("object or array", d.type());
     }
   }
 };
 
-// non-map containers
+///////////////////////////////////////////////////////////////////////////////
+// DynamicConstructor specializations
+
+/**
+ * Each specialization of DynamicConstructor has the function
+ *     'static dynamic construct(const C&);'
+ */
+
+// default
+template <typename C, typename Enable = void>
+struct DynamicConstructor {
+  static dynamic construct(const C& x) {
+    return dynamic(x);
+  }
+};
+
+// identity
 template <typename C>
-struct DynamicConverter<C,
-      typename std::enable_if<
-          dynamicconverter_detail::is_container<C>::value &&
-          !dynamicconverter_detail::is_map_container<C>::value
-        >::type
-    > {
-  static C convert(const dynamic& d) {
-    if (LIKELY(d.isArray())) {
-      return C(dynamicconverter_detail::conversionIterator<C>(d.begin()),
-               dynamicconverter_detail::conversionIterator<C>(d.end()));
-    } else {
-      throw TypeError("array", d.type());
+struct DynamicConstructor<
+    C,
+    typename std::enable_if<std::is_same<C, dynamic>::value>::type> {
+  static dynamic construct(const C& x) {
+    return x;
+  }
+};
+
+// maps
+template <typename C>
+struct DynamicConstructor<
+    C,
+    typename std::enable_if<
+        !std::is_same<C, dynamic>::value &&
+        dynamicconverter_detail::is_map<C>::value>::type> {
+  static dynamic construct(const C& x) {
+    dynamic d = dynamic::object;
+    for (auto& pair : x) {
+      d.insert(toDynamic(pair.first), toDynamic(pair.second));
     }
+    return d;
+  }
+};
+
+// other ranges
+template <typename C>
+struct DynamicConstructor<
+    C,
+    typename std::enable_if<
+        !std::is_same<C, dynamic>::value &&
+        !dynamicconverter_detail::is_map<C>::value &&
+        !std::is_constructible<StringPiece, const C&>::value &&
+        dynamicconverter_detail::is_range<C>::value>::type> {
+  static dynamic construct(const C& x) {
+    dynamic d = dynamic::array;
+    for (auto& item : x) {
+      d.push_back(toDynamic(item));
+    }
+    return d;
+  }
+};
+
+// pair
+template<typename A, typename B>
+struct DynamicConstructor<std::pair<A, B>, void> {
+  static dynamic construct(const std::pair<A, B>& x) {
+    dynamic d = dynamic::array;
+    d.push_back(toDynamic(x.first));
+    d.push_back(toDynamic(x.second));
+    return d;
   }
 };
 
 ///////////////////////////////////////////////////////////////////////////////
-// convertTo implementation
+// implementation
 
 template <typename T>
 T convertTo(const dynamic& d) {
-  return DynamicConverter<T>::convert(d);
+  return DynamicConverter<typename std::remove_cv<T>::type>::convert(d);
 }
 
-} // namespace folly
-
-#endif // DYNAMIC_CONVERTER_H
+template<typename T>
+dynamic toDynamic(const T& x) {
+  return DynamicConstructor<typename std::remove_cv<T>::type>::construct(x);
+}
 
+} // namespace folly