Add GenericArray helper class with range-based for

This commit is contained in:
Milo Yip 2016-02-13 19:06:03 +08:00
parent c6946fd2c1
commit 59309b5dd2
3 changed files with 157 additions and 22 deletions

View File

@ -483,6 +483,9 @@ struct TypeHelper<ValueType, std::basic_string<typename ValueType::Ch> > {
} // namespace internal } // namespace internal
template <typename ValueType>
class GenericArray;
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
// GenericValue // GenericValue
@ -510,6 +513,7 @@ public:
typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array.
typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array. typedef const GenericValue* ConstValueIterator; //!< Constant value iterator for iterating in array.
typedef GenericValue<Encoding, Allocator> ValueType; //!< Value type of itself. typedef GenericValue<Encoding, Allocator> ValueType; //!< Value type of itself.
typedef GenericArray<ValueType> ArrayType;
//!@name Constructors and destructor. //!@name Constructors and destructor.
//@{ //@{
@ -1556,6 +1560,9 @@ public:
return pos; return pos;
} }
ArrayType GetArray() { RAPIDJSON_ASSERT(IsArray()); return ArrayType(*this); }
const ArrayType GetArray() const { RAPIDJSON_ASSERT(IsArray()); return ArrayType(*this); }
//@} //@}
//!@name Number //!@name Number
@ -1653,9 +1660,12 @@ public:
//@} //@}
//!@name Array
//@{
//! Templated version for checking whether this value is type T. //! 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<Ch> \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<Ch>
*/ */
template <typename T> template <typename T>
bool Is() const { return internal::TypeHelper<ValueType, T>::Is(*this); } bool Is() const { return internal::TypeHelper<ValueType, T>::Is(*this); }
@ -1669,6 +1679,8 @@ public:
template<typename T> template<typename T>
ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper<ValueType, T>::Set(*this, data, allocator); } ValueType& Set(const T& data, AllocatorType& allocator) { return internal::TypeHelper<ValueType, T>::Set(*this, data, allocator); }
//@}
//! Generate events of this value to a Handler. //! Generate events of this value to a Handler.
/*! This function adopts the GoF visitor pattern. /*! This function adopts the GoF visitor pattern.
Typical usage is to output this JSON value as JSON text via Writer, which is a Handler. Typical usage is to output this JSON value as JSON text via Writer, which is a Handler.
@ -2278,6 +2290,65 @@ GenericValue<Encoding,Allocator>::GenericValue(const GenericValue<Encoding,Sourc
} }
} }
//! 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 <typename ValueType>
class GenericArray {
public:
typedef typename ValueType::ValueIterator ValueIterator;
typedef typename ValueType::ConstValueIterator ConstValueIterator;
typedef typename ValueType::AllocatorType AllocatorType;
typedef typename ValueType::StringRefType StringRefType;
template <typename, typename>
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 <typename T>
RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr<internal::IsPointer<T>, internal::IsGenericValue<T> >), (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<ValueType*>(&value)) {}
ValueType* ptr_;
};
typedef GenericArray<Value> Array;
RAPIDJSON_NAMESPACE_END RAPIDJSON_NAMESPACE_END
#ifdef _MSC_VER #ifdef _MSC_VER

View File

@ -530,6 +530,17 @@ RAPIDJSON_NAMESPACE_END
#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 #define RAPIDJSON_HAS_CXX11_TYPETRAITS 0
#endif #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 //!@endcond
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////

View File

@ -824,25 +824,9 @@ TEST(Value, SetStringNullException) {
EXPECT_THROW(v.SetString(0, 0), AssertException); EXPECT_THROW(v.SetString(0, 0), AssertException);
} }
TEST(Value, Array) { template <typename T, typename Allocator>
Value x(kArrayType); void TestArray(T& x, Allocator& allocator) {
const Value& y = x; const T& 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());
// PushBack() // PushBack()
Value v; Value v;
@ -889,7 +873,7 @@ TEST(Value, Array) {
#endif #endif
// iterator // iterator
Value::ValueIterator itr = x.Begin(); typename T::ValueIterator itr = x.Begin();
EXPECT_TRUE(itr != x.End()); EXPECT_TRUE(itr != x.End());
EXPECT_TRUE(itr->IsNull()); EXPECT_TRUE(itr->IsNull());
++itr; ++itr;
@ -908,7 +892,7 @@ TEST(Value, Array) {
EXPECT_STREQ("foo", itr->GetString()); EXPECT_STREQ("foo", itr->GetString());
// const iterator // const iterator
Value::ConstValueIterator citr = y.Begin(); typename T::ConstValueIterator citr = y.Begin();
EXPECT_TRUE(citr != y.End()); EXPECT_TRUE(citr != y.End());
EXPECT_TRUE(citr->IsNull()); EXPECT_TRUE(citr->IsNull());
++citr; ++citr;
@ -994,6 +978,29 @@ TEST(Value, Array) {
EXPECT_EQ(i + removeCount, x[static_cast<SizeType>(i)][0].GetUint()); EXPECT_EQ(i + removeCount, x[static_cast<SizeType>(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. // Working in gcc without C++11, but VS2013 cannot compile. To be diagnosed.
// http://en.wikipedia.org/wiki/Erase-remove_idiom // http://en.wikipedia.org/wiki/Erase-remove_idiom
@ -1017,6 +1024,52 @@ TEST(Value, Array) {
EXPECT_TRUE(z.Empty()); 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<const Value&>(x).GetArray())
EXPECT_EQ(i++, v.GetInt());
EXPECT_EQ(i, 10);
}
// Array a = x.GetArray();
// Array ca = const_cast<const Value&>(x).GetArray();
}
#endif
TEST(Value, Object) { TEST(Value, Object) {
Value x(kObjectType); Value x(kObjectType);
const Value& y = x; // const version const Value& y = x; // const version