Add converter for containers of pairs
authorChristopher Berner <cberner@fb.com>
Fri, 7 Dec 2012 21:38:28 +0000 (13:38 -0800)
committerJordan DeLong <jdelong@fb.com>
Sun, 16 Dec 2012 22:49:30 +0000 (14:49 -0800)
Summary:
Add specialized dynamic converter for containers of pairs,
which can convert from a list of pairs, or from a object

Test Plan: added a unit test

Reviewed By: njormrod@fb.com

FB internal diff: D650730

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

index b36a56cd8a1c9e1e1b90f4ab11723c4e90cf0bf3..7068d88ea0e81918090df6e64ea92f65d0be0a30 100644 (file)
@@ -50,15 +50,8 @@ 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);
 
-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 class_is_container {
   typedef std::reverse_iterator<T*> some_iterator;
   enum { value = has_value_type<T>::value &&
@@ -66,13 +59,6 @@ template <typename T> struct class_is_container {
               std::is_constructible<T, some_iterator, some_iterator>::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<
       std::is_class<T>::value,
@@ -80,13 +66,6 @@ template <typename T> struct is_container
       std::false_type
     >::type {};
 
-template <typename T> struct is_map_container
-  : std::conditional<
-      is_container<T>::value,
-      container_is_map<T>,
-      std::false_type
-    >::type {};
-
 } // namespace dynamicconverter_detail
 
 ///////////////////////////////////////////////////////////////////////////////
@@ -106,16 +85,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<
@@ -138,7 +134,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_;
@@ -226,44 +222,26 @@ 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
-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());
-    }
-  }
-};
-
 ///////////////////////////////////////////////////////////////////////////////
 // convertTo implementation
 
index 0dd06671598e059a73783f4652f9a7ae2dfe1ec2..47e87dc078ed6de254306f17836cdd3482523676 100644 (file)
@@ -41,19 +41,6 @@ TEST(DynamicConverter, template_metaprogramming) {
   EXPECT_EQ(c1t, true);
   EXPECT_EQ(c2t, true);
   EXPECT_EQ(c3t, true);
-
-  bool m1f = is_map_container<int>::value;
-  bool m2f = is_map_container<std::vector<int>>::value;
-  bool m3f = is_map_container<std::set<int>>::value;
-
-  bool m1t = is_map_container<std::map<int, int>>::value;
-  bool m2t = is_map_container<std::unordered_map<int, int>>::value;
-
-  EXPECT_EQ(m1f, false);
-  EXPECT_EQ(m2f, false);
-  EXPECT_EQ(m3f, false);
-  EXPECT_EQ(m1t, true);
-  EXPECT_EQ(m2t, true);
 }
 
 TEST(DynamicConverter, arithmetic_types) {
@@ -153,6 +140,13 @@ TEST(DynamicConverter, map_keyed_by_string) {
   EXPECT_EQ(i2, i2b);
 }
 
+TEST(DynamicConverter, map_to_vector_of_pairs) {
+  dynamic d1 = dynamic::object("1", "one")("2", "two");
+  auto i1 = convertTo<std::vector<std::pair<std::string, std::string>>>(d1);
+  decltype(i1) i1b = { { "1", "one" }, { "2", "two" } };
+  EXPECT_EQ(i1, i1b);
+}
+
 TEST(DynamicConverter, nested_containers) {
   dynamic d1 = { { 1 }, { }, { 2, 3 } };
   auto i1 = convertTo<folly::fbvector<std::vector<uint8_t>>>(d1);