dynamic::get_ptr
authorTom Jackson <tjackson@fb.com>
Fri, 27 Sep 2013 00:12:38 +0000 (17:12 -0700)
committerPeter Griess <pgriess@fb.com>
Tue, 15 Oct 2013 01:43:54 +0000 (18:43 -0700)
Summary: For testing containment and using the value in one operation.

Test Plan: Unit tests

Reviewed By: tudorb@fb.com

FB internal diff: D986986

folly/dynamic-inl.h
folly/dynamic.h
folly/test/DynamicTest.cpp

index bae776a246f52499b26eea2e7d67d04a35f6b3cb..1893f05142f98511b927907e71c481ef5e62ec83 100644 (file)
@@ -525,32 +525,53 @@ template<class K, class V> inline dynamic& dynamic::setDefault(K&& k, V&& v) {
                                    std::forward<V>(v))).first->second;
 }
 
-inline dynamic const& dynamic::at(dynamic const& idx) const {
-  return const_cast<dynamic*>(this)->at(idx);
+inline dynamic* dynamic::get_ptr(dynamic const& idx) {
+  return const_cast<dynamic*>(const_cast<dynamic const*>(this)->get_ptr(idx));
 }
 
-inline dynamic& dynamic::at(dynamic const& idx) {
-  if (!isObject() && !isArray()) {
+inline const dynamic* dynamic::get_ptr(dynamic const& idx) const {
+  if (auto* parray = get_nothrow<Array>()) {
+    if (!idx.isInt()) {
+      throw TypeError("int64", idx.type());
+    }
+    if (idx >= parray->size()) {
+      return nullptr;
+    }
+    return &(*parray)[idx.asInt()];
+  } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
+    auto it = pobject->find(idx);
+    if (it == pobject->end()) {
+      return nullptr;
+    }
+    return &it->second;
+  } else {
     throw TypeError("object/array", type());
   }
+}
 
+inline dynamic& dynamic::at(dynamic const& idx) {
+  return const_cast<dynamic&>(const_cast<dynamic const*>(this)->at(idx));
+}
+
+inline dynamic const& dynamic::at(dynamic const& idx) const {
   if (auto* parray = get_nothrow<Array>()) {
-    if (idx >= parray->size()) {
-      throw std::out_of_range("out of range in dynamic array");
-    }
     if (!idx.isInt()) {
       throw TypeError("int64", idx.type());
     }
+    if (idx >= parray->size()) {
+      throw std::out_of_range("out of range in dynamic array");
+    }
     return (*parray)[idx.asInt()];
+  } else if (auto* pobject = get_nothrow<ObjectImpl>()) {
+    auto it = pobject->find(idx);
+    if (it == pobject->end()) {
+      throw std::out_of_range(to<std::string>(
+          "couldn't find key ", idx.asString(), " in dynamic object"));
+    }
+    return it->second;
+  } else {
+    throw TypeError("object/array", type());
   }
-
-  assert(get_nothrow<ObjectImpl>());
-  auto it = find(idx);
-  if (it == items().end()) {
-    throw std::out_of_range(to<std::string>(
-        "couldn't find key ", idx.asString(), " in dynamic object"));
-  }
-  return const_cast<dynamic&>(it->second);
 }
 
 inline bool dynamic::empty() const {
index 3a1193f3a170cf61ddd9299a106669395081d25f..41680543c045c9705e70907365aef28db1a230fd 100644 (file)
@@ -316,6 +316,7 @@ public:
    */
   const_item_iterator find(dynamic const&) const;
 
+
   /*
    * If this is an object, returns whether it contains a field with
    * the given name.  Otherwise throws TypeError.
@@ -333,6 +334,20 @@ public:
   dynamic const& at(dynamic const&) const;
   dynamic&       at(dynamic const&);
 
+  /*
+   * Like 'at', above, except it returns either a pointer to the contained
+   * object or nullptr if it wasn't found. This allows a key to be tested for
+   * containment and retrieved in one operation. Example:
+   *
+   *   if (auto* found = d.get_ptr(key))
+   *     // use *found;
+   *
+   * Using these with dynamic objects that are not arrays or objects
+   * will throw a TypeError.
+   */
+  const dynamic* get_ptr(dynamic const&) const;
+  dynamic* get_ptr(dynamic const&);
+
   /*
    * This works for access to both objects and arrays.
    *
index f97ad5f9b9abfc4587d61bf3d5e8d187d3ae972e..163c6d35739df36021990206e97bba346ad32146 100644 (file)
  * limitations under the License.
  */
 
-#include "folly/dynamic.h"
-#include "folly/json.h"
-#include <gtest/gtest.h>
-#include <gflags/gflags.h>
 #include <boost/next_prior.hpp>
+#include <gflags/gflags.h>
+#include <gtest/gtest.h>
+
 #include "folly/Benchmark.h"
+#include "folly/dynamic.h"
+#include "folly/json.h"
 
 using folly::dynamic;
 
@@ -271,6 +272,24 @@ TEST(Dynamic, ObjectForwarding) {
                               ("key", "value1");
 }
 
+TEST(Dynamic, GetPtr) {
+  dynamic array = { 1, 2, "three" };
+  EXPECT_TRUE(array.get_ptr(0));
+  EXPECT_FALSE(array.get_ptr(3));
+  EXPECT_EQ(dynamic("three"), *array.get_ptr(2));
+  const dynamic& carray = array;
+  EXPECT_EQ(dynamic("three"), *carray.get_ptr(2));
+
+  dynamic object = dynamic::object("one", 1)("two", 2);
+  EXPECT_TRUE(object.get_ptr("one"));
+  EXPECT_FALSE(object.get_ptr("three"));
+  EXPECT_EQ(dynamic(2), *object.get_ptr("two"));
+  *object.get_ptr("one") = 11;
+  EXPECT_EQ(dynamic(11), *object.get_ptr("one"));
+  const dynamic& cobject = object;
+  EXPECT_EQ(dynamic(2), *cobject.get_ptr("two"));
+}
+
 int main(int argc, char** argv) {
   testing::InitGoogleTest(&argc, argv);
   google::ParseCommandLineFlags(&argc, &argv, true);