Detect identity conversion in toDynamic
authorAndrew Krieger <andrew.krieger@oculus.com>
Fri, 10 Feb 2017 00:54:55 +0000 (16:54 -0800)
committerFacebook Github Bot <facebook-github-bot@users.noreply.github.com>
Fri, 10 Feb 2017 01:04:44 +0000 (17:04 -0800)
Summary:
D4499520 added typedefs to `dynamic` which made it start matching
tests for ranges/containers. However, the typedefs are unconditional on the
actual contents of the `dynamic`. This made toDynamic(dynamic) select the
range-based conversion operator, always, which immediately asserts when
trying to do range-based iteration over an Object or a primitive. Add tests
to the converters that enable/disable depending on whether the object is
already a `dynamic` and early-out in that case.

Reviewed By: mzlee

Differential Revision: D4538617

fbshipit-source-id: f3a5aafab07946a221dcead782fc27de51afa0a6

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

index 19477b28fba9afa725045a4bf8327764bd836df2..9ab4f73ac324b3befc8d2395136ac4e308b08a57 100644 (file)
@@ -298,11 +298,23 @@ struct DynamicConstructor {
   }
 };
 
+// identity
+template <typename C>
+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,
+template <typename C>
+struct DynamicConstructor<
+    C,
     typename std::enable_if<
-      dynamicconverter_detail::is_map<C>::value>::type> {
+        !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) {
@@ -313,12 +325,14 @@ struct DynamicConstructor<C,
 };
 
 // other ranges
-template<typename C>
-struct DynamicConstructor<C,
+template <typename C>
+struct DynamicConstructor<
+    C,
     typename std::enable_if<
-      !dynamicconverter_detail::is_map<C>::value &&
-      !std::is_constructible<StringPiece, const C&>::value &&
-      dynamicconverter_detail::is_range<C>::value>::type> {
+        !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) {
index 1029a11c454ad5f4056db231021853b82a78932e..47165031465f338495fde5348080b5fb3fcda381 100644 (file)
@@ -396,3 +396,16 @@ TEST(DynamicConverter, errors) {
   dynamic d2 = floatOver;
   EXPECT_THROW(convertTo<float>(d2), std::range_error);
 }
+
+TEST(DynamicConverter, partial_dynamics) {
+  std::vector<dynamic> c{
+      dynamic::array(2, 3, 4), dynamic::array(3, 4, 5),
+  };
+  dynamic d = dynamic::array(dynamic::array(2, 3, 4), dynamic::array(3, 4, 5));
+  EXPECT_EQ(d, toDynamic(c));
+
+  std::unordered_map<std::string, dynamic> m{{"one", 1}, {"two", 2}};
+
+  dynamic md = dynamic::object("one", 1)("two", 2);
+  EXPECT_EQ(md, toDynamic(m));
+}