From 59309b5dd2bdfc405d2d9b6f842d702cb8db8b26 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sat, 13 Feb 2016 19:06:03 +0800 Subject: [PATCH] Add GenericArray helper class with range-based for --- include/rapidjson/document.h | 73 ++++++++++++++++++++++++++- include/rapidjson/rapidjson.h | 11 ++++ test/unittest/valuetest.cpp | 95 +++++++++++++++++++++++++++-------- 3 files changed, 157 insertions(+), 22 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 194c8c6..59eb59f 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -483,6 +483,9 @@ struct TypeHelper > { } // namespace internal +template +class GenericArray; + /////////////////////////////////////////////////////////////////////////////// // GenericValue @@ -510,6 +513,7 @@ public: typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. typedef GenericValue ValueType; //!< Value type of itself. + typedef GenericArray ArrayType; //!@name Constructors and destructor. //@{ @@ -1556,6 +1560,9 @@ public: return pos; } + ArrayType GetArray() { RAPIDJSON_ASSERT(IsArray()); return ArrayType(*this); } + const ArrayType GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ArrayType(*this); } + //@} //!@name Number @@ -1653,9 +1660,12 @@ public: //@} + //!@name Array + //@{ + //! Templated version for checking whether this value is type T. /*! - \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c std::basic_string + \tparam T Either \c bool, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c float, \c const \c char*, \c std::basic_string */ template bool Is() const { return internal::TypeHelper::Is(*this); } @@ -1669,6 +1679,8 @@ public: template ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper::Set(*this, data, allocator); } + //@} + //! Generate events of this value to a Handler. /*! This function adopts the GoF visitor pattern. Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. @@ -2278,6 +2290,65 @@ GenericValue::GenericValue(const GenericValue +class GenericArray { +public: + typedef typename ValueType::ValueIterator ValueIterator; + typedef typename ValueType::ConstValueIterator ConstValueIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + + template + friend class GenericValue; + + GenericArray() : ptr_() {} + GenericArray(const GenericArray& rhs) : ptr_(rhs.ptr_) {} + GenericArray& operator=(GenericArray& rhs) { ptr_ = rhs.ptr_; return *this; } + ~GenericArray() {} + + SizeType Size() const { return ptr_->Size(); } + SizeType Capacity() const { return ptr_->Capacity(); } + bool Empty() const { return ptr_->Empty(); } + void Clear() { ptr_->Clear(); } + ValueType& operator[](SizeType index) { return (*ptr_)[index]; } + const ValueType& operator[](SizeType index) const { return (*ptr_)[index]; } + ValueIterator Begin() { return ptr_->Begin(); } + ValueIterator End() { return ptr_->End(); } + ConstValueIterator Begin() const { return ptr_->Begin(); } + ConstValueIterator End() const { return ptr_->End(); } + GenericArray& Reserve(SizeType newCapacity, AllocatorType &allocator) { ptr_->Reserve(newCapacity, allocator); return *this; } + GenericArray& PushBack(ValueType& value, AllocatorType& allocator) { ptr_->PushBack(value, allocator); return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray& PushBack(ValueType&& value, AllocatorType& allocator) { ptr_->PushBack(value, allocator); return *this; } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericArray& PushBack(StringRefType value, AllocatorType& allocator) { ptr_->PushBack(value, allocator); return *this; } + template + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericArray&)) + PushBack(T value, AllocatorType& allocator) { ptr_->PushBack(value, allocator); return *this; } + GenericArray& PopBack() { ptr_->PopBack(); return *this; } + ValueIterator Erase(ConstValueIterator pos) { return ptr_->Erase(pos); } + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { return ptr_->Erase(first, last); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + ValueIterator begin() { return ptr_->Begin(); } + ValueIterator end() { return ptr_->End(); } + ConstValueIterator begin() const { return ptr_->Begin(); } + ConstValueIterator end() const { return ptr_->End(); } +#endif + +private: + GenericArray(ValueType& value) : ptr_(&value) {} + GenericArray(const ValueType& value) : ptr_(const_cast(&value)) {} + ValueType* ptr_; +}; + +typedef GenericArray Array; + RAPIDJSON_NAMESPACE_END #ifdef _MSC_VER diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index 9cb40a9..d5480ec 100644 --- a/include/rapidjson/rapidjson.h +++ b/include/rapidjson/rapidjson.h @@ -530,6 +530,17 @@ RAPIDJSON_NAMESPACE_END #define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 #endif +#ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR +#if defined(__clang) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR __has_feature(cxx_range_for) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \ + (defined(_MSC_VER) && _MSC_VER >= 1600) +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 1 +#else +#define RAPIDJSON_HAS_CXX11_RANGE_FOR 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RANGE_FOR + //!@endcond /////////////////////////////////////////////////////////////////////////////// diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index a1912b3..4f2d9df 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -824,25 +824,9 @@ TEST(Value, SetStringNullException) { EXPECT_THROW(v.SetString(0, 0), AssertException); } -TEST(Value, Array) { - Value x(kArrayType); - const Value& y = x; - Value::AllocatorType allocator; - - EXPECT_EQ(kArrayType, x.GetType()); - EXPECT_TRUE(x.IsArray()); - EXPECT_TRUE(x.Empty()); - EXPECT_EQ(0u, x.Size()); - EXPECT_TRUE(y.IsArray()); - EXPECT_TRUE(y.Empty()); - EXPECT_EQ(0u, y.Size()); - - EXPECT_FALSE(x.IsNull()); - EXPECT_FALSE(x.IsBool()); - EXPECT_FALSE(x.IsFalse()); - EXPECT_FALSE(x.IsTrue()); - EXPECT_FALSE(x.IsString()); - EXPECT_FALSE(x.IsObject()); +template +void TestArray(T& x, Allocator& allocator) { + const T& y = x; // PushBack() Value v; @@ -889,7 +873,7 @@ TEST(Value, Array) { #endif // iterator - Value::ValueIterator itr = x.Begin(); + typename T::ValueIterator itr = x.Begin(); EXPECT_TRUE(itr != x.End()); EXPECT_TRUE(itr->IsNull()); ++itr; @@ -908,7 +892,7 @@ TEST(Value, Array) { EXPECT_STREQ("foo", itr->GetString()); // const iterator - Value::ConstValueIterator citr = y.Begin(); + typename T::ConstValueIterator citr = y.Begin(); EXPECT_TRUE(citr != y.End()); EXPECT_TRUE(citr->IsNull()); ++citr; @@ -994,6 +978,29 @@ TEST(Value, Array) { EXPECT_EQ(i + removeCount, x[static_cast(i)][0].GetUint()); } } +} + +TEST(Value, Array) { + Value x(kArrayType); + const Value& y = x; + Value::AllocatorType allocator; + + EXPECT_EQ(kArrayType, x.GetType()); + EXPECT_TRUE(x.IsArray()); + EXPECT_TRUE(x.Empty()); + EXPECT_EQ(0u, x.Size()); + EXPECT_TRUE(y.IsArray()); + EXPECT_TRUE(y.Empty()); + EXPECT_EQ(0u, y.Size()); + + EXPECT_FALSE(x.IsNull()); + EXPECT_FALSE(x.IsBool()); + EXPECT_FALSE(x.IsFalse()); + EXPECT_FALSE(x.IsTrue()); + EXPECT_FALSE(x.IsString()); + EXPECT_FALSE(x.IsObject()); + + TestArray(x, allocator); // Working in gcc without C++11, but VS2013 cannot compile. To be diagnosed. // http://en.wikipedia.org/wiki/Erase-remove_idiom @@ -1017,6 +1024,52 @@ TEST(Value, Array) { EXPECT_TRUE(z.Empty()); } +TEST(Value, ArrayHelper) { + Value::AllocatorType allocator; + { + Value x(kArrayType); + Array a = x.GetArray(); + TestArray(a, allocator); + } + + Value x(kArrayType); + Array a = x.GetArray(); + a.PushBack(1, allocator); + + Array a2(a); // copy constructor + EXPECT_EQ(1, a2.Size()); + + Array a3; // default constructor + a3 = a; // assignment operator + EXPECT_EQ(1, a3.Size()); +} + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR +TEST(Value, ArrayHelperRangeFor) { + Value::AllocatorType allocator; + Value x(kArrayType); + + for (int i = 0; i < 10; i++) + x.PushBack(i, allocator); + + { + int i = 0; + for (auto& v : x.GetArray()) + EXPECT_EQ(i++, v.GetInt()); + EXPECT_EQ(i, 10); + } + { + int i = 0; + for (auto& v : const_cast(x).GetArray()) + EXPECT_EQ(i++, v.GetInt()); + EXPECT_EQ(i, 10); + } + + // Array a = x.GetArray(); + // Array ca = const_cast(x).GetArray(); +} +#endif + TEST(Value, Object) { Value x(kObjectType); const Value& y = x; // const version