toDynamic(T)
authorTom Jackson <tjackson@fb.com>
Tue, 23 Apr 2013 17:02:07 +0000 (10:02 -0700)
committerSara Golemon <sgolemon@fb.com>
Mon, 1 Jul 2013 19:57:43 +0000 (12:57 -0700)
Summary: It only makes sense to be able to go the other direction, too.

Test Plan: Unit tests

Reviewed By: delong.j@fb.com

FB internal diff: D785282

folly/DynamicConverter.h
folly/test/DynamicConverterTest.cpp

index 3455f64d944022266fc71aaff753ee18eaf1db7b..2c3d2c1d9691b01a20fc3e704f78df885aec5723 100644 (file)
@@ -22,6 +22,7 @@
 #include "folly/dynamic.h"
 namespace folly {
   template <typename T> T convertTo(const dynamic&);
+  template <typename T> dynamic toDynamic(const T&);
 }
 
 /**
@@ -51,6 +52,7 @@ namespace dynamicconverter_detail {
 
 BOOST_MPL_HAS_XXX_TRAIT_DEF(value_type);
 BOOST_MPL_HAS_XXX_TRAIT_DEF(iterator);
+BOOST_MPL_HAS_XXX_TRAIT_DEF(mapped_type);
 
 template <typename T> struct class_is_container {
   typedef std::reverse_iterator<T*> some_iterator;
@@ -59,6 +61,12 @@ template <typename T> struct class_is_container {
               std::is_constructible<T, some_iterator, some_iterator>::value };
 };
 
+template <typename T> struct class_is_range {
+  enum { value = has_value_type<T>::value &&
+                 has_iterator<T>::value };
+};
+
+
 template <typename T> struct is_container
   : std::conditional<
       std::is_class<T>::value,
@@ -66,6 +74,19 @@ template <typename T> struct is_container
       std::false_type
     >::type {};
 
+template <typename T> struct is_range
+  : std::conditional<
+      std::is_class<T>::value,
+      class_is_range<T>,
+      std::false_type
+    >::type {};
+
+template <typename T> struct is_associative_container
+  : std::integral_constant<
+      bool,
+      is_range<T>::value && has_mapped_type<T>::value
+    > {};
+
 } // namespace dynamicconverter_detail
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -240,6 +261,52 @@ struct DynamicConverter<C,
       throw TypeError("object or array", d.type());
     }
   }
+
+};
+
+template <typename C, typename Enable = void>
+struct DynamicConstructor {
+  static dynamic construct(const C& x) {
+    return dynamic(x);
+  }
+};
+
+template<typename C>
+struct DynamicConstructor<C,
+    typename std::enable_if<
+      dynamicconverter_detail::is_associative_container<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;
+  }
+};
+
+template<typename C>
+struct DynamicConstructor<C,
+    typename std::enable_if<
+      !dynamicconverter_detail::is_associative_container<C>::value &&
+      !std::is_constructible<StringPiece, const C&>::value &&
+      dynamicconverter_detail::is_range<C>::value>::type> {
+  static dynamic construct(const C& x) {
+    dynamic d = {};
+    for (auto& item : x) {
+      d.push_back(toDynamic(item));
+    }
+    return d;
+  }
+};
+
+template<typename A, typename B>
+struct DynamicConstructor<std::pair<A, B>, void> {
+  static dynamic construct(const std::pair<A, B>& x) {
+    dynamic d = {};
+    d.push_back(toDynamic(x.first));
+    d.push_back(toDynamic(x.second));
+    return d;
+  }
 };
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -250,6 +317,11 @@ T convertTo(const dynamic& d) {
   return DynamicConverter<typename std::remove_cv<T>::type>::convert(d);
 }
 
+template<typename T>
+dynamic toDynamic(const T& x) {
+  return DynamicConstructor<typename std::remove_cv<T>::type>::construct(x);
+}
+
 } // namespace folly
 
 #endif // DYNAMIC_CONVERTER_H
index 83c95b18e2889b79305ba5fe544a56d5bfa3abad..c94a00c6712b5d46eeb334f56c39a1661d2f9403 100644 (file)
 #include "folly/DynamicConverter.h"
 
 #include <algorithm>
-#include <gtest/gtest.h>
 #include <gflags/gflags.h>
+#include <gtest/gtest.h>
+#include <map>
+#include <vector>
+
 #include "folly/Benchmark.h"
 
 using namespace folly;
@@ -267,6 +270,7 @@ struct Token {
   explicit Token(int kind, const fbstring& lexeme)
     : kind_(kind), lexeme_(lexeme) {}
 };
+
 namespace folly {
 template <> struct DynamicConverter<Token> {
   static Token convert(const dynamic& d) {
@@ -276,6 +280,7 @@ template <> struct DynamicConverter<Token> {
   }
 };
 }
+
 TEST(DynamicConverter, example) {
   dynamic d1 = dynamic::object("KIND", 2)("LEXEME", "a token");
   auto i1 = convertTo<Token>(d1);
@@ -283,6 +288,50 @@ TEST(DynamicConverter, example) {
   EXPECT_EQ(i1.lexeme_, "a token");
 }
 
+TEST(DynamicConverter, construct) {
+  using std::vector;
+  using std::map;
+  using std::pair;
+  using std::string;
+  {
+    vector<int> c { 1, 2, 3 };
+    dynamic d = { 1, 2, 3 };
+    EXPECT_EQ(d, toDynamic(c));
+  }
+
+  {
+    map<int, int> c { { 2, 4 }, { 3, 9 } };
+    dynamic d = dynamic::object(2, 4)(3, 9);
+    EXPECT_EQ(d, toDynamic(c));
+  }
+
+  {
+    map<string, string> c { { "a", "b" } };
+    dynamic d = dynamic::object("a", "b");
+    EXPECT_EQ(d, toDynamic(c));
+  }
+
+  {
+    map<string, pair<string, int>> c { { "a", { "b", 3 } } };
+    dynamic d = dynamic::object("a", dynamic { "b", 3 });
+    EXPECT_EQ(d, toDynamic(c));
+  }
+
+  {
+    map<string, pair<string, int>> c { { "a", { "b", 3 } } };
+    dynamic d = dynamic::object("a", dynamic { "b", 3 });
+    EXPECT_EQ(d, toDynamic(c));
+  }
+
+  {
+    vector<int> vi { 2, 3, 4, 5 };
+    auto c = std::make_pair(makeRange(vi.begin(), vi.begin() + 3),
+                            makeRange(vi.begin() + 1, vi.begin() + 4));
+    dynamic d = { { 2, 3, 4 }, { 3, 4, 5 } };
+    EXPECT_EQ(d, toDynamic(c));
+  }
+}
+
 int main(int argc, char ** argv) {
   testing::InitGoogleTest(&argc, argv);
   google::ParseCommandLineFlags(&argc, &argv, true);