//////////////////////////////////////////////////////////////////////
+struct dynamic::item_iterator : boost::iterator_adaptor<
+ dynamic::item_iterator,
+ dynamic::ObjectImpl::iterator> {
+ /* implicit */ item_iterator(base_type b) : iterator_adaptor_(b) {}
+
+ using object_type = dynamic::ObjectImpl;
+
+ private:
+ friend class boost::iterator_core_access;
+};
+
+struct dynamic::value_iterator : boost::iterator_adaptor<
+ dynamic::value_iterator,
+ dynamic::ObjectImpl::iterator,
+ dynamic> {
+ /* implicit */ value_iterator(base_type b) : iterator_adaptor_(b) {}
+
+ using object_type = dynamic::ObjectImpl;
+
+ private:
+ dynamic& dereference() const {
+ return base_reference()->second;
+ }
+ friend class boost::iterator_core_access;
+};
+
struct dynamic::const_item_iterator
: boost::iterator_adaptor<dynamic::const_item_iterator,
dynamic::ObjectImpl::const_iterator> {
/* implicit */ const_item_iterator(base_type b) : iterator_adaptor_(b) { }
+ /* implicit */ const_item_iterator(item_iterator i)
+ : iterator_adaptor_(i.base()) {}
+ /* implicit */ const_item_iterator(dynamic::ObjectImpl::iterator i)
+ : iterator_adaptor_(i) {}
+
+ using object_type = dynamic::ObjectImpl const;
private:
friend class boost::iterator_core_access;
dynamic const> {
/* implicit */ const_key_iterator(base_type b) : iterator_adaptor_(b) { }
+ using object_type = dynamic::ObjectImpl const;
+
private:
dynamic const& dereference() const {
return base_reference()->first;
dynamic::ObjectImpl::const_iterator,
dynamic const> {
/* implicit */ const_value_iterator(base_type b) : iterator_adaptor_(b) { }
+ /* implicit */ const_value_iterator(value_iterator i)
+ : iterator_adaptor_(i.base()) {}
+ /* implicit */ const_value_iterator(dynamic::ObjectImpl::iterator i)
+ : iterator_adaptor_(i) {}
+
+ using object_type = dynamic::ObjectImpl const;
private:
dynamic const& dereference() const {
return get<Array>().end();
}
+inline dynamic::iterator dynamic::begin() {
+ return get<Array>().begin();
+}
+inline dynamic::iterator dynamic::end() {
+ return get<Array>().end();
+}
+
template <class It>
struct dynamic::IterableProxy {
- typedef It const_iterator;
+ typedef It iterator;
typedef typename It::value_type value_type;
+ typedef typename It::object_type object_type;
- /* implicit */ IterableProxy(const dynamic::ObjectImpl* o) : o_(o) { }
+ /* implicit */ IterableProxy(object_type* o) : o_(o) {}
It begin() const {
return o_->begin();
}
private:
- const dynamic::ObjectImpl* o_;
+ object_type* o_;
};
inline dynamic::IterableProxy<dynamic::const_key_iterator> dynamic::keys()
return &(get<ObjectImpl>());
}
+inline dynamic::IterableProxy<dynamic::value_iterator> dynamic::values() {
+ return &(get<ObjectImpl>());
+}
+
+inline dynamic::IterableProxy<dynamic::item_iterator> dynamic::items() {
+ return &(get<ObjectImpl>());
+}
+
inline bool dynamic::isString() const {
return get_nothrow<std::string>() != nullptr;
}
inline dynamic::const_item_iterator dynamic::find(dynamic const& key) const {
return get<ObjectImpl>().find(key);
}
+inline dynamic::item_iterator dynamic::find(dynamic const& key) {
+ return get<ObjectImpl>().find(key);
+}
template<class K, class V> inline void dynamic::insert(K&& key, V&& val) {
auto& obj = get<ObjectImpl>();
return obj.erase(key);
}
-inline dynamic::const_iterator dynamic::erase(const_iterator it) {
+inline dynamic::iterator dynamic::erase(const_iterator it) {
auto& arr = get<Array>();
// std::vector doesn't have an erase method that works on const iterators,
// even though the standard says it should, so this hack converts to a
return const_key_iterator(get<ObjectImpl>().erase(it.base()));
}
-inline dynamic::const_key_iterator dynamic::erase(const_key_iterator first,
- const_key_iterator last) {
+inline dynamic::const_key_iterator dynamic::erase(
+ const_key_iterator first,
+ const_key_iterator last) {
return const_key_iterator(get<ObjectImpl>().erase(first.base(),
last.base()));
}
-inline dynamic::const_value_iterator dynamic::erase(const_value_iterator it) {
- return const_value_iterator(get<ObjectImpl>().erase(it.base()));
+inline dynamic::value_iterator dynamic::erase(const_value_iterator it) {
+ return value_iterator(get<ObjectImpl>().erase(it.base()));
}
-inline dynamic::const_value_iterator dynamic::erase(const_value_iterator first,
- const_value_iterator last) {
- return const_value_iterator(get<ObjectImpl>().erase(first.base(),
- last.base()));
+inline dynamic::value_iterator dynamic::erase(
+ const_value_iterator first,
+ const_value_iterator last) {
+ return value_iterator(get<ObjectImpl>().erase(first.base(), last.base()));
}
-inline dynamic::const_item_iterator dynamic::erase(const_item_iterator it) {
- return const_item_iterator(get<ObjectImpl>().erase(it.base()));
+inline dynamic::item_iterator dynamic::erase(const_item_iterator it) {
+ return item_iterator(get<ObjectImpl>().erase(it.base()));
}
-inline dynamic::const_item_iterator dynamic::erase(const_item_iterator first,
- const_item_iterator last) {
- return const_item_iterator(get<ObjectImpl>().erase(first.base(),
- last.base()));
+inline dynamic::item_iterator dynamic::erase(
+ const_item_iterator first,
+ const_item_iterator last) {
+ return item_iterator(get<ObjectImpl>().erase(first.base(), last.base()));
}
inline void dynamic::resize(std::size_t sz, dynamic const& c) {
throw TypeError("array/object", type());
}
-dynamic::const_iterator
-dynamic::erase(const_iterator first, const_iterator last) {
+dynamic::iterator dynamic::erase(const_iterator first, const_iterator last) {
auto& arr = get<Array>();
return get<Array>().erase(
arr.begin() + (first - arr.begin()),
private:
typedef std::vector<dynamic> Array;
public:
+ typedef Array::iterator iterator;
typedef Array::const_iterator const_iterator;
typedef dynamic value_type;
+
struct const_key_iterator;
struct const_value_iterator;
struct const_item_iterator;
+ struct value_iterator;
+ struct item_iterator;
+
/*
* Creation routines for making dynamic objects and arrays. Objects
* are maps from key to value (so named due to json-related origins
*/
const_iterator begin() const;
const_iterator end() const;
+ iterator begin();
+ iterator end();
private:
/*
IterableProxy<const_key_iterator> keys() const;
IterableProxy<const_value_iterator> values() const;
IterableProxy<const_item_iterator> items() const;
+ IterableProxy<value_iterator> values();
+ IterableProxy<item_iterator> items();
/*
* AssociativeContainer-style find interface for objects. Throws if
* const_item_iterator pointing to the item.
*/
const_item_iterator find(dynamic const&) const;
+ item_iterator find(dynamic const&);
/*
* If this is an object, returns whether it contains a field with
* removed, or end() if there are none. (The iteration order does
* not change.)
*/
- const_iterator erase(const_iterator it);
- const_iterator erase(const_iterator first, const_iterator last);
+ iterator erase(const_iterator it);
+ iterator erase(const_iterator first, const_iterator last);
const_key_iterator erase(const_key_iterator it);
const_key_iterator erase(const_key_iterator first, const_key_iterator last);
- const_value_iterator erase(const_value_iterator it);
- const_value_iterator erase(const_value_iterator first,
- const_value_iterator last);
+ value_iterator erase(const_value_iterator it);
+ value_iterator erase(const_value_iterator first, const_value_iterator last);
- const_item_iterator erase(const_item_iterator it);
- const_item_iterator erase(const_item_iterator first,
- const_item_iterator last);
+ item_iterator erase(const_item_iterator it);
+ item_iterator erase(const_item_iterator first, const_item_iterator last);
/*
* Append elements to an array. If this is not an array, throws
* TypeError.
ss << folly::dynamic(nullptr);
EXPECT_EQ("null", ss.str());
}
+
+TEST(Dynamic, WriteThroughArrayIterators) {
+ dynamic const cint(0);
+ dynamic d = dynamic::array(cint, cint, cint);
+ size_t size = d.size();
+
+ for (auto& val : d) {
+ EXPECT_EQ(val, cint);
+ }
+ EXPECT_EQ(d.size(), size);
+
+ dynamic ds(make_long_string());
+ for (auto& val : d) {
+ val = ds; // assign through reference
+ }
+
+ ds = "short string";
+ dynamic ds2(make_long_string());
+
+ for (auto& val : d) {
+ EXPECT_EQ(val, ds2);
+ }
+ EXPECT_EQ(d.size(), size);
+}
+
+TEST(Dynamic, MoveOutOfArrayIterators) {
+ dynamic ds(make_long_string());
+ dynamic d = dynamic::array(ds, ds, ds);
+ size_t size = d.size();
+
+ for (auto& val : d) {
+ EXPECT_EQ(val, ds);
+ }
+ EXPECT_EQ(d.size(), size);
+
+ for (auto& val : d) {
+ dynamic waste = std::move(val); // force moving out
+ EXPECT_EQ(waste, ds);
+ }
+
+ for (auto& val : d) {
+ EXPECT_NE(val, ds);
+ }
+ EXPECT_EQ(d.size(), size);
+}
+
+TEST(Dynamic, WriteThroughObjectIterators) {
+ dynamic const cint(0);
+ dynamic d = dynamic::object("key1", cint)("key2", cint);
+ size_t size = d.size();
+
+ for (auto& val : d.items()) {
+ EXPECT_EQ(val.second, cint);
+ }
+ EXPECT_EQ(d.size(), size);
+
+ dynamic ds(make_long_string());
+ for (auto& val : d.items()) {
+ val.second = ds; // assign through reference
+ }
+
+ ds = "short string";
+ dynamic ds2(make_long_string());
+ for (auto& val : d.items()) {
+ EXPECT_EQ(val.second, ds2);
+ }
+ EXPECT_EQ(d.size(), size);
+}
+
+TEST(Dynamic, MoveOutOfObjectIterators) {
+ dynamic ds(make_long_string());
+ dynamic d = dynamic::object("key1", ds)("key2", ds);
+ size_t size = d.size();
+
+ for (auto& val : d.items()) {
+ EXPECT_EQ(val.second, ds);
+ }
+ EXPECT_EQ(d.size(), size);
+
+ for (auto& val : d.items()) {
+ dynamic waste = std::move(val.second); // force moving out
+ EXPECT_EQ(waste, ds);
+ }
+
+ for (auto& val : d.items()) {
+ EXPECT_NE(val.second, ds);
+ }
+ EXPECT_EQ(d.size(), size);
+}
+
+TEST(Dynamic, ArrayIteratorInterop) {
+ dynamic d = dynamic::array(0, 1, 2);
+ dynamic const& cdref = d;
+
+ auto it = d.begin();
+ auto cit = cdref.begin();
+
+ EXPECT_EQ(it, cit);
+ EXPECT_EQ(cit, d.begin());
+ EXPECT_EQ(it, cdref.begin());
+
+ // Erase using non-const iterator
+ it = d.erase(it);
+ cit = cdref.begin();
+ EXPECT_EQ(*it, 1);
+ EXPECT_EQ(cit, it);
+
+ // Assign from non-const to const, preserve equality
+ decltype(cit) cit2 = it;
+ EXPECT_EQ(cit, cit2);
+}
+
+TEST(Dynamic, ObjectIteratorInterop) {
+ dynamic ds = make_long_string();
+ dynamic d = dynamic::object(0, ds)(1, ds)(2, ds);
+ dynamic const& cdref = d;
+
+ auto it = d.find(0);
+ auto cit = cdref.find(0);
+ EXPECT_NE(it, cdref.items().end());
+ EXPECT_NE(cit, cdref.items().end());
+ EXPECT_EQ(it, cit);
+
+ ++cit;
+ // Erase using non-const iterator
+ auto it2 = d.erase(it);
+ EXPECT_EQ(cit, it2);
+
+ // Assign from non-const to const, preserve equality
+ decltype(cit) cit2 = it2;
+ EXPECT_EQ(cit, cit2);
+}