From 4d648fdcd8bd32826e12151c5eba29c3417cde89 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 12 Feb 2016 18:23:32 +0800 Subject: [PATCH 01/20] Add templated accessors --- include/rapidjson/document.h | 98 ++++++++++++++++++++++++++++++++++++ test/unittest/valuetest.cpp | 56 +++++++++++++++++++-- 2 files changed, 151 insertions(+), 3 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index b0a5d34..d0f4bea 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -393,6 +393,87 @@ template struct IsGenericValue : IsGenericValueImpl::Type {}; } // namespace internal +/////////////////////////////////////////////////////////////////////////////// +// TypeHelper + +namespace internal { + +template +struct TypeHelper { + static bool Is(const ValueType&) { RAPIDJSON_ASSERT(false && "Unsupport type"); } + static T Get(const ValueType&) { RAPIDJSON_ASSERT(false && "Unsupport type"); } + static ValueType& Set(ValueType&, T) { RAPIDJSON_ASSERT(false && "Unsupport type"); } + static ValueType& Set(ValueType&, T, typename ValueType::AllocatorType&) { RAPIDJSON_ASSERT(false && "Unsupport type"); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsBool(); } + static bool Get(const ValueType& v) { return v.GetBool(); } + static ValueType& Set(ValueType& v, bool data) { return v.SetBool(data); } + static ValueType& Set(ValueType& v, bool data, typename ValueType::AllocatorType&) { return v.SetBool(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt(); } + static int Get(const ValueType& v) { return v.GetInt(); } + static ValueType& Set(ValueType& v, int data) { return v.SetInt(data); } + static ValueType& Set(ValueType& v, int data, typename ValueType::AllocatorType&) { return v.SetInt(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint(); } + static unsigned Get(const ValueType& v) { return v.GetUint(); } + static ValueType& Set(ValueType& v, unsigned data) { return v.SetUint(data); } + static ValueType& Set(ValueType& v, unsigned data, typename ValueType::AllocatorType&) { return v.SetUint(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsInt64(); } + static int64_t Get(const ValueType& v) { return v.GetInt64(); } + static ValueType& Set(ValueType& v, int64_t data) { return v.SetInt64(data); } + static ValueType& Set(ValueType& v, int64_t data, typename ValueType::AllocatorType&) { return v.SetInt64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsUint64(); } + static uint64_t Get(const ValueType& v) { return v.GetUint64(); } + static ValueType& Set(ValueType& v, uint64_t data) { return v.SetUint64(data); } + static ValueType& Set(ValueType& v, uint64_t data, typename ValueType::AllocatorType&) { return v.SetUint64(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsDouble(); } + static double Get(const ValueType& v) { return v.GetDouble(); } + static ValueType& Set(ValueType& v, double data) { return v.SetDouble(data); } + static ValueType& Set(ValueType& v, double data, typename ValueType::AllocatorType&) { return v.SetDouble(data); } +}; + +template +struct TypeHelper { + static bool Is(const ValueType& v) { return v.IsFloat(); } + static float Get(const ValueType& v) { return v.GetFloat(); } + static ValueType& Set(ValueType& v, float data) { return v.SetFloat(data); } + static ValueType& Set(ValueType& v, float data, typename ValueType::AllocatorType&) { return v.SetFloat(data); } +}; + +#if RAPIDJSON_HAS_STDSTRING +template +struct TypeHelper > { + typedef std::basic_string StringType; + static bool Is(const ValueType& v) { return v.IsString(); } + static StringType Get(const ValueType& v) { return v.GetString(); } + static ValueType& Set(ValueType& v, const StringType& data, typename ValueType::AllocatorType& a) { return v.SetString(data, a); } +}; +#endif + +} // namespace internal + /////////////////////////////////////////////////////////////////////////////// // GenericValue @@ -1484,6 +1565,7 @@ public: /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessFloat() to check whether the converison is lossless. */ double GetFloat() const { + RAPIDJSON_ASSERT(IsFloat()); return static_cast(GetDouble()); } @@ -1554,6 +1636,22 @@ public: //@} + //! 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 + */ + template + bool Is() const { return internal::TypeHelper::Is(*this); } + + template + T Get() const { return internal::TypeHelper::Get(*this); } + + template + ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } + + 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. diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 4179ccb..7d1bb9e 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -335,6 +335,12 @@ TEST(Value, True) { Value z; z.SetBool(true); EXPECT_TRUE(z.IsTrue()); + + // Templated functions + EXPECT_TRUE(z.Is()); + EXPECT_TRUE(z.Get()); + EXPECT_FALSE(z.Set(false).Get()); + EXPECT_TRUE(z.Set(true).Get()); } TEST(Value, False) { @@ -414,6 +420,12 @@ TEST(Value, Int) { // operator=(int) z = 5678; EXPECT_EQ(5678, z.GetInt()); + + // Templated functions + EXPECT_TRUE(z.Is()); + EXPECT_EQ(5678, z.Get()); + EXPECT_EQ(5679, z.Set(5679).Get()); + EXPECT_EQ(5680, z.Set(5680).Get()); } TEST(Value, Uint) { @@ -453,6 +465,12 @@ TEST(Value, Uint) { EXPECT_EQ(2147483648u, z.GetUint()); EXPECT_FALSE(z.IsInt()); EXPECT_TRUE(z.IsInt64()); // Issue 41: Incorrect parsing of unsigned int number types + + // Templated functions + EXPECT_TRUE(z.Is()); + EXPECT_EQ(2147483648u, z.Get()); + EXPECT_EQ(2147483649u, z.Set(2147483649u).Get()); + EXPECT_EQ(2147483650u, z.Set(2147483650u).Get()); } TEST(Value, Int64) { @@ -505,8 +523,15 @@ TEST(Value, Int64) { EXPECT_FALSE(z.IsInt()); EXPECT_NEAR(-2147483649.0, z.GetDouble(), 0.0); - z.SetInt64(static_cast(RAPIDJSON_UINT64_C2(0x80000000, 00000000))); + int64_t i = static_cast(RAPIDJSON_UINT64_C2(0x80000000, 00000000)); + z.SetInt64(i); EXPECT_DOUBLE_EQ(-9223372036854775808.0, z.GetDouble()); + + // Templated functions + EXPECT_TRUE(z.Is()); + EXPECT_EQ(i, z.Get()); + EXPECT_EQ(i - 1, z.Set(i - 1).Get()); + EXPECT_EQ(i - 2, z.Set(i - 2).Get()); } TEST(Value, Uint64) { @@ -547,10 +572,17 @@ TEST(Value, Uint64) { EXPECT_FALSE(z.IsUint()); EXPECT_TRUE(z.IsInt64()); - z.SetUint64(RAPIDJSON_UINT64_C2(0x80000000, 0x00000000)); // 2^63 cannot cast as int64 + uint64_t u = RAPIDJSON_UINT64_C2(0x80000000, 0x00000000); + z.SetUint64(u); // 2^63 cannot cast as int64 EXPECT_FALSE(z.IsInt64()); - EXPECT_EQ(RAPIDJSON_UINT64_C2(0x80000000, 0x00000000), z.GetUint64()); // Issue 48 + EXPECT_EQ(u, z.GetUint64()); // Issue 48 EXPECT_DOUBLE_EQ(9223372036854775808.0, z.GetDouble()); + + // Templated functions + EXPECT_TRUE(z.Is()); + EXPECT_EQ(u, z.Get()); + EXPECT_EQ(u + 1, z.Set(u + 1).Get()); + EXPECT_EQ(u + 2, z.Set(u + 2).Get()); } TEST(Value, Double) { @@ -577,6 +609,12 @@ TEST(Value, Double) { z = 56.78; EXPECT_NEAR(56.78, z.GetDouble(), 0.0); + + // Templated functions + EXPECT_TRUE(z.Is()); + EXPECT_EQ(56.78, z.Get()); + EXPECT_EQ(57.78, z.Set(57.78).Get()); + EXPECT_EQ(58.78, z.Set(58.78).Get()); } TEST(Value, Float) { @@ -604,6 +642,12 @@ TEST(Value, Float) { z = 56.78f; EXPECT_NEAR(56.78f, z.GetFloat(), 0.0f); + + // Templated functions + EXPECT_TRUE(z.Is()); + EXPECT_EQ(56.78f, z.Get()); + EXPECT_EQ(57.78f, z.Set(57.78f).Get()); + EXPECT_EQ(58.78f, z.Set(58.78f).Get()); } TEST(Value, IsLosslessDouble) { @@ -759,6 +803,12 @@ TEST(Value, String) { vs1 = StringRef(str); TestEqual(str, vs1); TestEqual(vs0, vs1); + + // Templated function. + EXPECT_TRUE(vs0.Is()); + EXPECT_EQ(str, vs0.Get()); + EXPECT_EQ(std::string("Apple"), vs0.Set(std::string("Apple"), allocator).template Get()); + EXPECT_EQ(std::string("Orange"), vs0.Set(std::string("Orange"), allocator).template Get()); } #endif // RAPIDJSON_HAS_STDSTRING } From 59309b5dd2bdfc405d2d9b6f842d702cb8db8b26 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sat, 13 Feb 2016 19:06:03 +0800 Subject: [PATCH 02/20] 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 From 0b098eb38d57c35741a62883340cf2b2af7e4634 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sat, 13 Feb 2016 22:09:14 +0800 Subject: [PATCH 03/20] Rectify constness of Array --- include/rapidjson/document.h | 54 ++++++++++++++++++++++++++---------- test/unittest/valuetest.cpp | 38 +++++++++++++++++++------ 2 files changed, 68 insertions(+), 24 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 59eb59f..1339c5b 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -481,9 +481,25 @@ struct TypeHelper > { }; #endif +template +struct TypeHelper { + typedef typename ValueType::Array ArratType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArratType Get(ValueType& v) { return v.GetArray(); } + static ValueType& Set(ValueType& v, ArratType data) { return v.SetArray(data); } + static ValueType& Set(ValueType& v, ArratType data, typename ValueType::AllocatorType&) { return v.SetArray(data); } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstArray ArratType; + static bool Is(const ValueType& v) { return v.IsArray(); } + static ArratType Get(const ValueType& v) { return v.GetArray(); } +}; + } // namespace internal -template +template class GenericArray; /////////////////////////////////////////////////////////////////////////////// @@ -513,7 +529,8 @@ 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; + typedef GenericArray Array; + typedef GenericArray ConstArray; //!@name Constructors and destructor. //@{ @@ -1114,7 +1131,7 @@ public: RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(name.IsString()); - Object& o = data_.o; + ObjectData& o = data_.o; if (o.size >= o.capacity) { if (o.capacity == 0) { o.capacity = kDefaultObjectCapacity; @@ -1392,7 +1409,10 @@ public: //! Set this value as an empty array. /*! \post IsArray == true */ - GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } + + //! Set this value with an array. + GenericValue& SetArray(Array& a) { return *this = *a.ptr_; } //! Get the number of elements in array. SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } @@ -1560,8 +1580,8 @@ public: return pos; } - ArrayType GetArray() { RAPIDJSON_ASSERT(IsArray()); return ArrayType(*this); } - const ArrayType GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ArrayType(*this); } + Array GetArray() { RAPIDJSON_ASSERT(IsArray()); return Array(*this); } + ConstArray GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ConstArray(*this); } //@} @@ -1673,6 +1693,9 @@ public: template T Get() const { return internal::TypeHelper::Get(*this); } + template + T Get() { return internal::TypeHelper::Get(*this); } + template ValueType& Set(const T& data) { return internal::TypeHelper::Set(*this, data); } @@ -1815,13 +1838,13 @@ private: double d; }; // 8 bytes - struct Object { + struct ObjectData { Member* members; SizeType size; SizeType capacity; }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode - struct Array { + struct ArrayData { GenericValue* elements; SizeType size; SizeType capacity; @@ -1831,8 +1854,8 @@ private: String s; ShortString ss; Number n; - Object o; - Array a; + ObjectData o; + ArrayData a; }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode // Initialize this value as array with initial data, without calling destructor. @@ -2295,9 +2318,13 @@ GenericValue::GenericValue(const GenericValue +template class GenericArray { public: + typedef GenericArray ConstArray; + typedef GenericArray Array; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; typedef typename ValueType::ValueIterator ValueIterator; typedef typename ValueType::ConstValueIterator ConstValueIterator; typedef typename ValueType::AllocatorType AllocatorType; @@ -2308,7 +2335,7 @@ public: GenericArray() : ptr_() {} GenericArray(const GenericArray& rhs) : ptr_(rhs.ptr_) {} - GenericArray& operator=(GenericArray& rhs) { ptr_ = rhs.ptr_; return *this; } + GenericArray& operator=(const GenericArray& rhs) { ptr_ = rhs.ptr_; return *this; } ~GenericArray() {} SizeType Size() const { return ptr_->Size(); } @@ -2343,12 +2370,9 @@ public: 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/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 4f2d9df..9af1e1c 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -1028,20 +1028,40 @@ TEST(Value, ArrayHelper) { Value::AllocatorType allocator; { Value x(kArrayType); - Array a = x.GetArray(); + Value::Array a = x.GetArray(); TestArray(a, allocator); } - Value x(kArrayType); - Array a = x.GetArray(); - a.PushBack(1, allocator); + { + Value x(kArrayType); + Value::Array a = x.GetArray(); + a.PushBack(1, allocator); - Array a2(a); // copy constructor - EXPECT_EQ(1, a2.Size()); + Value::Array a2(a); // copy constructor + EXPECT_EQ(1, a2.Size()); - Array a3; // default constructor - a3 = a; // assignment operator - EXPECT_EQ(1, a3.Size()); + Value::Array a3; // default constructor + a3 = a; // assignment operator + EXPECT_EQ(1, a3.Size()); + + Value::ConstArray y = static_cast(x).GetArray(); + (void)y; + // y.PushBack(1, allocator); // should not compile + + // Templated functions + x.Clear(); + EXPECT_TRUE(x.Is()); + EXPECT_TRUE(x.Is()); + a.PushBack(1, allocator); + a = x.Get(); + EXPECT_EQ(1, a[0].GetInt()); + EXPECT_EQ(1, x.Get()[0].GetInt()); + + Value x2; + x2.Set(a); + EXPECT_TRUE(x.IsNull()); + EXPECT_EQ(1, x2.Get()[0].GetInt()); + } } #if RAPIDJSON_HAS_CXX11_RANGE_FOR From 2a78b4da8c1f80d1845ed23ec9abeffa546f38fa Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sat, 13 Feb 2016 22:11:12 +0800 Subject: [PATCH 04/20] Fix Value.String test compilation error --- test/unittest/valuetest.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 9af1e1c..a8b6c57 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -812,8 +812,10 @@ TEST(Value, String) { // Templated function. EXPECT_TRUE(vs0.Is()); EXPECT_EQ(str, vs0.Get()); - EXPECT_EQ(std::string("Apple"), vs0.Set(std::string("Apple"), allocator).template Get()); - EXPECT_EQ(std::string("Orange"), vs0.Set(std::string("Orange"), allocator).template Get()); + vs0.Set(std::string("Apple"), allocator); + EXPECT_EQ(std::string("Apple"), vs0.Get()); + vs0.Set(std::string("Orange"), allocator); + EXPECT_EQ(std::string("Orange"), vs0.Get()); } #endif // RAPIDJSON_HAS_STDSTRING } From 960324a95b65315afdcf736806e8f0666f70cd3f Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sat, 13 Feb 2016 23:07:26 +0800 Subject: [PATCH 05/20] Try to fix gcc compilation error --- test/unittest/valuetest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index a8b6c57..7077fe5 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -1082,7 +1082,7 @@ TEST(Value, ArrayHelperRangeFor) { } { int i = 0; - for (auto& v : const_cast(x).GetArray()) + for (const auto& v : const_cast(x).GetArray()) EXPECT_EQ(i++, v.GetInt()); EXPECT_EQ(i, 10); } From effc8a8f304bc8239c8a29e1a8988a2567277c72 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 14 Feb 2016 00:12:04 +0800 Subject: [PATCH 06/20] Let constness of GenericArray::ValueItaertor depending on ValueType --- include/rapidjson/document.h | 39 ++++++++++++++++-------------------- 1 file changed, 17 insertions(+), 22 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 1339c5b..1ce2e4f 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -2325,8 +2325,8 @@ public: typedef GenericArray Array; typedef ValueT PlainType; typedef typename internal::MaybeAddConst::Type ValueType; - typedef typename ValueType::ValueIterator ValueIterator; - typedef typename ValueType::ConstValueIterator ConstValueIterator; + typedef ValueType* ValueIterator; // This may be const or non-const iterator + typedef const ValueType* ConstValueIterator; typedef typename ValueType::AllocatorType AllocatorType; typedef typename ValueType::StringRefType StringRefType; @@ -2341,31 +2341,26 @@ public: 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; } + void Clear() const { ptr_->Clear(); } + ValueType& operator[](SizeType index) const { return (*ptr_)[index]; } + ValueIterator Begin() const { return ptr_->Begin(); } + ValueIterator End() const { return ptr_->End(); } + const GenericArray& Reserve(SizeType newCapacity, AllocatorType &allocator) const { ptr_->Reserve(newCapacity, allocator); return *this; } + const GenericArray& PushBack(ValueType& value, AllocatorType& allocator) const { ptr_->PushBack(value, allocator); return *this; } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericArray& PushBack(ValueType&& value, AllocatorType& allocator) { ptr_->PushBack(value, allocator); return *this; } + const GenericArray& PushBack(ValueType&& value, AllocatorType& allocator) const { ptr_->PushBack(value, allocator); return *this; } #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericArray& PushBack(StringRefType value, AllocatorType& allocator) { ptr_->PushBack(value, allocator); return *this; } + const GenericArray& PushBack(StringRefType value, AllocatorType& allocator) const { 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); } + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) + PushBack(T value, AllocatorType& allocator) const { ptr_->PushBack(value, allocator); return *this; } + const GenericArray& PopBack() const { ptr_->PopBack(); return *this; } + ValueIterator Erase(ConstValueIterator pos) const { return ptr_->Erase(pos); } + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { 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(); } + ValueIterator begin() const { return ptr_->Begin(); } + ValueIterator end() const { return ptr_->End(); } #endif private: From 923db0e6413a5589a67fdc7f5b2e694f09e6f16c Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 14 Feb 2016 06:14:12 +0800 Subject: [PATCH 07/20] Fix gcc compilation error --- include/rapidjson/document.h | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 1ce2e4f..bd47e0c 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -399,12 +399,7 @@ template struct IsGenericValue : IsGenericValueImpl::Type {}; namespace internal { template -struct TypeHelper { - static bool Is(const ValueType&) { RAPIDJSON_ASSERT(false && "Unsupport type"); } - static T Get(const ValueType&) { RAPIDJSON_ASSERT(false && "Unsupport type"); return T(); } - static ValueType& Set(ValueType&, T) { RAPIDJSON_ASSERT(false && "Unsupport type"); } - static ValueType& Set(ValueType&, T, typename ValueType::AllocatorType&) { RAPIDJSON_ASSERT(false && "Unsupport type"); } -}; +struct TypeHelper {}; template struct TypeHelper { From 1634395378d886c4461e78117ecb715e1619d968 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 14 Feb 2016 13:49:52 +0800 Subject: [PATCH 08/20] Add object helper --- include/rapidjson/document.h | 121 ++++++++++++++++++++++++++++++++-- include/rapidjson/rapidjson.h | 2 +- test/unittest/valuetest.cpp | 116 +++++++++++++++++++++++++++----- 3 files changed, 217 insertions(+), 22 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index bd47e0c..4370409 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -492,10 +492,27 @@ struct TypeHelper { static ArratType Get(const ValueType& v) { return v.GetArray(); } }; +template +struct TypeHelper { + typedef typename ValueType::Object ArratType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ArratType Get(ValueType& v) { return v.GetObject(); } + static ValueType& Set(ValueType& v, ArratType data) { return v.SetObject(data); } + static ValueType& Set(ValueType& v, ArratType data, typename ValueType::AllocatorType&) { return v.SetObject(data); } +}; + +template +struct TypeHelper { + typedef typename ValueType::ConstObject ArratType; + static bool Is(const ValueType& v) { return v.IsObject(); } + static ArratType Get(const ValueType& v) { return v.GetObject(); } +}; + } // namespace internal -template -class GenericArray; +// Forward declarations +template class GenericArray; +template class GenericObject; /////////////////////////////////////////////////////////////////////////////// // GenericValue @@ -526,6 +543,8 @@ public: typedef GenericValue ValueType; //!< Value type of itself. typedef GenericArray Array; typedef GenericArray ConstArray; + typedef GenericObject Object; + typedef GenericObject ConstObject; //!@name Constructors and destructor. //@{ @@ -951,6 +970,9 @@ public: /*! \post IsObject() == true */ GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } + //! Set this value with an object. + GenericValue& SetObject(Object& o) { return *this = *o.ptr_; } + //! Get the number of members in the object. SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } @@ -1397,6 +1419,9 @@ public: return false; } + Object GetObject() { RAPIDJSON_ASSERT(IsObject()); return Object(*this); } + ConstObject GetObject() const { RAPIDJSON_ASSERT(IsObject()); return ConstObject(*this); } + //@} //!@name Array @@ -2321,7 +2346,7 @@ public: typedef ValueT PlainType; typedef typename internal::MaybeAddConst::Type ValueType; typedef ValueType* ValueIterator; // This may be const or non-const iterator - typedef const ValueType* ConstValueIterator; + typedef const ValueT* ConstValueIterator; typedef typename ValueType::AllocatorType AllocatorType; typedef typename ValueType::StringRefType StringRefType; @@ -2346,9 +2371,7 @@ public: const GenericArray& PushBack(ValueType&& value, AllocatorType& allocator) const { ptr_->PushBack(value, allocator); return *this; } #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS const GenericArray& PushBack(StringRefType value, AllocatorType& allocator) const { ptr_->PushBack(value, allocator); return *this; } - template - RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) - PushBack(T value, AllocatorType& allocator) const { ptr_->PushBack(value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { ptr_->PushBack(value, allocator); return *this; } const GenericArray& PopBack() const { ptr_->PopBack(); return *this; } ValueIterator Erase(ConstValueIterator pos) const { return ptr_->Erase(pos); } ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return ptr_->Erase(first, last); } @@ -2363,6 +2386,92 @@ private: ValueType* ptr_; }; +//! Helper class for accessing Value of array type. +/*! + Instance of this helper class is obtained by \c GenericValue::GetArray(). + In addition to all APIs for array type, it provides range-based for loop if \c RAPIDJSON_HAS_CXX11_RANGE_FOR=1. +*/ +template +class GenericObject { +public: + typedef GenericObject ConstObject; + typedef GenericObject Object; + typedef ValueT PlainType; + typedef typename internal::MaybeAddConst::Type ValueType; + typedef GenericMemberIterator MemberIterator; // This may be const or non-const iterator + typedef GenericMemberIterator ConstMemberIterator; + typedef typename ValueType::AllocatorType AllocatorType; + typedef typename ValueType::StringRefType StringRefType; + typedef typename ValueType::EncodingType EncodingType; + typedef typename ValueType::Ch Ch; + + template + friend class GenericValue; + + GenericObject() : ptr_() {} + GenericObject(const GenericObject& rhs) : ptr_(rhs.ptr_) {} + GenericObject& operator=(const GenericObject& rhs) { ptr_ = rhs.ptr_; return *this; } + ~GenericObject() {} + + SizeType MemberCount() const { return ptr_->MemberCount(); } + bool ObjectEmpty() const { return ptr_->ObjectEmpty(); } + ValueType& operator[](Ch* name) const { return (*ptr_)[name]; } + template ValueType& operator[](const GenericValue& name) const { return (*ptr_)[name]; } +#if RAPIDJSON_HAS_STDSTRING + ValueType& operator[](const std::basic_string& name) const { return (*ptr_)[name]; } +#endif + MemberIterator MemberBegin() const { return ptr_->MemberBegin(); } + MemberIterator MemberEnd() const { return ptr_->MemberEnd(); } + bool HasMember(const Ch* name) const { return ptr_->HasMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool HasMember(const std::basic_string& name) const { return ptr_->HasMember(name); } +#endif + template bool HasMember(const GenericValue& name) const { return ptr_->HasMember(name); } + MemberIterator FindMember(const Ch* name) const { ptr_->FindMember(name); } + template MemberIterator FindMember(const GenericValue& name) const { ptr_->FindMember(name); } +#if RAPIDJSON_HAS_STDSTRING + MemberIterator FindMember(const std::basic_string& name) const { return ptr_->FindMember(name); } +#endif + ValueType& AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } + ValueType& AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } +#if RAPIDJSON_HAS_STDSTRING + ValueType& AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } +#endif + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + ValueType& AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } + ValueType& AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } + ValueType& AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } + ValueType& AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + ValueType& AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } + ValueType& AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } + void RemoveAllMembers() { return ptr_->RemoveAllMembers(); } + bool RemoveMember(const Ch* name) const { return ptr_->RemoveMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool RemoveMember(const std::basic_string& name) const { return ptr_->RemoveMember(name); } +#endif + template bool RemoveMember(const GenericValue& name) const { return ptr_->RemoveMember(name); } + MemberIterator RemoveMember(MemberIterator m) const { return ptr_->RemoveMember(m); } + MemberIterator EraseMember(ConstMemberIterator pos) const { return ptr_->EraseMember(pos); } + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return ptr_->EraseMember(first, last); } + bool EraseMember(const Ch* name) const { ptr_->EraseMember(name); } +#if RAPIDJSON_HAS_STDSTRING + bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } +#endif + template bool EraseMember(const GenericValue& name) const { ptr_->EraseMember(name); } + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR + MemberIterator begin() const { return ptr_->MemberBegin(); } + MemberIterator end() const { return ptr_->MemberEnd(); } +#endif + +private: + GenericObject(ValueType& value) : ptr_(&value) {} + ValueType* ptr_; +}; + RAPIDJSON_NAMESPACE_END #ifdef _MSC_VER diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index d5480ec..d0bbd10 100644 --- a/include/rapidjson/rapidjson.h +++ b/include/rapidjson/rapidjson.h @@ -531,7 +531,7 @@ RAPIDJSON_NAMESPACE_END #endif #ifndef RAPIDJSON_HAS_CXX11_RANGE_FOR -#if defined(__clang) +#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) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 7077fe5..50eb206 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -827,7 +827,7 @@ TEST(Value, SetStringNullException) { } template -void TestArray(T& x, Allocator& allocator) { +static void TestArray(T& x, Allocator& allocator) { const T& y = x; // PushBack() @@ -1092,19 +1092,9 @@ TEST(Value, ArrayHelperRangeFor) { } #endif -TEST(Value, Object) { - Value x(kObjectType); - const Value& y = x; // const version - Value::AllocatorType allocator; - - EXPECT_EQ(kObjectType, x.GetType()); - EXPECT_TRUE(x.IsObject()); - EXPECT_TRUE(x.ObjectEmpty()); - EXPECT_EQ(0u, x.MemberCount()); - EXPECT_EQ(kObjectType, y.GetType()); - EXPECT_TRUE(y.IsObject()); - EXPECT_TRUE(y.ObjectEmpty()); - EXPECT_EQ(0u, y.MemberCount()); +template +static void TestObject(T& x, Allocator& allocator) { + const T& y = x; // const version // AddMember() x.AddMember("A", "Apple", allocator); @@ -1345,7 +1335,7 @@ TEST(Value, Object) { const unsigned n = 10; for (unsigned first = 0; first < n; first++) { for (unsigned last = first; last <= n; last++) { - Value(kObjectType).Swap(x); + x.RemoveAllMembers(); for (unsigned i = 0; i < n; i++) x.AddMember(keys[i], Value(kArrayType).PushBack(i, allocator), allocator); @@ -1368,6 +1358,23 @@ TEST(Value, Object) { x.RemoveAllMembers(); EXPECT_TRUE(x.ObjectEmpty()); EXPECT_EQ(0u, x.MemberCount()); +} + +TEST(Value, Object) { + Value x(kObjectType); + const Value& y = x; // const version + Value::AllocatorType allocator; + + EXPECT_EQ(kObjectType, x.GetType()); + EXPECT_TRUE(x.IsObject()); + EXPECT_TRUE(x.ObjectEmpty()); + EXPECT_EQ(0u, x.MemberCount()); + EXPECT_EQ(kObjectType, y.GetType()); + EXPECT_TRUE(y.IsObject()); + EXPECT_TRUE(y.ObjectEmpty()); + EXPECT_EQ(0u, y.MemberCount()); + + TestObject(x, allocator); // SetObject() Value z; @@ -1375,6 +1382,85 @@ TEST(Value, Object) { EXPECT_TRUE(z.IsObject()); } +TEST(Value, ObjectHelper) { + Value::AllocatorType allocator; + { + Value x(kObjectType); + Value::Object o = x.GetObject(); + TestObject(o, allocator); + } + + { + Value x(kObjectType); + Value::Object o = x.GetObject(); + o.AddMember("1", 1, allocator); + + Value::Object o2(o); // copy constructor + EXPECT_EQ(1, o2.MemberCount()); + + Value::Object o3; // default constructor + o3 = o; // assignment operator + EXPECT_EQ(1, o3.MemberCount()); + + Value::ConstObject y = static_cast(x).GetObject(); + (void)y; + // y.AddMember("1", 1, allocator); // should not compile + + // Templated functions + x.RemoveAllMembers(); + EXPECT_TRUE(x.Is()); + EXPECT_TRUE(x.Is()); + o.AddMember("1", 1, allocator); + o = x.Get(); + EXPECT_EQ(1, o["1"].GetInt()); + EXPECT_EQ(1, x.Get()["1"].GetInt()); + + Value x2; + x2.Set(o); + EXPECT_TRUE(x.IsNull()); + EXPECT_EQ(1, x2.Get()["1"].GetInt()); + } +} + +#if RAPIDJSON_HAS_CXX11_RANGE_FOR +TEST(Value, ObjectHelperRangeFor) { + Value::AllocatorType allocator; + Value x(kObjectType); + + for (int i = 0; i < 10; i++) { + char name[10]; + Value n(name, static_cast(sprintf(name, "%d", i)), allocator); + x.AddMember(n, i, allocator); + } + + { + int i = 0; + for (auto& m : x.GetObject()) { + char name[10]; + sprintf(name, "%d", i); + EXPECT_STREQ(name, m.name.GetString()); + EXPECT_EQ(i, m.value.GetInt()); + i++; + } + EXPECT_EQ(i, 10); + } + { + int i = 0; + for (const auto& m : const_cast(x).GetObject()) { + char name[10]; + sprintf(name, "%d", i); + EXPECT_STREQ(name, m.name.GetString()); + EXPECT_EQ(i, m.value.GetInt()); + i++; + } + EXPECT_EQ(i, 10); + } + + // Object a = x.GetObject(); + // Object ca = const_cast(x).GetObject(); +} +#endif + TEST(Value, EraseMember_String) { Value::AllocatorType allocator; Value x(kObjectType); From be66450ecdb5450f320561544fcdf19ab0141e78 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 14 Feb 2016 14:00:27 +0800 Subject: [PATCH 09/20] Fix compilation errors --- include/rapidjson/document.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 4370409..f208902 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -2415,7 +2415,7 @@ public: SizeType MemberCount() const { return ptr_->MemberCount(); } bool ObjectEmpty() const { return ptr_->ObjectEmpty(); } - ValueType& operator[](Ch* name) const { return (*ptr_)[name]; } + template ValueType& operator[](T* name) const { return (*ptr_)[name]; } template ValueType& operator[](const GenericValue& name) const { return (*ptr_)[name]; } #if RAPIDJSON_HAS_STDSTRING ValueType& operator[](const std::basic_string& name) const { return (*ptr_)[name]; } @@ -2456,7 +2456,7 @@ public: MemberIterator RemoveMember(MemberIterator m) const { return ptr_->RemoveMember(m); } MemberIterator EraseMember(ConstMemberIterator pos) const { return ptr_->EraseMember(pos); } MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return ptr_->EraseMember(first, last); } - bool EraseMember(const Ch* name) const { ptr_->EraseMember(name); } + bool EraseMember(const Ch* name) const { return ptr_->EraseMember(name); } #if RAPIDJSON_HAS_STDSTRING bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } #endif From 6671bd50f1a3fbc70a50347e15b2160956fe969b Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Sun, 14 Feb 2016 14:07:19 +0800 Subject: [PATCH 10/20] Fix another compilation error --- include/rapidjson/document.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index f208902..67f9e9b 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -2460,7 +2460,7 @@ public: #if RAPIDJSON_HAS_STDSTRING bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } #endif - template bool EraseMember(const GenericValue& name) const { ptr_->EraseMember(name); } + template bool EraseMember(const GenericValue& name) const { return ptr_->EraseMember(name); } #if RAPIDJSON_HAS_CXX11_RANGE_FOR MemberIterator begin() const { return ptr_->MemberBegin(); } From e731726c562cf69a5abe71a3d4b909f92b544fb4 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Mon, 15 Feb 2016 11:20:00 +0800 Subject: [PATCH 11/20] Optimize memory consumption with RAPIDJSON_48BITPOINTER_OPTIMIZATION #330 --- include/rapidjson/document.h | 290 ++++++++++++++++++---------------- include/rapidjson/rapidjson.h | 33 +++- test/unittest/valuetest.cpp | 12 ++ 3 files changed, 200 insertions(+), 135 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index b59cc69..70a6bdd 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -295,7 +295,7 @@ struct GenericStringRef { */ #endif explicit GenericStringRef(const CharType* str) - : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != NULL); } + : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != 0); } //! Create constant string reference from pointer and length #ifndef __clang__ // -Wdocumentation @@ -307,7 +307,7 @@ struct GenericStringRef { */ #endif GenericStringRef(const CharType* str, SizeType len) - : s(str), length(len) { RAPIDJSON_ASSERT(s != NULL); } + : s(str), length(len) { RAPIDJSON_ASSERT(s != 0); } //! implicit conversion to plain CharType pointer operator const Ch *() const { return s; } @@ -425,12 +425,12 @@ public: //@{ //! Default constructor creates a null value. - GenericValue() RAPIDJSON_NOEXCEPT : data_(), flags_(kNullFlag) {} + GenericValue() RAPIDJSON_NOEXCEPT : data_() { data_.f.flags = kNullFlag; } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Move constructor in C++11 - GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_), flags_(rhs.flags_) { - rhs.flags_ = kNullFlag; // give up contents + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_) { + rhs.data_.f.flags = kNullFlag; // give up contents } #endif @@ -455,13 +455,13 @@ public: \param type Type of the value. \note Default content for number is zero. */ - explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_(), flags_() { - static const unsigned defaultFlags[7] = { + explicit GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_() { + static const uint16_t defaultFlags[7] = { kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kShortStringFlag, kNumberAnyFlag }; RAPIDJSON_ASSERT(type <= kNumberType); - flags_ = defaultFlags[type]; + data_.f.flags = defaultFlags[type]; // Use ShortString to store empty string. if (type == kStringType) @@ -490,70 +490,71 @@ public: #else explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT #endif - : data_(), flags_(b ? kTrueFlag : kFalseFlag) { + : data_() { // safe-guard against failing SFINAE RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + data_.f.flags = b ? kTrueFlag : kFalseFlag; } //! Constructor for int value. - explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberIntFlag) { + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_() { data_.n.i64 = i; - if (i >= 0) - flags_ |= kUintFlag | kUint64Flag; + data_.f.flags = (i >= 0) ? (kNumberIntFlag | kUintFlag | kUint64Flag) : kNumberIntFlag; } //! Constructor for unsigned value. - explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUintFlag) { + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_() { data_.n.u64 = u; - if (!(u & 0x80000000)) - flags_ |= kIntFlag | kInt64Flag; + data_.f.flags = (u & 0x80000000) ? kNumberUintFlag : (kNumberUintFlag | kIntFlag | kInt64Flag); } //! Constructor for int64_t value. - explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberInt64Flag) { + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_() { data_.n.i64 = i64; + data_.f.flags = kNumberInt64Flag; if (i64 >= 0) { - flags_ |= kNumberUint64Flag; + data_.f.flags |= kNumberUint64Flag; if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) - flags_ |= kUintFlag; + data_.f.flags |= kUintFlag; if (!(static_cast(i64) & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) - flags_ |= kIntFlag; + data_.f.flags |= kIntFlag; } else if (i64 >= static_cast(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) - flags_ |= kIntFlag; + data_.f.flags |= kIntFlag; } //! Constructor for uint64_t value. - explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUint64Flag) { + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_() { data_.n.u64 = u64; + data_.f.flags = kNumberUint64Flag; if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) - flags_ |= kInt64Flag; + data_.f.flags |= kInt64Flag; if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x00000000))) - flags_ |= kUintFlag; + data_.f.flags |= kUintFlag; if (!(u64 & RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0x80000000))) - flags_ |= kIntFlag; + data_.f.flags |= kIntFlag; } //! Constructor for double value. - explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; } + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_() { data_.n.d = d; data_.f.flags = kNumberDoubleFlag; } //! Constructor for constant string (i.e. do not make a copy of string) - GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(StringRef(s, length)); } + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(StringRef(s, length)); } //! Constructor for constant string (i.e. do not make a copy of string) - explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(s); } + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_() { SetStringRaw(s); } //! Constructor for copy-string (i.e. do make a copy of string) - GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s, length), allocator); } + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_() { SetStringRaw(StringRef(s, length), allocator); } //! Constructor for copy-string (i.e. do make a copy of string) - GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } + GenericValue(const Ch*s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } #if RAPIDJSON_HAS_STDSTRING //! Constructor for copy-string from a string object (i.e. do make a copy of string) /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. */ - GenericValue(const std::basic_string& s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } + GenericValue(const std::basic_string& s, Allocator& allocator) : data_() { SetStringRaw(StringRef(s), allocator); } #endif //! Destructor. @@ -561,21 +562,24 @@ public: */ ~GenericValue() { if (Allocator::kNeedFree) { // Shortcut by Allocator's trait - switch(flags_) { + switch(data_.f.flags) { case kArrayFlag: - for (GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) - v->~GenericValue(); - Allocator::Free(data_.a.elements); + { + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); + Allocator::Free(e); + } break; case kObjectFlag: for (MemberIterator m = MemberBegin(); m != MemberEnd(); ++m) m->~Member(); - Allocator::Free(data_.o.members); + Allocator::Free(GetMembersPointer()); break; case kCopyStringFlag: - Allocator::Free(const_cast(data_.s.str)); + Allocator::Free(GetStringPointer()); break; default: @@ -773,20 +777,20 @@ public: //!@name Type //@{ - Type GetType() const { return static_cast(flags_ & kTypeMask); } - bool IsNull() const { return flags_ == kNullFlag; } - bool IsFalse() const { return flags_ == kFalseFlag; } - bool IsTrue() const { return flags_ == kTrueFlag; } - bool IsBool() const { return (flags_ & kBoolFlag) != 0; } - bool IsObject() const { return flags_ == kObjectFlag; } - bool IsArray() const { return flags_ == kArrayFlag; } - bool IsNumber() const { return (flags_ & kNumberFlag) != 0; } - bool IsInt() const { return (flags_ & kIntFlag) != 0; } - bool IsUint() const { return (flags_ & kUintFlag) != 0; } - bool IsInt64() const { return (flags_ & kInt64Flag) != 0; } - bool IsUint64() const { return (flags_ & kUint64Flag) != 0; } - bool IsDouble() const { return (flags_ & kDoubleFlag) != 0; } - bool IsString() const { return (flags_ & kStringFlag) != 0; } + Type GetType() const { return static_cast(data_.f.flags & kTypeMask); } + bool IsNull() const { return data_.f.flags == kNullFlag; } + bool IsFalse() const { return data_.f.flags == kFalseFlag; } + bool IsTrue() const { return data_.f.flags == kTrueFlag; } + bool IsBool() const { return (data_.f.flags & kBoolFlag) != 0; } + bool IsObject() const { return data_.f.flags == kObjectFlag; } + bool IsArray() const { return data_.f.flags == kArrayFlag; } + bool IsNumber() const { return (data_.f.flags & kNumberFlag) != 0; } + bool IsInt() const { return (data_.f.flags & kIntFlag) != 0; } + bool IsUint() const { return (data_.f.flags & kUintFlag) != 0; } + bool IsInt64() const { return (data_.f.flags & kInt64Flag) != 0; } + bool IsUint64() const { return (data_.f.flags & kUint64Flag) != 0; } + bool IsDouble() const { return (data_.f.flags & kDoubleFlag) != 0; } + bool IsString() const { return (data_.f.flags & kStringFlag) != 0; } // Checks whether a number can be losslessly converted to a double. bool IsLosslessDouble() const { @@ -806,7 +810,7 @@ public: // Checks whether a number is a float (possible lossy). bool IsFloat() const { - if ((flags_ & kDoubleFlag) == 0) + if ((data_.f.flags & kDoubleFlag) == 0) return false; double d = GetDouble(); return d >= -3.4028234e38 && d <= 3.4028234e38; @@ -831,7 +835,7 @@ public: //!@name Bool //@{ - bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return flags_ == kTrueFlag; } + bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return data_.f.flags == kTrueFlag; } //!< Set boolean value /*! \post IsBool() == true */ GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } @@ -905,16 +909,16 @@ public: //! Const member iterator /*! \pre IsObject() == true */ - ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members); } + ConstMemberIterator MemberBegin() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer()); } //! Const \em past-the-end member iterator /*! \pre IsObject() == true */ - ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(data_.o.members + data_.o.size); } + ConstMemberIterator MemberEnd() const { RAPIDJSON_ASSERT(IsObject()); return ConstMemberIterator(GetMembersPointer() + data_.o.size); } //! Member iterator /*! \pre IsObject() == true */ - MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members); } + MemberIterator MemberBegin() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer()); } //! \em Past-the-end member iterator /*! \pre IsObject() == true */ - MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(data_.o.members + data_.o.size); } + MemberIterator MemberEnd() { RAPIDJSON_ASSERT(IsObject()); return MemberIterator(GetMembersPointer() + data_.o.size); } //! Check whether a member exists in the object. /*! @@ -1024,16 +1028,17 @@ public: if (o.size >= o.capacity) { if (o.capacity == 0) { o.capacity = kDefaultObjectCapacity; - o.members = reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member))); + SetMembersPointer(reinterpret_cast(allocator.Malloc(o.capacity * sizeof(Member)))); } else { SizeType oldCapacity = o.capacity; o.capacity += (oldCapacity + 1) / 2; // grow by factor 1.5 - o.members = reinterpret_cast(allocator.Realloc(o.members, oldCapacity * sizeof(Member), o.capacity * sizeof(Member))); + SetMembersPointer(reinterpret_cast(allocator.Realloc(GetMembersPointer(), oldCapacity * sizeof(Member), o.capacity * sizeof(Member)))); } } - o.members[o.size].name.RawAssign(name); - o.members[o.size].value.RawAssign(value); + Member* members = GetMembersPointer(); + members[o.size].name.RawAssign(name); + members[o.size].value.RawAssign(value); o.size++; return *this; } @@ -1212,18 +1217,14 @@ public: MemberIterator RemoveMember(MemberIterator m) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(data_.o.size > 0); - RAPIDJSON_ASSERT(data_.o.members != 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); RAPIDJSON_ASSERT(m >= MemberBegin() && m < MemberEnd()); - MemberIterator last(data_.o.members + (data_.o.size - 1)); - if (data_.o.size > 1 && m != last) { - // Move the last one to this place - *m = *last; - } - else { - // Only one left, just destroy - m->~Member(); - } + MemberIterator last(GetMembersPointer() + (data_.o.size - 1)); + if (data_.o.size > 1 && m != last) + *m = *last; // Move the last one to this place + else + m->~Member(); // Only one left, just destroy --data_.o.size; return m; } @@ -1253,7 +1254,7 @@ public: MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(data_.o.size > 0); - RAPIDJSON_ASSERT(data_.o.members != 0); + RAPIDJSON_ASSERT(GetMembersPointer() != 0); RAPIDJSON_ASSERT(first >= MemberBegin()); RAPIDJSON_ASSERT(first <= last); RAPIDJSON_ASSERT(last <= MemberEnd()); @@ -1315,8 +1316,9 @@ public: */ void Clear() { RAPIDJSON_ASSERT(IsArray()); - for (SizeType i = 0; i < data_.a.size; ++i) - data_.a.elements[i].~GenericValue(); + GenericValue* e = GetElementsPointer(); + for (GenericValue* v = e; v != e + data_.a.size; ++v) + v->~GenericValue(); data_.a.size = 0; } @@ -1328,16 +1330,16 @@ public: GenericValue& operator[](SizeType index) { RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(index < data_.a.size); - return data_.a.elements[index]; + return GetElementsPointer()[index]; } const GenericValue& operator[](SizeType index) const { return const_cast(*this)[index]; } //! Element iterator /*! \pre IsArray() == true */ - ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements; } + ValueIterator Begin() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer(); } //! \em Past-the-end element iterator /*! \pre IsArray() == true */ - ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return data_.a.elements + data_.a.size; } + ValueIterator End() { RAPIDJSON_ASSERT(IsArray()); return GetElementsPointer() + data_.a.size; } //! Constant element iterator /*! \pre IsArray() == true */ ConstValueIterator Begin() const { return const_cast(*this).Begin(); } @@ -1354,7 +1356,7 @@ public: GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { RAPIDJSON_ASSERT(IsArray()); if (newCapacity > data_.a.capacity) { - data_.a.elements = static_cast(allocator.Realloc(data_.a.elements, data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue))); + SetElementsPointer(reinterpret_cast(allocator.Realloc(GetElementsPointer(), data_.a.capacity * sizeof(GenericValue), newCapacity * sizeof(GenericValue)))); data_.a.capacity = newCapacity; } return *this; @@ -1374,7 +1376,7 @@ public: RAPIDJSON_ASSERT(IsArray()); if (data_.a.size >= data_.a.capacity) Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); - data_.a.elements[data_.a.size++].RawAssign(value); + GetElementsPointer()[data_.a.size++].RawAssign(value); return *this; } @@ -1428,7 +1430,7 @@ public: GenericValue& PopBack() { RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(!Empty()); - data_.a.elements[--data_.a.size].~GenericValue(); + GetElementsPointer()[--data_.a.size].~GenericValue(); return *this; } @@ -1454,7 +1456,7 @@ public: ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) { RAPIDJSON_ASSERT(IsArray()); RAPIDJSON_ASSERT(data_.a.size > 0); - RAPIDJSON_ASSERT(data_.a.elements != 0); + RAPIDJSON_ASSERT(GetElementsPointer() != 0); RAPIDJSON_ASSERT(first >= Begin()); RAPIDJSON_ASSERT(first <= last); RAPIDJSON_ASSERT(last <= End()); @@ -1471,21 +1473,21 @@ public: //!@name Number //@{ - int GetInt() const { RAPIDJSON_ASSERT(flags_ & kIntFlag); return data_.n.i.i; } - unsigned GetUint() const { RAPIDJSON_ASSERT(flags_ & kUintFlag); return data_.n.u.u; } - int64_t GetInt64() const { RAPIDJSON_ASSERT(flags_ & kInt64Flag); return data_.n.i64; } - uint64_t GetUint64() const { RAPIDJSON_ASSERT(flags_ & kUint64Flag); return data_.n.u64; } + int GetInt() const { RAPIDJSON_ASSERT(data_.f.flags & kIntFlag); return data_.n.i.i; } + unsigned GetUint() const { RAPIDJSON_ASSERT(data_.f.flags & kUintFlag); return data_.n.u.u; } + int64_t GetInt64() const { RAPIDJSON_ASSERT(data_.f.flags & kInt64Flag); return data_.n.i64; } + uint64_t GetUint64() const { RAPIDJSON_ASSERT(data_.f.flags & kUint64Flag); return data_.n.u64; } //! Get the value as double type. /*! \note If the value is 64-bit integer type, it may lose precision. Use \c IsLosslessDouble() to check whether the converison is lossless. */ double GetDouble() const { RAPIDJSON_ASSERT(IsNumber()); - if ((flags_ & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. - if ((flags_ & kIntFlag) != 0) return data_.n.i.i; // int -> double - if ((flags_ & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double - if ((flags_ & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) - RAPIDJSON_ASSERT((flags_ & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) + if ((data_.f.flags & kDoubleFlag) != 0) return data_.n.d; // exact type, no conversion. + if ((data_.f.flags & kIntFlag) != 0) return data_.n.i.i; // int -> double + if ((data_.f.flags & kUintFlag) != 0) return data_.n.u.u; // unsigned -> double + if ((data_.f.flags & kInt64Flag) != 0) return static_cast(data_.n.i64); // int64_t -> double (may lose precision) + RAPIDJSON_ASSERT((data_.f.flags & kUint64Flag) != 0); return static_cast(data_.n.u64); // uint64_t -> double (may lose precision) } //! Get the value as float type. @@ -1508,12 +1510,12 @@ public: //!@name String //@{ - const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? data_.ss.str : data_.s.str); } + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return (data_.f.flags & kInlineStrFlag) ? data_.ss.str : GetStringPointer(); } //! Get the length of string. /*! Since rapidjson permits "\\u0000" in the json string, strlen(v.GetString()) may not equal to v.GetStringLength(). */ - SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((data_.f.flags & kInlineStrFlag) ? (data_.ss.GetLength()) : data_.s.length); } //! Set this value as a string without copying source string. /*! This version has better performance with supplied length, and also support string containing null character. @@ -1582,7 +1584,7 @@ public: return false; for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { RAPIDJSON_ASSERT(m->name.IsString()); // User may change the type of name by MemberIterator. - if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.flags_ & kCopyFlag) != 0))) + if (RAPIDJSON_UNLIKELY(!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.data_.f.flags & kCopyFlag) != 0))) return false; if (RAPIDJSON_UNLIKELY(!m->value.Accept(handler))) return false; @@ -1592,13 +1594,13 @@ public: case kArrayType: if (RAPIDJSON_UNLIKELY(!handler.StartArray())) return false; - for (const GenericValue* v = data_.a.elements; v != data_.a.elements + data_.a.size; ++v) + for (const GenericValue* v = Begin(); v != End(); ++v) if (RAPIDJSON_UNLIKELY(!v->Accept(handler))) return false; return handler.EndArray(data_.a.size); case kStringType: - return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0); + return handler.String(GetString(), GetStringLength(), (data_.f.flags & kCopyFlag) != 0); default: RAPIDJSON_ASSERT(GetType() == kNumberType); @@ -1615,16 +1617,16 @@ private: template friend class GenericDocument; enum { - kBoolFlag = 0x100, - kNumberFlag = 0x200, - kIntFlag = 0x400, - kUintFlag = 0x800, - kInt64Flag = 0x1000, - kUint64Flag = 0x2000, - kDoubleFlag = 0x4000, - kStringFlag = 0x100000, - kCopyFlag = 0x200000, - kInlineStrFlag = 0x400000, + kBoolFlag = 0x0008, + kNumberFlag = 0x0010, + kIntFlag = 0x0020, + kUintFlag = 0x0040, + kInt64Flag = 0x0080, + kUint64Flag = 0x0100, + kDoubleFlag = 0x0200, + kStringFlag = 0x0400, + kCopyFlag = 0x0800, + kInlineStrFlag = 0x1000, // Initial flags of different types. kNullFlag = kNullType, @@ -1642,16 +1644,27 @@ private: kObjectFlag = kObjectType, kArrayFlag = kArrayType, - kTypeMask = 0xFF // bitwise-and with mask of 0xFF can be optimized by compiler + kTypeMask = 0x07 }; static const SizeType kDefaultArrayCapacity = 16; static const SizeType kDefaultObjectCapacity = 16; + struct Flag { +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION + char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer +#elif RAPIDJSON_64BIT + char payload[Sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes +#else + char payload[Sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes +#endif + uint16_t flags; + }; + struct String { - const Ch* str; SizeType length; - unsigned hashcode; //!< reserved + SizeType hashcode; //!< reserved + const Ch* str; }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars @@ -1660,10 +1673,10 @@ private: // to store has the maximal length of MaxSize then str[LenPos] will be 0 and therefore act as // the string terminator as well. For getting the string length back from that value just use // "MaxSize - str[LenPos]". - // This allows to store 11-chars strings in 32-bit mode and 15-chars strings in 64-bit mode - // inline (for `UTF8`-encoded strings). + // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, + // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). struct ShortString { - enum { MaxChars = sizeof(String) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + enum { MaxChars = sizeof(Flag::payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; Ch str[MaxChars]; inline static bool Usable(SizeType len) { return (MaxSize >= len); } @@ -1698,15 +1711,15 @@ private: }; // 8 bytes struct Object { - Member* members; SizeType size; SizeType capacity; + Member* members; }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode struct Array { - GenericValue* elements; SizeType size; SizeType capacity; + GenericValue* elements; }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode union Data { @@ -1715,51 +1728,61 @@ private: Number n; Object o; Array a; - }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + Flag f; + }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION + + RAPIDJSON_FORCEINLINE Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } + RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } + RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } + RAPIDJSON_FORCEINLINE const GenericValue* SetElementsPointer(const GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } + RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); } + RAPIDJSON_FORCEINLINE const Member* SetMembersPointer(const Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } // Initialize this value as array with initial data, without calling destructor. void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { - flags_ = kArrayFlag; + data_.f.flags = kArrayFlag; if (count) { - data_.a.elements = static_cast(allocator.Malloc(count * sizeof(GenericValue))); - std::memcpy(data_.a.elements, values, count * sizeof(GenericValue)); + GenericValue* e = static_cast(allocator.Malloc(count * sizeof(GenericValue))); + SetElementsPointer(e); + std::memcpy(e, values, count * sizeof(GenericValue)); } else - data_.a.elements = NULL; + SetElementsPointer(0); data_.a.size = data_.a.capacity = count; } //! Initialize this value as object with initial data, without calling destructor. void SetObjectRaw(Member* members, SizeType count, Allocator& allocator) { - flags_ = kObjectFlag; + data_.f.flags = kObjectFlag; if (count) { - data_.o.members = static_cast(allocator.Malloc(count * sizeof(Member))); - std::memcpy(data_.o.members, members, count * sizeof(Member)); + Member* m = static_cast(allocator.Malloc(count * sizeof(Member))); + SetMembersPointer(m); + std::memcpy(m, members, count * sizeof(Member)); } else - data_.o.members = NULL; + SetMembersPointer(0); data_.o.size = data_.o.capacity = count; } //! Initialize this value as constant string, without calling destructor. void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { - flags_ = kConstStringFlag; - data_.s.str = s; + data_.f.flags = kConstStringFlag; + SetStringPointer(s); data_.s.length = s.length; } //! Initialize this value as copy string with initial data, without calling destructor. void SetStringRaw(StringRefType s, Allocator& allocator) { - Ch* str = NULL; - if(ShortString::Usable(s.length)) { - flags_ = kShortStringFlag; + Ch* str = 0; + if (ShortString::Usable(s.length)) { + data_.f.flags = kShortStringFlag; data_.ss.SetLength(s.length); str = data_.ss.str; } else { - flags_ = kCopyStringFlag; + data_.f.flags = kCopyStringFlag; data_.s.length = s.length; str = static_cast(allocator.Malloc((s.length + 1) * sizeof(Ch))); - data_.s.str = str; + SetStringPointer(str); } std::memcpy(str, s, s.length * sizeof(Ch)); str[s.length] = '\0'; @@ -1768,8 +1791,8 @@ private: //! Assignment without calling destructor void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { data_ = rhs.data_; - flags_ = rhs.flags_; - rhs.flags_ = kNullFlag; + // data_.f.flags = rhs.data_.f.flags; + rhs.data_.f.flags = kNullFlag; } template @@ -1789,7 +1812,6 @@ private: } Data data_; - unsigned flags_; }; //! GenericValue with UTF8 encoding @@ -2158,15 +2180,15 @@ GenericValue::GenericValue(const GenericValue(&rhs.data_); } else { SetStringRaw(StringRef(rhs.GetString(), rhs.GetStringLength()), allocator); } break; default: - flags_ = rhs.flags_; + data_.f.flags = rhs.data_.f.flags; data_ = *reinterpret_cast(&rhs.data_); break; } diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index 9cb40a9..028513d 100644 --- a/include/rapidjson/rapidjson.h +++ b/include/rapidjson/rapidjson.h @@ -265,7 +265,8 @@ \param x pointer to align Some machines require strict data alignment. Currently the default uses 4 bytes - alignment. User can customize by defining the RAPIDJSON_ALIGN function macro. + alignment on 32-bit platforms and 8 bytes alignment for 64-bit platforms. + User can customize by defining the RAPIDJSON_ALIGN function macro. */ #ifndef RAPIDJSON_ALIGN #if RAPIDJSON_64BIT == 1 @@ -288,6 +289,36 @@ #define RAPIDJSON_UINT64_C2(high32, low32) ((static_cast(high32) << 32) | static_cast(low32)) #endif +/////////////////////////////////////////////////////////////////////////////// +// RAPIDJSON_48BITPOINTER_OPTIMIZATION + +//! Use only lower 48-bit address for some pointers. +/*! + \ingroup RAPIDJSON_CONFIG + + This optimization uses the fact that current X86-64 architecture only implement lower 48-bit virtual address. + The higher 16-bit can be used for storing other data. + \c GenericValue uses this optimization to reduce its size form 24 bytes to 16 bytes in 64-bit architecture. +*/ +#ifndef RAPIDJSON_48BITPOINTER_OPTIMIZATION +#if defined(__amd64__) || defined(__amd64) || defined(__x86_64__) || defined(__x86_64) || defined(_M_X64) || defined(_M_AMD64) +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 1 +#else +#define RAPIDJSON_48BITPOINTER_OPTIMIZATION 0 +#endif +#endif // RAPIDJSON_48BITPOINTER_OPTIMIZATION + +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION == 1 +#if RAPIDJSON_64BIT != 1 +#error RAPIDJSON_48BITPOINTER_OPTIMIZATION can only be set to 1 when RAPIDJSON_64BIT=1 +#endif +#define RAPIDJSON_SETPOINTER(type, p, x) (p = reinterpret_cast((reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0xFFFF0000, 0x00000000))) | reinterpret_cast(reinterpret_cast(x)))) +#define RAPIDJSON_GETPOINTER(type, p) (reinterpret_cast(reinterpret_cast(p) & static_cast(RAPIDJSON_UINT64_C2(0x0000FFFF, 0xFFFFFFFF)))) +#else +#define RAPIDJSON_SETPOINTER(type, p, x) (p = (x)) +#define RAPIDJSON_GETPOINTER(type, p) (p) +#endif + /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_SSE2/RAPIDJSON_SSE42/RAPIDJSON_SIMD diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 4179ccb..81ff41d 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -23,6 +23,18 @@ RAPIDJSON_DIAG_OFF(c++98-compat) using namespace rapidjson; +TEST(Value, Size) { + if (sizeof(SizeType) == 4) { +#if RAPIDJSON_48BITPOINTER_OPTIMIZATION + EXPECT_EQ(16, sizeof(Value)); +#elif RAPIDJSON_64BIT + EXPECT_EQ(24, sizeof(Value)); +#else + EXPECT_EQ(16, sizeof(Value)); +#endif + } +} + TEST(Value, DefaultConstructor) { Value x; EXPECT_EQ(kNullType, x.GetType()); From 4bb6f2c089abec74d26545401adbccb6a19ab35e Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Mon, 15 Feb 2016 11:39:32 +0800 Subject: [PATCH 12/20] Fix compilation errors on 32-bit gcc/clang --- include/rapidjson/document.h | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 70a6bdd..6aead38 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -579,7 +579,7 @@ public: break; case kCopyStringFlag: - Allocator::Free(GetStringPointer()); + Allocator::Free(const_cast(GetStringPointer())); break; default: @@ -1654,9 +1654,9 @@ private: #if RAPIDJSON_48BITPOINTER_OPTIMIZATION char payload[sizeof(SizeType) * 2 + 6]; // 2 x SizeType + lower 48-bit pointer #elif RAPIDJSON_64BIT - char payload[Sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 6]; // 6 padding bytes #else - char payload[Sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes + char payload[sizeof(SizeType) * 2 + sizeof(void*) + 2]; // 2 padding bytes #endif uint16_t flags; }; @@ -1731,12 +1731,12 @@ private: Flag f; }; // 16 bytes in 32-bit mode, 24 bytes in 64-bit mode, 16 bytes in 64-bit with RAPIDJSON_48BITPOINTER_OPTIMIZATION - RAPIDJSON_FORCEINLINE Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } + RAPIDJSON_FORCEINLINE const Ch* GetStringPointer() const { return RAPIDJSON_GETPOINTER(Ch, data_.s.str); } RAPIDJSON_FORCEINLINE const Ch* SetStringPointer(const Ch* str) { return RAPIDJSON_SETPOINTER(Ch, data_.s.str, str); } RAPIDJSON_FORCEINLINE GenericValue* GetElementsPointer() const { return RAPIDJSON_GETPOINTER(GenericValue, data_.a.elements); } - RAPIDJSON_FORCEINLINE const GenericValue* SetElementsPointer(const GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } + RAPIDJSON_FORCEINLINE GenericValue* SetElementsPointer(GenericValue* elements) { return RAPIDJSON_SETPOINTER(GenericValue, data_.a.elements, elements); } RAPIDJSON_FORCEINLINE Member* GetMembersPointer() const { return RAPIDJSON_GETPOINTER(Member, data_.o.members); } - RAPIDJSON_FORCEINLINE const Member* SetMembersPointer(const Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } + RAPIDJSON_FORCEINLINE Member* SetMembersPointer(Member* members) { return RAPIDJSON_SETPOINTER(Member, data_.o.members, members); } // Initialize this value as array with initial data, without calling destructor. void SetArrayRaw(GenericValue* values, SizeType count, Allocator& allocator) { From 49c982b4b71a479d94650e7eb5b5290320c833a3 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Mon, 15 Feb 2016 12:19:30 +0800 Subject: [PATCH 13/20] Fix C++03 compilation error about sizeof() --- include/rapidjson/document.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 6aead38..63cca99 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1676,7 +1676,7 @@ private: // This allows to store 13-chars strings in 32-bit mode, 21-chars strings in 64-bit mode, // 13-chars strings for RAPIDJSON_48BITPOINTER_OPTIMIZATION=1 inline (for `UTF8`-encoded strings). struct ShortString { - enum { MaxChars = sizeof(Flag::payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + enum { MaxChars = sizeof(static_cast(0)->payload) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; Ch str[MaxChars]; inline static bool Usable(SizeType len) { return (MaxSize >= len); } From 4ababca8938b478ced660bcafa6b52c8a053640f Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Wed, 17 Feb 2016 08:48:22 +0800 Subject: [PATCH 14/20] Fixed typos pointed out by @pah --- include/rapidjson/document.h | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 67f9e9b..1aecbd5 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -478,34 +478,34 @@ struct TypeHelper > { template struct TypeHelper { - typedef typename ValueType::Array ArratType; + typedef typename ValueType::Array ArrayType; static bool Is(const ValueType& v) { return v.IsArray(); } - static ArratType Get(ValueType& v) { return v.GetArray(); } - static ValueType& Set(ValueType& v, ArratType data) { return v.SetArray(data); } - static ValueType& Set(ValueType& v, ArratType data, typename ValueType::AllocatorType&) { return v.SetArray(data); } + static ArrayType Get(ValueType& v) { return v.GetArray(); } + static ValueType& Set(ValueType& v, ArrayType data) { return v.SetArray(data); } + static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v.SetArray(data); } }; template struct TypeHelper { - typedef typename ValueType::ConstArray ArratType; + typedef typename ValueType::ConstArray ArrayType; static bool Is(const ValueType& v) { return v.IsArray(); } - static ArratType Get(const ValueType& v) { return v.GetArray(); } + static ArrayType Get(const ValueType& v) { return v.GetArray(); } }; template struct TypeHelper { - typedef typename ValueType::Object ArratType; + typedef typename ValueType::Object ObjectType; static bool Is(const ValueType& v) { return v.IsObject(); } - static ArratType Get(ValueType& v) { return v.GetObject(); } - static ValueType& Set(ValueType& v, ArratType data) { return v.SetObject(data); } - static ValueType& Set(ValueType& v, ArratType data, typename ValueType::AllocatorType&) { return v.SetObject(data); } + static ObjectType Get(ValueType& v) { return v.GetObject(); } + static ValueType& Set(ValueType& v, ObjectType data) { return v.SetObject(data); } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v.SetObject(data); } }; template struct TypeHelper { - typedef typename ValueType::ConstObject ArratType; + typedef typename ValueType::ConstObject ObjectType; static bool Is(const ValueType& v) { return v.IsObject(); } - static ArratType Get(const ValueType& v) { return v.GetObject(); } + static ObjectType Get(const ValueType& v) { return v.GetObject(); } }; } // namespace internal From 8c79fb65ae86a78c78e757e82af4c225c206afe6 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Wed, 17 Feb 2016 09:34:19 +0800 Subject: [PATCH 15/20] Fix return type of GenericObject::AddMember() --- include/rapidjson/document.h | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 1aecbd5..5f2c1ba 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -2432,21 +2432,21 @@ public: #if RAPIDJSON_HAS_STDSTRING MemberIterator FindMember(const std::basic_string& name) const { return ptr_->FindMember(name); } #endif - ValueType& AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } - ValueType& AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } + const GenericObject& AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + const GenericObject& AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } #if RAPIDJSON_HAS_STDSTRING - ValueType& AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } + const GenericObject& AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } #endif - template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS - ValueType& AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } - ValueType& AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } - ValueType& AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } - ValueType& AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } + const GenericObject& AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + const GenericObject& AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + const GenericObject& AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + const GenericObject& AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS - ValueType& AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } - ValueType& AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } - template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { return ptr_->AddMember(name, value, allocator); } + const GenericObject& AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + const GenericObject& AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericObject&)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } void RemoveAllMembers() { return ptr_->RemoveAllMembers(); } bool RemoveMember(const Ch* name) const { return ptr_->RemoveMember(name); } #if RAPIDJSON_HAS_STDSTRING From 70f9671bdfffb500160fbe9dfb3410fa1230c60b Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Thu, 18 Feb 2016 01:19:47 +0800 Subject: [PATCH 16/20] Return value type for GenericObject/Array member functions --- include/rapidjson/document.h | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 5f2c1ba..20c6712 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -2365,14 +2365,14 @@ public: ValueType& operator[](SizeType index) const { return (*ptr_)[index]; } ValueIterator Begin() const { return ptr_->Begin(); } ValueIterator End() const { return ptr_->End(); } - const GenericArray& Reserve(SizeType newCapacity, AllocatorType &allocator) const { ptr_->Reserve(newCapacity, allocator); return *this; } - const GenericArray& PushBack(ValueType& value, AllocatorType& allocator) const { ptr_->PushBack(value, allocator); return *this; } + GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { ptr_->Reserve(newCapacity, allocator); return *this; } + GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { ptr_->PushBack(value, allocator); return *this; } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS - const GenericArray& PushBack(ValueType&& value, AllocatorType& allocator) const { ptr_->PushBack(value, allocator); return *this; } + GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { ptr_->PushBack(value, allocator); return *this; } #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS - const GenericArray& PushBack(StringRefType value, AllocatorType& allocator) const { ptr_->PushBack(value, allocator); return *this; } + GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { ptr_->PushBack(value, allocator); return *this; } template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { ptr_->PushBack(value, allocator); return *this; } - const GenericArray& PopBack() const { ptr_->PopBack(); return *this; } + GenericArray PopBack() const { ptr_->PopBack(); return *this; } ValueIterator Erase(ConstValueIterator pos) const { return ptr_->Erase(pos); } ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return ptr_->Erase(first, last); } @@ -2432,21 +2432,21 @@ public: #if RAPIDJSON_HAS_STDSTRING MemberIterator FindMember(const std::basic_string& name) const { return ptr_->FindMember(name); } #endif - const GenericObject& AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } - const GenericObject& AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } #if RAPIDJSON_HAS_STDSTRING - const GenericObject& AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } #endif template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS - const GenericObject& AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } - const GenericObject& AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } - const GenericObject& AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } - const GenericObject& AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS - const GenericObject& AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } - const GenericObject& AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } - template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericObject&)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } void RemoveAllMembers() { return ptr_->RemoveAllMembers(); } bool RemoveMember(const Ch* name) const { return ptr_->RemoveMember(name); } #if RAPIDJSON_HAS_STDSTRING From d13be6c72170842bcde1f182d72ca7638960bf66 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Thu, 18 Feb 2016 19:04:22 +0800 Subject: [PATCH 17/20] Change pointer to reference in GenericArray|Object --- include/rapidjson/document.h | 128 +++++++++++++++++------------------ test/unittest/valuetest.cpp | 12 ++-- 2 files changed, 68 insertions(+), 72 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 20c6712..c90705c 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -971,7 +971,7 @@ public: GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } //! Set this value with an object. - GenericValue& SetObject(Object& o) { return *this = *o.ptr_; } + GenericValue& SetObject(Object& o) { return *this = o.value_; } //! Get the number of members in the object. SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } @@ -1432,7 +1432,7 @@ public: GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } //! Set this value with an array. - GenericValue& SetArray(Array& a) { return *this = *a.ptr_; } + GenericValue& SetArray(Array& a) { return *this = a.value_; } //! Get the number of elements in array. SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } @@ -2353,37 +2353,37 @@ public: template friend class GenericValue; - GenericArray() : ptr_() {} - GenericArray(const GenericArray& rhs) : ptr_(rhs.ptr_) {} - GenericArray& operator=(const GenericArray& rhs) { ptr_ = rhs.ptr_; return *this; } + GenericArray(const GenericArray& rhs) : value_(rhs.value_) {} + GenericArray& operator=(const GenericArray& rhs) { value_ = rhs.value_; return *this; } ~GenericArray() {} - SizeType Size() const { return ptr_->Size(); } - SizeType Capacity() const { return ptr_->Capacity(); } - bool Empty() const { return ptr_->Empty(); } - void Clear() const { ptr_->Clear(); } - ValueType& operator[](SizeType index) const { return (*ptr_)[index]; } - ValueIterator Begin() const { return ptr_->Begin(); } - ValueIterator End() const { return ptr_->End(); } - GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { ptr_->Reserve(newCapacity, allocator); return *this; } - GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { ptr_->PushBack(value, allocator); return *this; } + SizeType Size() const { return value_.Size(); } + SizeType Capacity() const { return value_.Capacity(); } + bool Empty() const { return value_.Empty(); } + void Clear() const { value_.Clear(); } + ValueType& operator[](SizeType index) const { return value_[index]; } + ValueIterator Begin() const { return value_.Begin(); } + ValueIterator End() const { return value_.End(); } + GenericArray Reserve(SizeType newCapacity, AllocatorType &allocator) const { value_.Reserve(newCapacity, allocator); return *this; } + GenericArray PushBack(ValueType& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { ptr_->PushBack(value, allocator); return *this; } + GenericArray PushBack(ValueType&& value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { ptr_->PushBack(value, allocator); return *this; } - template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { ptr_->PushBack(value, allocator); return *this; } - GenericArray PopBack() const { ptr_->PopBack(); return *this; } - ValueIterator Erase(ConstValueIterator pos) const { return ptr_->Erase(pos); } - ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return ptr_->Erase(first, last); } + GenericArray PushBack(StringRefType value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (const GenericArray&)) PushBack(T value, AllocatorType& allocator) const { value_.PushBack(value, allocator); return *this; } + GenericArray PopBack() const { value_.PopBack(); return *this; } + ValueIterator Erase(ConstValueIterator pos) const { return value_.Erase(pos); } + ValueIterator Erase(ConstValueIterator first, ConstValueIterator last) const { return value_.Erase(first, last); } #if RAPIDJSON_HAS_CXX11_RANGE_FOR - ValueIterator begin() const { return ptr_->Begin(); } - ValueIterator end() const { return ptr_->End(); } + ValueIterator begin() const { return value_.Begin(); } + ValueIterator end() const { return value_.End(); } #endif private: - GenericArray(ValueType& value) : ptr_(&value) {} - ValueType* ptr_; + GenericArray(); + GenericArray(ValueType& value) : value_(value) {} + ValueType& value_; }; //! Helper class for accessing Value of array type. @@ -2408,68 +2408,68 @@ public: template friend class GenericValue; - GenericObject() : ptr_() {} - GenericObject(const GenericObject& rhs) : ptr_(rhs.ptr_) {} - GenericObject& operator=(const GenericObject& rhs) { ptr_ = rhs.ptr_; return *this; } + GenericObject(const GenericObject& rhs) : value_(rhs.value_) {} + GenericObject& operator=(const GenericObject& rhs) { value_ = rhs.value_; return *this; } ~GenericObject() {} - SizeType MemberCount() const { return ptr_->MemberCount(); } - bool ObjectEmpty() const { return ptr_->ObjectEmpty(); } - template ValueType& operator[](T* name) const { return (*ptr_)[name]; } - template ValueType& operator[](const GenericValue& name) const { return (*ptr_)[name]; } + SizeType MemberCount() const { return value_.MemberCount(); } + bool ObjectEmpty() const { return value_.ObjectEmpty(); } + template ValueType& operator[](T* name) const { return value_[name]; } + template ValueType& operator[](const GenericValue& name) const { return value_[name]; } #if RAPIDJSON_HAS_STDSTRING - ValueType& operator[](const std::basic_string& name) const { return (*ptr_)[name]; } + ValueType& operator[](const std::basic_string& name) const { return value_[name]; } #endif - MemberIterator MemberBegin() const { return ptr_->MemberBegin(); } - MemberIterator MemberEnd() const { return ptr_->MemberEnd(); } - bool HasMember(const Ch* name) const { return ptr_->HasMember(name); } + MemberIterator MemberBegin() const { return value_.MemberBegin(); } + MemberIterator MemberEnd() const { return value_.MemberEnd(); } + bool HasMember(const Ch* name) const { return value_.HasMember(name); } #if RAPIDJSON_HAS_STDSTRING - bool HasMember(const std::basic_string& name) const { return ptr_->HasMember(name); } + bool HasMember(const std::basic_string& name) const { return value_.HasMember(name); } #endif - template bool HasMember(const GenericValue& name) const { return ptr_->HasMember(name); } - MemberIterator FindMember(const Ch* name) const { ptr_->FindMember(name); } - template MemberIterator FindMember(const GenericValue& name) const { ptr_->FindMember(name); } + template bool HasMember(const GenericValue& name) const { return value_.HasMember(name); } + MemberIterator FindMember(const Ch* name) const { value_.FindMember(name); } + template MemberIterator FindMember(const GenericValue& name) const { value_.FindMember(name); } #if RAPIDJSON_HAS_STDSTRING - MemberIterator FindMember(const std::basic_string& name) const { return ptr_->FindMember(name); } + MemberIterator FindMember(const std::basic_string& name) const { return value_.FindMember(name); } #endif - GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } - GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } #if RAPIDJSON_HAS_STDSTRING - GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, std::basic_string& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } #endif - template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (ValueType&)) AddMember(ValueType& name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } #if RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } - GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } - GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } - GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType&& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType&& name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(ValueType& name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, ValueType&& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS - GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } - GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } - template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { ptr_->AddMember(name, value, allocator); return *this; } - void RemoveAllMembers() { return ptr_->RemoveAllMembers(); } - bool RemoveMember(const Ch* name) const { return ptr_->RemoveMember(name); } + GenericObject AddMember(StringRefType name, ValueType& value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + GenericObject AddMember(StringRefType name, StringRefType value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericObject)) AddMember(StringRefType name, T value, AllocatorType& allocator) const { value_.AddMember(name, value, allocator); return *this; } + void RemoveAllMembers() { return value_.RemoveAllMembers(); } + bool RemoveMember(const Ch* name) const { return value_.RemoveMember(name); } #if RAPIDJSON_HAS_STDSTRING - bool RemoveMember(const std::basic_string& name) const { return ptr_->RemoveMember(name); } + bool RemoveMember(const std::basic_string& name) const { return value_.RemoveMember(name); } #endif - template bool RemoveMember(const GenericValue& name) const { return ptr_->RemoveMember(name); } - MemberIterator RemoveMember(MemberIterator m) const { return ptr_->RemoveMember(m); } - MemberIterator EraseMember(ConstMemberIterator pos) const { return ptr_->EraseMember(pos); } - MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return ptr_->EraseMember(first, last); } - bool EraseMember(const Ch* name) const { return ptr_->EraseMember(name); } + template bool RemoveMember(const GenericValue& name) const { return value_.RemoveMember(name); } + MemberIterator RemoveMember(MemberIterator m) const { return value_.RemoveMember(m); } + MemberIterator EraseMember(ConstMemberIterator pos) const { return value_.EraseMember(pos); } + MemberIterator EraseMember(ConstMemberIterator first, ConstMemberIterator last) const { return value_.EraseMember(first, last); } + bool EraseMember(const Ch* name) const { return value_.EraseMember(name); } #if RAPIDJSON_HAS_STDSTRING bool EraseMember(const std::basic_string& name) const { return EraseMember(ValueType(StringRef(name))); } #endif - template bool EraseMember(const GenericValue& name) const { return ptr_->EraseMember(name); } + template bool EraseMember(const GenericValue& name) const { return value_.EraseMember(name); } #if RAPIDJSON_HAS_CXX11_RANGE_FOR - MemberIterator begin() const { return ptr_->MemberBegin(); } - MemberIterator end() const { return ptr_->MemberEnd(); } + MemberIterator begin() const { return value_.MemberBegin(); } + MemberIterator end() const { return value_.MemberEnd(); } #endif private: - GenericObject(ValueType& value) : ptr_(&value) {} - ValueType* ptr_; + GenericObject(); + GenericObject(ValueType& value) : value_(value) {} + ValueType& value_; }; RAPIDJSON_NAMESPACE_END diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 50eb206..28d84d4 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -1042,8 +1042,7 @@ TEST(Value, ArrayHelper) { Value::Array a2(a); // copy constructor EXPECT_EQ(1, a2.Size()); - Value::Array a3; // default constructor - a3 = a; // assignment operator + Value::Array a3 = a; EXPECT_EQ(1, a3.Size()); Value::ConstArray y = static_cast(x).GetArray(); @@ -1055,8 +1054,7 @@ TEST(Value, ArrayHelper) { EXPECT_TRUE(x.Is()); EXPECT_TRUE(x.Is()); a.PushBack(1, allocator); - a = x.Get(); - EXPECT_EQ(1, a[0].GetInt()); + EXPECT_EQ(1, x.Get()[0].GetInt()); EXPECT_EQ(1, x.Get()[0].GetInt()); Value x2; @@ -1398,8 +1396,7 @@ TEST(Value, ObjectHelper) { Value::Object o2(o); // copy constructor EXPECT_EQ(1, o2.MemberCount()); - Value::Object o3; // default constructor - o3 = o; // assignment operator + Value::Object o3 = o; EXPECT_EQ(1, o3.MemberCount()); Value::ConstObject y = static_cast(x).GetObject(); @@ -1411,8 +1408,7 @@ TEST(Value, ObjectHelper) { EXPECT_TRUE(x.Is()); EXPECT_TRUE(x.Is()); o.AddMember("1", 1, allocator); - o = x.Get(); - EXPECT_EQ(1, o["1"].GetInt()); + EXPECT_EQ(1, x.Get()["1"].GetInt()); EXPECT_EQ(1, x.Get()["1"].GetInt()); Value x2; From 46dc8e9240113f53e3d9db32f66f46b8e40f89f7 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 19 Feb 2016 00:49:05 +0800 Subject: [PATCH 18/20] Add implicit constructors of GenericValue for GenercArray|Object MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Also remove SetArray|Object(…) --- include/rapidjson/document.h | 36 +++++++++++++++++++++-------- test/unittest/valuetest.cpp | 44 ++++++++++++++++++++++++++++++++++-- 2 files changed, 68 insertions(+), 12 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index c90705c..520e9e1 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -481,8 +481,8 @@ struct TypeHelper { typedef typename ValueType::Array ArrayType; static bool Is(const ValueType& v) { return v.IsArray(); } static ArrayType Get(ValueType& v) { return v.GetArray(); } - static ValueType& Set(ValueType& v, ArrayType data) { return v.SetArray(data); } - static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v.SetArray(data); } + static ValueType& Set(ValueType& v, ArrayType data) { return v = data; } + static ValueType& Set(ValueType& v, ArrayType data, typename ValueType::AllocatorType&) { return v = data; } }; template @@ -497,8 +497,8 @@ struct TypeHelper { typedef typename ValueType::Object ObjectType; static bool Is(const ValueType& v) { return v.IsObject(); } static ObjectType Get(ValueType& v) { return v.GetObject(); } - static ValueType& Set(ValueType& v, ObjectType data) { return v.SetObject(data); } - static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { return v.SetObject(data); } + static ValueType& Set(ValueType& v, ObjectType data) { return v = data; } + static ValueType& Set(ValueType& v, ObjectType data, typename ValueType::AllocatorType&) { v = data; } }; template @@ -681,6 +681,28 @@ public: GenericValue(const std::basic_string& s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } #endif + //! Constructor for Array. + /*! + \param a An array obtained by \c GetArray(). + \note \c Array is always pass-by-value. + \note the source array is moved into this value and the sourec array becomes empty. + */ + GenericValue(Array a) : data_(a.value_.data_), flags_(a.value_.flags_) { + a.value_.data_ = Data(); + a.value_.flags_ = kArrayFlag; + } + + //! Constructor for Object. + /*! + \param o An object obtained by \c GetObject(). + \note \c Object is always pass-by-value. + \note the source object is moved into this value and the sourec object becomes empty. + */ + GenericValue(Object o) : data_(o.value_.data_), flags_(o.value_.flags_) { + o.value_.data_ = Data(); + o.value_.flags_ = kObjectFlag; + } + //! Destructor. /*! Need to destruct elements of array, members of object, or copy-string. */ @@ -970,9 +992,6 @@ public: /*! \post IsObject() == true */ GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } - //! Set this value with an object. - GenericValue& SetObject(Object& o) { return *this = o.value_; } - //! Get the number of members in the object. SizeType MemberCount() const { RAPIDJSON_ASSERT(IsObject()); return data_.o.size; } @@ -1431,9 +1450,6 @@ public: /*! \post IsArray == true */ GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } - //! Set this value with an array. - GenericValue& SetArray(Array& a) { return *this = a.value_; } - //! Get the number of elements in array. SizeType Size() const { RAPIDJSON_ASSERT(IsArray()); return data_.a.size; } diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 28d84d4..db99907 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -1059,9 +1059,32 @@ TEST(Value, ArrayHelper) { Value x2; x2.Set(a); - EXPECT_TRUE(x.IsNull()); + EXPECT_TRUE(x.IsArray()); // IsArray() is invariant after moving. EXPECT_EQ(1, x2.Get()[0].GetInt()); } + + { + Value y(kArrayType); + y.PushBack(123, allocator); + + Value x(y.GetArray()); // Construct value form array. + EXPECT_TRUE(x.IsArray()); + EXPECT_EQ(123, x[0].GetInt()); + EXPECT_TRUE(y.IsArray()); // Invariant + EXPECT_TRUE(y.Empty()); + } + + { + Value x(kArrayType); + Value y(kArrayType); + y.PushBack(123, allocator); + x.PushBack(y.GetArray(), allocator); // Implicit constructor to convert Array to GenericValue + + EXPECT_EQ(1, x.Size()); + EXPECT_EQ(123, x[0][0].GetInt()); + EXPECT_TRUE(y.IsArray()); + EXPECT_TRUE(y.Empty()); + } } #if RAPIDJSON_HAS_CXX11_RANGE_FOR @@ -1413,9 +1436,26 @@ TEST(Value, ObjectHelper) { Value x2; x2.Set(o); - EXPECT_TRUE(x.IsNull()); + EXPECT_TRUE(x.IsObject()); // IsObject() is invariant after moving EXPECT_EQ(1, x2.Get()["1"].GetInt()); } + + { + Value x(kObjectType); + x.AddMember("a", "apple", allocator); + Value y(x.GetObject()); + EXPECT_STREQ("apple", y["a"].GetString()); + EXPECT_TRUE(x.IsObject()); // Invariant + } + + { + Value x(kObjectType); + x.AddMember("a", "apple", allocator); + Value y(kObjectType); + y.AddMember("fruits", x.GetObject(), allocator); + EXPECT_STREQ("apple", y["fruits"]["a"].GetString()); + EXPECT_TRUE(x.IsObject()); // Invariant + } } #if RAPIDJSON_HAS_CXX11_RANGE_FOR From 9f6736c2ccbf74c5fa4a0fad915f086dd3a7b5b5 Mon Sep 17 00:00:00 2001 From: Milo Yip Date: Fri, 19 Feb 2016 01:07:16 +0800 Subject: [PATCH 19/20] Add noexcept to the constructors --- include/rapidjson/document.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 520e9e1..f2dfbef 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -687,7 +687,7 @@ public: \note \c Array is always pass-by-value. \note the source array is moved into this value and the sourec array becomes empty. */ - GenericValue(Array a) : data_(a.value_.data_), flags_(a.value_.flags_) { + GenericValue(Array a) RAPIDJSON_NOEXCEPT : data_(a.value_.data_), flags_(a.value_.flags_) { a.value_.data_ = Data(); a.value_.flags_ = kArrayFlag; } @@ -698,7 +698,7 @@ public: \note \c Object is always pass-by-value. \note the source object is moved into this value and the sourec object becomes empty. */ - GenericValue(Object o) : data_(o.value_.data_), flags_(o.value_.flags_) { + GenericValue(Object o) RAPIDJSON_NOEXCEPT : data_(o.value_.data_), flags_(o.value_.flags_) { o.value_.data_ = Data(); o.value_.flags_ = kObjectFlag; } From 479a6b9f68b1f3c2103f3e61dcfc63f747cfd994 Mon Sep 17 00:00:00 2001 From: Ian Bell Date: Thu, 18 Feb 2016 19:23:07 -0700 Subject: [PATCH 20/20] Editorial changes to schema documentation Fixed up some wording to the schema documentation --- doc/schema.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/schema.md b/doc/schema.md index 4e07e9f..053fc23 100644 --- a/doc/schema.md +++ b/doc/schema.md @@ -2,17 +2,17 @@ ## Status: experimental, shall be included in v1.1 -JSON Schema is a draft standard for describing format of JSON. The schema itself is also a JSON. By validating a JSON with JSON Schema, your code can safely access the DOM without manually checking types, or whether a key exists, etc. It can also ensure that the serialized JSON conform to a specified schema. +JSON Schema is a draft standard for describing the format of JSON data. The schema itself is also JSON data. By validating a JSON structure with JSON Schema, your code can safely access the DOM without manually checking types, or whether a key exists, etc. It can also ensure that the serialized JSON conform to a specified schema. -RapidJSON implemented a JSON Schema validator for [JSON Schema Draft v4](http://json-schema.org/documentation.html). If you do not familiar with JSON Schema, you may refer to [Understanding JSON Schema](http://spacetelescope.github.io/understanding-json-schema/). +RapidJSON implemented a JSON Schema validator for [JSON Schema Draft v4](http://json-schema.org/documentation.html). If you are not familiar with JSON Schema, you may refer to [Understanding JSON Schema](http://spacetelescope.github.io/understanding-json-schema/). [TOC] ## Basic Usage -First of all, you need to parse a JSON Schema into `Document`, and then compile the `Document` into `SchemaDocument`. +First of all, you need to parse a JSON Schema into `Document`, and then compile the `Document` into a `SchemaDocument`. -Secondly, construct a `SchemaValidator` with the `SchedmaDocument`. It is similar to a `Writer` in the sense of handling SAX events. So, you can use `document.Accept(validator)` to validate a document, and then check the validity. +Secondly, construct a `SchemaValidator` with the `SchemaDocument`. It is similar to a `Writer` in the sense of handling SAX events. So, you can use `document.Accept(validator)` to validate a document, and then check the validity. ~~~cpp #include "rapidjson/schema.h" @@ -54,7 +54,7 @@ Some notes: ## Validation during parsing/serialization -Differ to most JSON Schema validator implementations, RapidJSON provides a SAX-based schema validator. Therefore, you can parse a JSON from a stream while validating it on the fly. If the validator encounters a JSON value that invalidates the supplied schema, the parsing will be terminated immediately. This design is especially useful for parsing large JSON files. +Unlike most JSON Schema validator implementations, RapidJSON provides a SAX-based schema validator. Therefore, you can parse a JSON from a stream while validating it on the fly. If the validator encounters a JSON value that invalidates the supplied schema, the parsing will be terminated immediately. This design is especially useful for parsing large JSON files. ### DOM parsing @@ -111,7 +111,7 @@ if (!reader.Parse(stream, validator)) { } ~~~ -This is exactly the method used in [schemavalidator](example/schemavalidator/schemavalidator.cpp) example. The distinct advantage is low memory usage, no matter how big the JSON was (the memory usage depends on the complexity of the schema). +This is exactly the method used in the [schemavalidator](example/schemavalidator/schemavalidator.cpp) example. The distinct advantage is low memory usage, no matter how big the JSON was (the memory usage depends on the complexity of the schema). If you need to handle the SAX events further, then you need to use the template class `GenericSchemaValidator` to set the output handler of the validator: @@ -213,7 +213,7 @@ For C++11 compiler, it is also possible to use the `std::regex` by defining `RAP ## Performance -Most C++ JSON libraries have not yet supporting JSON Schema. So we tried to evaluate the performance of RapidJSON's JSON Schema validator according to [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark), which tests 11 JavaScript libraries running on Node.js. +Most C++ JSON libraries do not yet support JSON Schema. So we tried to evaluate the performance of RapidJSON's JSON Schema validator according to [json-schema-benchmark](https://github.com/ebdrup/json-schema-benchmark), which tests 11 JavaScript libraries running on Node.js. That benchmark runs validations on [JSON Schema Test Suite](https://github.com/json-schema/JSON-Schema-Test-Suite), in which some test suites and tests are excluded. We made the same benchmarking procedure in [`schematest.cpp`](test/perftest/schematest.cpp).