From a9add7bd2ad6ed0711c4059c4e9dbce2913e498c Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Tue, 26 Aug 2014 20:06:25 +0200 Subject: [PATCH 01/50] GenericValue: add non-template overload for operator!= As reported in #113, recent versions of Clang complain about ambiguous overloads for some comparison operator instantiations, especially if the deduced template type is a GenericValue. Add an explicit, non-templated version for now (which is a better match). This only solves part of the problem, as comparisons between * GenericValue & GenericDocument * GenericValue with different SourceAllocator types will still cause ambiguities. --- include/rapidjson/document.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 965cefe..5d8e079 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -685,6 +685,11 @@ public: */ template RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer, bool) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + //! Not-equal-to operator + /*! \return !(*this == rhs) + */ + bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + //! Not-equal-to operator with arbitrary types /*! \return !(*this == rhs) */ From d63a40a05d5f56ed42df92d94ddc90b9eecc6752 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Wed, 27 Aug 2014 15:38:19 +0200 Subject: [PATCH 02/50] valuetest: extended value comparison tests Prepare equalto_operator tests to test comparisons between * GenericValue and GenericDocument * GenericValue with different SourceAllocator types Both combinations currently fail due to ambiguities with the templated operators on several compilers. --- test/unittest/valuetest.cpp | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index c9241d4..ce8fce7 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -106,15 +106,35 @@ TEST(Value, equalto_operator) { TestEqual(x["pi"], 3.14); // Test operator==() +#ifdef RAPIDJSON_COMPARE_DIFFERENT_ALLOCATORS + CrtAllocator crtAllocator; + GenericValue, CrtAllocator> y; + GenericDocument, CrtAllocator> z(&crtAllocator); + CrtAllocator& yAllocator = crtAllocator; +#else + Value::AllocatorType& yAllocator = allocator; Value y; - y.CopyFrom(x, allocator); + Document z; +#endif // RAPIDJSON_COMPARE_DIFFERENT_ALLOCATORS + y.CopyFrom(x, yAllocator); + z.CopyFrom(y, z.GetAllocator()); + TestEqual(x, y); + TestEqual(y, z); + TestEqual(z, x); // Swapping member order should be fine. - y.RemoveMember("t"); + EXPECT_TRUE(y.RemoveMember("t")); TestUnequal(x, y); - y.AddMember("t", Value(true).Move(), allocator); + TestUnequal(z, y); + EXPECT_TRUE(z.RemoveMember("t")); + TestUnequal(x, z); + TestEqual(y, z); + y.AddMember("t", true, yAllocator); + z.AddMember("t", true, z.GetAllocator()); TestEqual(x, y); + TestEqual(y, z); + TestEqual(z, x); } template From b56641106b87de1fc513d08bd6febf207ea7addf Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 30 Aug 2014 12:52:36 +0200 Subject: [PATCH 03/50] improve EN/DISABLEIF macros to support complex templated parameters --- build/Doxyfile | 4 +++- include/rapidjson/document.h | 8 ++++---- include/rapidjson/internal/meta.h | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/build/Doxyfile b/build/Doxyfile index 65de7e9..6827b51 100644 --- a/build/Doxyfile +++ b/build/Doxyfile @@ -1993,7 +1993,9 @@ INCLUDE_FILE_PATTERNS = PREDEFINED = \ RAPIDJSON_DOXYGEN_RUNNING \ - RAPIDJSON_DISABLEIF_RETURN(cond,returntype)=returntype + RAPIDJSON_REMOVEFPTR_(x)=x \ + RAPIDJSON_ENABLEIF_RETURN(cond,returntype)="RAPIDJSON_REMOVEFPTR_ returntype" \ + RAPIDJSON_DISABLEIF_RETURN(cond,returntype)="RAPIDJSON_REMOVEFPTR_ returntype" # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 5d8e079..43f85f5 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -588,7 +588,7 @@ public: use \ref SetBool() instead. */ template - RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) operator=(T value) { GenericValue v(value); return *this = v; @@ -683,7 +683,7 @@ public: //! Equal-to operator with primitive types /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false */ - template RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer, bool) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + template RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } //! Not-equal-to operator /*! \return !(*this == rhs) @@ -934,7 +934,7 @@ public: \note Amortized Constant time complexity. */ template - RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) AddMember(StringRefType name, T value, Allocator& allocator) { GenericValue n(name); GenericValue v(value); @@ -1147,7 +1147,7 @@ int z = a[0u].GetInt(); // This works too. \note Amortized constant time complexity. */ template - RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) PushBack(T value, Allocator& allocator) { GenericValue v(value); return PushBack(v, allocator); diff --git a/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h index b19a9f4..91e20dc 100644 --- a/include/rapidjson/internal/meta.h +++ b/include/rapidjson/internal/meta.h @@ -66,6 +66,9 @@ struct IsMoreConst { }; }; +////////////////////////////////////////////////////////////////////////// +// EnableIf / DisableIf +// template struct EnableIfCond; template struct EnableIfCond { typedef T Type; }; template struct EnableIfCond { /* empty */ }; @@ -92,8 +95,19 @@ template struct RemoveSfinaeFptr { typedef typename ::rapidjson::internal::EnableIf \ ::Type * = NULL +#define RAPIDJSON_DISABLEIF(cond) \ + typename ::rapidjson::internal::DisableIf \ + ::Type * = NULL + +#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ + typename ::rapidjson::internal::EnableIf \ + ::Type + #define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ - typename ::rapidjson::internal::DisableIf::Type + typename ::rapidjson::internal::DisableIf \ + ::Type } // namespace internal } // namespace rapidjson From ed282b8141d65adaf8e92c7cfb691076b28b0e59 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 30 Aug 2014 13:10:56 +0200 Subject: [PATCH 04/50] meta.h: simplify meta programming helpers Some (older) compilers have problems with compile-time constants. This commit simplifies the implementation of the helper classes in order to improve compiler support, especially be removing the need of partial template specialisation. No functional changes. --- include/rapidjson/internal/meta.h | 82 ++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 29 deletions(-) diff --git a/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h index 91e20dc..068a37d 100644 --- a/include/rapidjson/internal/meta.h +++ b/include/rapidjson/internal/meta.h @@ -30,51 +30,75 @@ RAPIDJSON_DIAG_OFF(effc++) namespace rapidjson { namespace internal { -template struct IntegralC { enum { Value = N }; }; -template struct BoolType : IntegralC {}; -struct TrueType : BoolType {}; -struct FalseType : BoolType {}; +/////////////////////////////////////////////////////////////////////////////// +// BoolType, TrueType, FalseType +// +template struct BoolType { + static const bool Value = Cond; + typedef BoolType Type; +}; +typedef BoolType TrueType; +typedef BoolType FalseType; + + +/////////////////////////////////////////////////////////////////////////////// +// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr +// + +template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; +template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; +template struct SelectIfCond : SelectIfImpl::template Apply {}; +template struct SelectIf : SelectIfCond {}; + +template struct AndExprCond : FalseType {}; +template <> struct AndExprCond : TrueType {}; +template struct OrExprCond : TrueType {}; +template <> struct OrExprCond : FalseType {}; + +template struct BoolExpr : SelectIf::Type {}; +template struct NotExpr : SelectIf::Type {}; +template struct AndExpr : AndExprCond::Type {}; +template struct OrExpr : OrExprCond::Type {}; + + +/////////////////////////////////////////////////////////////////////////////// +// AddConst, MaybeAddConst, RemoveConst template struct AddConst { typedef const T Type; }; +template struct MaybeAddConst : SelectIfCond {}; template struct RemoveConst { typedef T Type; }; template struct RemoveConst { typedef T Type; }; -template struct SelectIfCond; -template struct SelectIfCond { typedef T1 Type; }; -template struct SelectIfCond { typedef T2 Type; }; -template -struct SelectIf : SelectIfCond {}; - -template -struct MaybeAddConst : SelectIfCond {}; - -template struct IsSame : FalseType {}; -template struct IsSame : TrueType {}; +/////////////////////////////////////////////////////////////////////////////// +// IsSame, IsConst, IsMoreConst, IsPointer +// +template struct IsSame : FalseType {}; +template struct IsSame : TrueType {}; template struct IsConst : FalseType {}; template struct IsConst : TrueType {}; +template +struct IsMoreConst + : AndExpr::Type, typename RemoveConst::Type>, + BoolType::Value >= IsConst::Value> >::Type {}; + template struct IsPointer : FalseType {}; template struct IsPointer : TrueType {}; -template -struct IsMoreConst { - enum { Value = - ( IsSame< typename RemoveConst::Type, typename RemoveConst::Type>::Value - && ( IsConst::Value >= IsConst::Value ) ) }; }; + ////////////////////////////////////////////////////////////////////////// // EnableIf / DisableIf // -template struct EnableIfCond; -template struct EnableIfCond { typedef T Type; }; +template struct EnableIfCond { typedef T Type; }; template struct EnableIfCond { /* empty */ }; -template -struct DisableIfCond : EnableIfCond {}; +template struct DisableIfCond { typedef T Type; }; +template struct DisableIfCond { /* empty */ }; template struct EnableIf : EnableIfCond {}; @@ -83,13 +107,13 @@ template struct DisableIf : DisableIfCond {}; // SFINAE helpers -struct SfinaeResultTag {}; -template struct RemoveSfinaeFptr {}; -template struct RemoveSfinaeFptr { typedef T Type; }; +struct SfinaeTag {}; +template struct RemoveSfinaeTag; +template struct RemoveSfinaeTag { typedef T Type; }; #define RAPIDJSON_REMOVEFPTR_(type) \ - typename ::rapidjson::internal::RemoveSfinaeFptr \ - < ::rapidjson::internal::SfinaeResultTag&(*) type>::Type + typename ::rapidjson::internal::RemoveSfinaeTag \ + < ::rapidjson::internal::SfinaeTag&(*) type>::Type #define RAPIDJSON_ENABLEIF(cond) \ typename ::rapidjson::internal::EnableIf \ From f076faca3d1c086789d30ae67505c6f2e696d328 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 30 Aug 2014 13:14:07 +0200 Subject: [PATCH 05/50] meta.h: add IsBaseOf meta function In order to match GenericValue and its derived classes for the SFINAE-implementation of some of the operators/functions, this meta-function matches all types equal to or derived from a given class. See std::is_base_of available in C++11. Define RAPIDJSON_HAS_CXX_TYPETRAITS to use the C++11 implementation. --- include/rapidjson/internal/meta.h | 41 ++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h index 068a37d..bcff1a5 100644 --- a/include/rapidjson/internal/meta.h +++ b/include/rapidjson/internal/meta.h @@ -25,6 +25,14 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif +#if defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(6334) +#endif + +#ifdef RAPIDJSON_HAS_CXX11_TYPETRAITS +#include +#endif //@cond RAPIDJSON_INTERNAL namespace rapidjson { @@ -87,9 +95,40 @@ struct IsMoreConst template struct IsPointer : FalseType {}; template struct IsPointer : TrueType {}; +/////////////////////////////////////////////////////////////////////////////// +// IsBaseOf +// +#ifdef RAPIDJSON_HAS_CXX11_TYPETRAITS + +template struct IsBaseOf + : BoolType< ::std::is_base_of::value> {}; + +#else // simplified version adopted from Boost + +template struct IsBaseOfImpl { + RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); + RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); + + typedef char (&Yes)[1]; + typedef char (&No) [2]; + + template + static Yes Check(const D*, T); + static No Check(const B*, int); + + struct Host { + operator const B*() const; + operator const D*(); }; + + enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; }; +template struct IsBaseOf + : OrExpr, BoolExpr > >::Type {}; + +#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS + ////////////////////////////////////////////////////////////////////////// // EnableIf / DisableIf @@ -137,7 +176,7 @@ template struct RemoveSfinaeTag { typedef T Type; } // namespace rapidjson //@endcond -#ifdef __GNUC__ +#if defined(__GNUC__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif From fdd7a763865efdd2e5ca3b1998dd3de87cd56b75 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 30 Aug 2014 16:01:06 +0200 Subject: [PATCH 06/50] GenericValue: use IsBaseOf to support comparisons between Value/Document This finally fixes #113. --- include/rapidjson/document.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 43f85f5..83e984a 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -683,7 +683,7 @@ public: //! Equal-to operator with primitive types /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false */ - template RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsBaseOf >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } //! Not-equal-to operator /*! \return !(*this == rhs) @@ -693,17 +693,17 @@ public: //! Not-equal-to operator with arbitrary types /*! \return !(*this == rhs) */ - template bool operator!=(const T& rhs) const { return !(*this == rhs); } + template RAPIDJSON_DISABLEIF_RETURN((internal::IsBaseOf), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } //! Equal-to operator with arbitrary types (symmetric version) /*! \return (rhs == lhs) */ - template friend bool operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsBaseOf), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } //! Not-Equal-to operator with arbitrary types (symmetric version) /*! \return !(rhs == lhs) */ - template friend bool operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsBaseOf), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } //@} //!@name Type From 804b7fcb2f12419d0900a20970c35ef3a1abd60d Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 30 Aug 2014 16:39:15 +0200 Subject: [PATCH 07/50] GenericValue: add static assert to catch broken SFINAE Before applying the simplifications in ed282b814, the SFINAE check for the GenericValue(bool) constructor has been broken in MSVC 2005. Add a static assert as a safe-guard against future reappearance of this problem. --- include/rapidjson/document.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 83e984a..cd0ca22 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -458,7 +458,10 @@ public: #else explicit GenericValue(bool b) #endif - : data_(), flags_(b ? kTrueFlag : kFalseFlag) {} + : data_(), flags_(b ? kTrueFlag : kFalseFlag) { + // safe-guard against failing SFINAE + RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + } //! Constructor for int value. explicit GenericValue(int i) : data_(), flags_(kNumberIntFlag) { From e294ce6f7a32c5823460eb0160254c21140454e8 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 30 Aug 2014 18:34:49 +0200 Subject: [PATCH 08/50] GenericValue: accept different allocators for read-only access Several GenericValue functions take a const-reference to another GenericValue only. These functions can safely accept values with a different allocator type. The following functions are therefore extended by a SourceAllocator template parameter in this commit: - operator==/!=/[] - HasMember, FindMember, RemoveMember - StringEqual --- include/rapidjson/document.h | 41 ++++++++++++++++++++++-------------- 1 file changed, 25 insertions(+), 16 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 92c7316..8adfd7d 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -444,7 +444,7 @@ public: \see CopyFrom() */ template< typename SourceAllocator > - GenericValue(const GenericValue& rhs, Allocator & allocator); + GenericValue(const GenericValue& rhs, Allocator & allocator); //! Constructor for boolean value. /*! \param b Boolean value @@ -603,10 +603,10 @@ public: \param allocator Allocator to use for copying */ template - GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { + GenericValue& CopyFrom(const GenericValue& rhs, Allocator& allocator) { RAPIDJSON_ASSERT((void*)this != (void const*)&rhs); this->~GenericValue(); - new (this) GenericValue(rhs,allocator); + new (this) GenericValue(rhs, allocator); return *this; } @@ -635,7 +635,9 @@ public: \note If an object contains duplicated named member, comparing equality with any object is always \c false. \note Linear time complexity (number of all values in the subtree and total lengths of all strings). */ - bool operator==(const GenericValue& rhs) const { + template + bool operator==(const GenericValue& rhs) const { + typedef GenericValue RhsType; if (GetType() != rhs.GetType()) return false; @@ -644,7 +646,7 @@ public: if (data_.o.size != rhs.data_.o.size) return false; for (ConstMemberIterator lhsMemberItr = MemberBegin(); lhsMemberItr != MemberEnd(); ++lhsMemberItr) { - ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); + typename RhsType::ConstMemberIterator rhsMemberItr = rhs.FindMember(lhsMemberItr->name); if (rhsMemberItr == rhs.MemberEnd() || lhsMemberItr->value != rhsMemberItr->value) return false; } @@ -690,7 +692,8 @@ public: //! Not-equal-to operator /*! \return !(*this == rhs) */ - bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + template + bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } //! Not-equal-to operator with arbitrary types /*! \return !(*this == rhs) @@ -774,7 +777,8 @@ public: // This version is faster because it does not need a StrLen(). // It can also handle string with null character. - GenericValue& operator[](const GenericValue& name) { + template + GenericValue& operator[](const GenericValue& name) { MemberIterator member = FindMember(name); if (member != MemberEnd()) return member->value; @@ -784,7 +788,8 @@ public: return NullValue; } } - const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } + template + const GenericValue& operator[](const GenericValue& name) const { return const_cast(*this)[name]; } //! Const member iterator /*! \pre IsObject() == true */ @@ -818,7 +823,8 @@ public: \note It is better to use FindMember() directly if you need the obtain the value as well. \note Linear time complexity. */ - bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } + template + bool HasMember(const GenericValue& name) const { return FindMember(name) != MemberEnd(); } //! Find member by name. /*! @@ -852,7 +858,8 @@ public: \c std::map, this has been changed to MemberEnd() now. \note Linear time complexity. */ - MemberIterator FindMember(const GenericValue& name) { + template + MemberIterator FindMember(const GenericValue& name) { RAPIDJSON_ASSERT(IsObject()); RAPIDJSON_ASSERT(name.IsString()); MemberIterator member = MemberBegin(); @@ -861,7 +868,7 @@ public: break; return member; } - ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } + template ConstMemberIterator FindMember(const GenericValue& name) const { return const_cast(*this).FindMember(name); } //! Add a member (name-value pair) to the object. /*! \param name A string value as name of member. @@ -971,7 +978,8 @@ public: return RemoveMember(n); } - bool RemoveMember(const GenericValue& name) { + template + bool RemoveMember(const GenericValue& name) { MemberIterator m = FindMember(name); if (m != MemberEnd()) { RemoveMember(m); @@ -1352,8 +1360,8 @@ int z = a[0u].GetInt(); // This works too. } private: - template - friend class GenericDocument; + template friend class GenericValue; + template friend class GenericDocument; enum { kBoolFlag = 0x100, @@ -1477,7 +1485,8 @@ private: rhs.flags_ = kNullFlag; } - bool StringEqual(const GenericValue& rhs) const { + template + bool StringEqual(const GenericValue& rhs) const { RAPIDJSON_ASSERT(IsString()); RAPIDJSON_ASSERT(rhs.IsString()); return data_.s.length == rhs.data_.s.length && @@ -1671,7 +1680,7 @@ private: // callers of the following private Handler functions template friend class GenericReader; // for parsing - friend class GenericValue; // for deep copying + template friend class GenericValue; // for deep copying // Implementation of Handler bool Null() { new (stack_.template Push()) ValueType(); return true; } From 1da078433111558ec3a9e7cbdb96f5f8fb26c436 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 30 Aug 2014 13:22:04 +0200 Subject: [PATCH 09/50] GenericValue: add and use IsGenericValue meta function This commit adds an IsGenericValue meta function to match arbitrary instantiations of the GenericValue template (or derived classes). This meta function is used in the SFINAE-checks to avoid matching the generic APIs (operator=,==,!=; AddMember, PushBack) for instances of the main template. This avoids ambiguities with the GenericValue overloads. --- include/rapidjson/document.h | 28 ++++++++++++++++++++++------ include/rapidjson/internal/meta.h | 2 ++ 2 files changed, 24 insertions(+), 6 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 8adfd7d..e206d84 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -382,6 +382,22 @@ inline GenericStringRef StringRef(const std::basic_string& s } #endif +/////////////////////////////////////////////////////////////////////////////// +// GenericValue type traits +namespace internal { + +template +struct IsGenericValueImpl : FalseType {}; + +// select candidates according to nested encoding and allocator types +template struct IsGenericValueImpl::Type, typename Void::Type> + : IsBaseOf, T>::Type {}; + +// helper to match arbitrary GenericValue instantiations, including derived classes +template struct IsGenericValue : IsGenericValueImpl::Type {}; + +} // namespace internal + /////////////////////////////////////////////////////////////////////////////// // GenericValue @@ -687,7 +703,7 @@ public: //! Equal-to operator with primitive types /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false */ - template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsBaseOf >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsGenericValue >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } //! Not-equal-to operator /*! \return !(*this == rhs) @@ -698,17 +714,17 @@ public: //! Not-equal-to operator with arbitrary types /*! \return !(*this == rhs) */ - template RAPIDJSON_DISABLEIF_RETURN((internal::IsBaseOf), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } + template RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } //! Equal-to operator with arbitrary types (symmetric version) /*! \return (rhs == lhs) */ - template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsBaseOf), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } //! Not-Equal-to operator with arbitrary types (symmetric version) /*! \return !(rhs == lhs) */ - template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsBaseOf), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsGenericValue), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } //@} //!@name Type @@ -949,7 +965,7 @@ public: \note Amortized Constant time complexity. */ template - RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) AddMember(StringRefType name, T value, Allocator& allocator) { GenericValue n(name); GenericValue v(value); @@ -1174,7 +1190,7 @@ int z = a[0u].GetInt(); // This works too. \note Amortized constant time complexity. */ template - RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) + RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr, internal::IsGenericValue >), (GenericValue&)) PushBack(T value, Allocator& allocator) { GenericValue v(value); return PushBack(v, allocator); diff --git a/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h index bcff1a5..578b670 100644 --- a/include/rapidjson/internal/meta.h +++ b/include/rapidjson/internal/meta.h @@ -38,6 +38,8 @@ RAPIDJSON_DIAG_OFF(6334) namespace rapidjson { namespace internal { +// Helper to wrap/convert arbitrary types to void, useful for arbitrary type matching +template struct Void { typedef void Type; }; /////////////////////////////////////////////////////////////////////////////// // BoolType, TrueType, FalseType From a40dcb95254a14d6117c5bcc0a9bbf58cee55ae8 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 30 Aug 2014 18:26:57 +0200 Subject: [PATCH 10/50] valuetest: always test comparisons with different allocators --- test/unittest/valuetest.cpp | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 9324813..d57f943 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -105,20 +105,12 @@ TEST(Value, equalto_operator) { TestEqual(x["i"], 123); TestEqual(x["pi"], 3.14); - // Test operator==() -#ifdef RAPIDJSON_COMPARE_DIFFERENT_ALLOCATORS + // Test operator==() (including different allocators) CrtAllocator crtAllocator; GenericValue, CrtAllocator> y; GenericDocument, CrtAllocator> z(&crtAllocator); - CrtAllocator& yAllocator = crtAllocator; -#else - Value::AllocatorType& yAllocator = allocator; - Value y; - Document z; -#endif // RAPIDJSON_COMPARE_DIFFERENT_ALLOCATORS - y.CopyFrom(x, yAllocator); + y.CopyFrom(x, crtAllocator); z.CopyFrom(y, z.GetAllocator()); - TestEqual(x, y); TestEqual(y, z); TestEqual(z, x); @@ -130,7 +122,7 @@ TEST(Value, equalto_operator) { EXPECT_TRUE(z.RemoveMember("t")); TestUnequal(x, z); TestEqual(y, z); - y.AddMember("t", true, yAllocator); + y.AddMember("t", true, crtAllocator); z.AddMember("t", true, z.GetAllocator()); TestEqual(x, y); TestEqual(y, z); From f8db47377916f66bfb899c56f71f9de275a5ab51 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 30 Aug 2014 18:51:43 +0200 Subject: [PATCH 11/50] GenericValue: add explicit operator!=(const Ch*) MSVC'2005 needs an explicit overload for operator!=(const Ch*) after the addition of the IsGenericValue SFINAE restrictions. --- include/rapidjson/document.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index e206d84..1eb506c 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -711,6 +711,9 @@ public: template bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + //! Not-equal-to operator with const C-string pointer + bool operator!=(const Ch* rhs) const { return !(*this == rhs); } + //! Not-equal-to operator with arbitrary types /*! \return !(*this == rhs) */ From c59750576159dc11855ee684b732201a7f9f0dd3 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 31 Aug 2014 10:27:26 +0200 Subject: [PATCH 12/50] valuetest: test member operations with different allocators --- test/unittest/valuetest.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index d57f943..9b2658f 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -830,10 +830,18 @@ TEST(Value, Object) { EXPECT_TRUE(x.HasMember(name)); EXPECT_TRUE(y.HasMember(name)); + GenericValue, CrtAllocator> othername("A"); + EXPECT_TRUE(x.HasMember(othername)); + EXPECT_TRUE(y.HasMember(othername)); + othername.SetString("C\0D"); + EXPECT_TRUE(x.HasMember(othername)); + EXPECT_TRUE(y.HasMember(othername)); + // operator[] EXPECT_STREQ("Apple", x["A"].GetString()); EXPECT_STREQ("Banana", x["B"].GetString()); EXPECT_STREQ("CherryD", x[C0D].GetString()); + EXPECT_STREQ("CherryD", x[othername].GetString()); // const operator[] EXPECT_STREQ("Apple", y["A"].GetString()); @@ -904,7 +912,7 @@ TEST(Value, Object) { x.RemoveMember("B"); EXPECT_FALSE(x.HasMember("B")); - x.RemoveMember(name); + x.RemoveMember(othername); EXPECT_FALSE(x.HasMember(name)); EXPECT_TRUE(x.MemberBegin() == x.MemberEnd()); From ffed1d67c1c7747e4331010de23295bad2f1557b Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 31 Aug 2014 10:29:59 +0200 Subject: [PATCH 13/50] valuetest: more testing of MemberCount --- test/unittest/valuetest.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 9b2658f..6705538 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -925,11 +925,14 @@ TEST(Value, Object) { for (int i = 0; i < 10; i++) x.AddMember(keys[i], Value(kArrayType).PushBack(i, allocator), allocator); + // MemberCount, iterator difference + EXPECT_EQ(x.MemberCount(), SizeType(x.MemberEnd() - x.MemberBegin())); + // Erase the first itr = x.EraseMember(x.MemberBegin()); EXPECT_FALSE(x.HasMember(keys[0])); EXPECT_EQ(x.MemberBegin(), itr); - EXPECT_EQ(9, x.MemberEnd() - x.MemberBegin()); + EXPECT_EQ(9u, x.MemberCount()); for (; itr != x.MemberEnd(); ++itr) { int i = (itr - x.MemberBegin()) + 1; EXPECT_STREQ(itr->name.GetString(), keys[i]); @@ -940,7 +943,7 @@ TEST(Value, Object) { itr = x.EraseMember(x.MemberEnd() - 1); EXPECT_FALSE(x.HasMember(keys[9])); EXPECT_EQ(x.MemberEnd(), itr); - EXPECT_EQ(8, x.MemberEnd() - x.MemberBegin()); + EXPECT_EQ(8u, x.MemberCount()); for (; itr != x.MemberEnd(); ++itr) { int i = (itr - x.MemberBegin()) + 1; EXPECT_STREQ(itr->name.GetString(), keys[i]); @@ -951,7 +954,7 @@ TEST(Value, Object) { itr = x.EraseMember(x.MemberBegin() + 4); EXPECT_FALSE(x.HasMember(keys[5])); EXPECT_EQ(x.MemberBegin() + 4, itr); - EXPECT_EQ(7, x.MemberEnd() - x.MemberBegin()); + EXPECT_EQ(7u, x.MemberCount()); for (; itr != x.MemberEnd(); ++itr) { int i = (itr - x.MemberBegin()); i += (i<4) ? 1 : 2; @@ -975,7 +978,7 @@ TEST(Value, Object) { EXPECT_EQ(x.MemberBegin() + first, itr); size_t removeCount = last - first; - EXPECT_EQ(n - removeCount, size_t(x.MemberEnd() - x.MemberBegin())); + EXPECT_EQ(n - removeCount, x.MemberCount()); for (unsigned i = 0; i < first; i++) EXPECT_EQ(i, x[keys[i]][0u].GetUint()); for (unsigned i = first; i < n - removeCount; i++) From a2a0d16167ffbb11ca83fdd2489b4ce21e4173fd Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 31 Aug 2014 11:08:33 +0200 Subject: [PATCH 14/50] unittest.h: simplify AssertException Some compilers warn about the missing initialisation of the std::exception base class of the AssertException helper. The simplest solution is to inherit from std::logic_error instead, which provides all of the required functionality already. --- test/unittest/unittest.h | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/test/unittest/unittest.h b/test/unittest/unittest.h index a476db7..6f5230a 100644 --- a/test/unittest/unittest.h +++ b/test/unittest/unittest.h @@ -89,15 +89,9 @@ inline FILE* TempFile(char *filename) { #pragma warning(disable : 4127) #endif -class AssertException : public std::exception { +class AssertException : public std::logic_error { public: - AssertException(const char* w) : what_(w) {} - AssertException(const AssertException& other) : what_(other.what_) {} - AssertException& operator=(const AssertException& rhs) { what_ = rhs.what_; return *this; } - virtual const char* what() const throw() { return what_; } - -private: - const char* what_; + AssertException(const char* w) : std::logic_error(w) {} }; #define RAPIDJSON_ASSERT(x) if (!(x)) throw AssertException(RAPIDJSON_STRINGIFY(x)) From 1beec85453448a7186a01a1bb16848d90afe6908 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 31 Aug 2014 12:25:00 +0200 Subject: [PATCH 15/50] GenericValue: add move constructor/move assignment When C++11 is enabled, several algorithms will fail, if GenericValue is neither copyable, nor movable. Cherry-picked from 8005b55. --- include/rapidjson/document.h | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 92c7316..044a8fd 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -416,6 +416,13 @@ public: //! Default constructor creates a null value. GenericValue() : data_(), flags_(kNullFlag) {} +#ifdef RAPIDJSON_CXX11 + //! Move constructor in C++11 + GenericValue(GenericValue&& rhs) : data_(rhs.data_), flags_(rhs.flags_) { + rhs.flags_ = kNullFlag; // give up contents + } +#endif + private: //! Copy constructor is not permitted. GenericValue(const GenericValue& rhs); @@ -567,6 +574,13 @@ public: return *this; } +#ifdef RAPIDJSON_CXX11 + //! Move assignment in C++11 + GenericValue& operator=(GenericValue&& rhs) { + return *this = rhs.Move(); + } +#endif + //! Assignment of constant string reference (no copy) /*! \param str Constant string reference to be assigned \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. From defc3aaf9b1bbcc21b03f9a7c4253785a94fb9d0 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 31 Aug 2014 15:04:45 +0200 Subject: [PATCH 16/50] always include "meta.h", as it is not only used for member iterators --- 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 044a8fd..8631c54 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -24,6 +24,7 @@ /*! \file document.h */ #include "reader.h" +#include "internal/meta.h" #include "internal/strfunc.h" #include // placement new @@ -56,7 +57,6 @@ RAPIDJSON_DIAG_OFF(effc++) #endif // RAPIDJSON_HAS_STDSTRING #ifndef RAPIDJSON_NOMEMBERITERATORCLASS -#include "internal/meta.h" #include // std::iterator, std::random_access_iterator_tag #endif From 5f6967b08396ce6a93962fd651644196ac15ce6b Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 31 Aug 2014 15:05:34 +0200 Subject: [PATCH 17/50] meta.h: disallow direct inclusion --- include/rapidjson/internal/meta.h | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h index bcff1a5..d83e1f1 100644 --- a/include/rapidjson/internal/meta.h +++ b/include/rapidjson/internal/meta.h @@ -21,6 +21,10 @@ #ifndef RAPIDJSON_INTERNAL_META_H_ #define RAPIDJSON_INTERNAL_META_H_ +#ifndef RAPIDJSON_RAPIDJSON_H_ +#error not yet included. Do not include this file directly. +#endif + #ifdef __GNUC__ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) From a16fe366edde17a4d2090d3b816530092b8b6c55 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 31 Aug 2014 15:08:22 +0200 Subject: [PATCH 18/50] rapidjson.h: add RAPIDJSON_GNUC as GCC version shortcut --- include/rapidjson/rapidjson.h | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index e2173a4..752744c 100644 --- a/include/rapidjson/rapidjson.h +++ b/include/rapidjson/rapidjson.h @@ -318,7 +318,12 @@ template struct StaticAssertTest {}; /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF -#if defined(__clang__) || (defined(__GNUC__) && RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) >= RAPIDJSON_VERSION_CODE(4,2,0)) +#if defined(__GNUC__) +#define RAPIDJSON_GNUC \ + RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) +#endif + +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0)) #define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x)) #define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x) @@ -326,7 +331,7 @@ template struct StaticAssertTest {}; RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x))) // push/pop support in Clang and GCC>=4.6 -#if defined(__clang__) || (defined(__GNUC__) && RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) >= RAPIDJSON_VERSION_CODE(4,6,0)) +#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) #define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push) #define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop) #else // GCC >= 4.2, < 4.6 From 5656078a38b07013b4a1e21c6b816f3239170c2c Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 31 Aug 2014 15:07:36 +0200 Subject: [PATCH 19/50] detect rvalue reference support (RAPIDJSON_HAS_CXX11_RVALUE_REFS) --- include/rapidjson/document.h | 4 ++-- include/rapidjson/internal/meta.h | 4 ++-- include/rapidjson/rapidjson.h | 20 ++++++++++++++++++++ 3 files changed, 24 insertions(+), 4 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 8631c54..1ce4ef2 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -416,7 +416,7 @@ public: //! Default constructor creates a null value. GenericValue() : data_(), flags_(kNullFlag) {} -#ifdef RAPIDJSON_CXX11 +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Move constructor in C++11 GenericValue(GenericValue&& rhs) : data_(rhs.data_), flags_(rhs.flags_) { rhs.flags_ = kNullFlag; // give up contents @@ -574,7 +574,7 @@ public: return *this; } -#ifdef RAPIDJSON_CXX11 +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Move assignment in C++11 GenericValue& operator=(GenericValue&& rhs) { return *this = rhs.Move(); diff --git a/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h index d83e1f1..3e6c931 100644 --- a/include/rapidjson/internal/meta.h +++ b/include/rapidjson/internal/meta.h @@ -34,7 +34,7 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(6334) #endif -#ifdef RAPIDJSON_HAS_CXX11_TYPETRAITS +#if RAPIDJSON_HAS_CXX11_TYPETRAITS #include #endif @@ -102,7 +102,7 @@ template struct IsPointer : TrueType {}; /////////////////////////////////////////////////////////////////////////////// // IsBaseOf // -#ifdef RAPIDJSON_HAS_CXX11_TYPETRAITS +#if RAPIDJSON_HAS_CXX11_TYPETRAITS template struct IsBaseOf : BoolType< ::std::is_base_of::value> {}; diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index 752744c..7328c4e 100644 --- a/include/rapidjson/rapidjson.h +++ b/include/rapidjson/rapidjson.h @@ -357,6 +357,26 @@ template struct StaticAssertTest {}; #endif // RAPIDJSON_DIAG_* +/////////////////////////////////////////////////////////////////////////////// +// C++11 features + +#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS __has_feature(cxx_rvalue_references) +#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_RVALUE_REFS 1 +#else +#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0 +#endif +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + +// no automatic detection, yet +#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS +#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 +#endif + //!@endcond /////////////////////////////////////////////////////////////////////////////// From c1c9ba7c59534905c37cb62d87bb946e76e00169 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 31 Aug 2014 15:09:18 +0200 Subject: [PATCH 20/50] always define RAPIDJSON_HAS_STDSTRING (default: 0) --- include/rapidjson/document.h | 14 ++++++++------ test/unittest/valuetest.cpp | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 1ce4ef2..5487918 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -39,15 +39,17 @@ RAPIDJSON_DIAG_OFF(effc++) /////////////////////////////////////////////////////////////////////////////// // RAPIDJSON_HAS_STDSTRING +#ifndef RAPIDJSON_HAS_STDSTRING #ifdef RAPIDJSON_DOXYGEN_RUNNING #define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation +#else +#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default #endif -#ifdef RAPIDJSON_HAS_STDSTRING /*! \def RAPIDJSON_HAS_STDSTRING \ingroup RAPIDJSON_CONFIG \brief Enable RapidJSON support for \c std::string - By defining this preprocessor symbol, several convenience functions for using + By defining this preprocessor symbol to \c 1, several convenience functions for using \ref rapidjson::GenericValue with \c std::string are enabled, especially for construction and comparison. @@ -363,7 +365,7 @@ inline GenericStringRef StringRef(const CharType* str, size_t length) return GenericStringRef(str, SizeType(length)); } -#ifdef RAPIDJSON_HAS_STDSTRING +#if RAPIDJSON_HAS_STDSTRING //! Mark a string object as constant string /*! Mark a string object (e.g. \c std::string) as a "string literal". This function can be used to avoid copying a string to be referenced as a @@ -524,7 +526,7 @@ public: //! Constructor for copy-string (i.e. do make a copy of string) GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } -#ifdef RAPIDJSON_HAS_STDSTRING +#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. */ @@ -689,7 +691,7 @@ public: //! Equal-to operator with const C-string pointer bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); } -#ifdef RAPIDJSON_HAS_STDSTRING +#if RAPIDJSON_HAS_STDSTRING //! Equal-to operator with string object /*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING. */ @@ -1303,7 +1305,7 @@ int z = a[0u].GetInt(); // This works too. */ GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } -#ifdef RAPIDJSON_HAS_STDSTRING +#if RAPIDJSON_HAS_STDSTRING //! Set this value as a string by copying from source string. /*! \param s source string. \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 9324813..53a196f 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -558,7 +558,7 @@ TEST(Value, String) { EXPECT_STREQ("World", w.GetString()); EXPECT_EQ(5u, w.GetStringLength()); -#ifdef RAPIDJSON_HAS_STDSTRING +#if RAPIDJSON_HAS_STDSTRING { std::string str = "Hello World"; str[5] = '\0'; From a9d2b751791430a48db51ea45526319e7e6c2878 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 31 Aug 2014 17:18:26 +0200 Subject: [PATCH 21/50] valuetest: avoid underscores in GoogleTest tests See https://code.google.com/p/googletest/wiki/FAQ#Why_should_not_test_case_names_and_test_names_contain_underscore --- test/unittest/valuetest.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 53a196f..7e54bbf 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -24,7 +24,7 @@ using namespace rapidjson; -TEST(Value, default_constructor) { +TEST(Value, DefaultConstructor) { Value x; EXPECT_EQ(kNullType, x.GetType()); EXPECT_TRUE(x.IsNull()); @@ -38,7 +38,7 @@ TEST(Value, default_constructor) { // Value y = x; //} -TEST(Value, assignment_operator) { +TEST(Value, AssignmentOperator) { Value x(1234); Value y; y = x; @@ -81,7 +81,7 @@ void TestUnequal(const A& a, const B& b) { EXPECT_TRUE (b != a); } -TEST(Value, equalto_operator) { +TEST(Value, EqualtoOperator) { Value::AllocatorType allocator; Value x(kObjectType); x.AddMember("hello", "world", allocator) From b5f9d6076596c5c2b14daa99c33fc01fa69b54e0 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 31 Aug 2014 17:23:15 +0200 Subject: [PATCH 22/50] GenericValue: add rvalue-ref overloads to AddMember/PushBack Directly allows temporary GenericValues as parameters: v.AddMember("foo", Value(s.c_str(),alloc), alloc); --- include/rapidjson/document.h | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 5487918..b6edb4a 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -911,6 +911,23 @@ public: return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) { + return AddMember(name, value, allocator); + } + GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) { + GenericValue n(name); + return AddMember(n, value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + + //! Add a member (name-value pair) to the object. /*! \param name A constant string reference as name of member. \param value Value of any type. @@ -1151,6 +1168,12 @@ int z = a[0u].GetInt(); // This works too. return *this; } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + GenericValue& PushBack(GenericValue&& value, Allocator& allocator) { + return PushBack(value, allocator); + } +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + //! Append a constant string reference at the end of the array. /*! \param value Constant string reference to be appended. \param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator(). From 36031b1b6f12b38a0e92cd6fc8293f66725dc465 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 31 Aug 2014 17:19:36 +0200 Subject: [PATCH 23/50] valuetest: add tests for rvalue references, reenable erase/remove pattern --- test/unittest/valuetest.cpp | 77 +++++++++++++++++++++++++++++++++++-- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 7e54bbf..0023a7c 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -38,6 +38,31 @@ TEST(Value, DefaultConstructor) { // Value y = x; //} +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS +TEST(Value, MoveConstructor) { + typedef GenericValue, CrtAllocator> Value; + Value::AllocatorType allocator; + + Value x((Value(kArrayType))); + x.Reserve(4u, allocator); + x.PushBack(1, allocator).PushBack(2, allocator).PushBack(3, allocator).PushBack(4, allocator); + EXPECT_TRUE(x.IsArray()); + EXPECT_EQ(4u, x.Size()); + + // Value y(x); // should not compile + Value y(std::move(x)); + EXPECT_TRUE(x.IsNull()); + EXPECT_TRUE(y.IsArray()); + EXPECT_EQ(4u, y.Size()); + + // Value z = y; // should not compile + Value z = std::move(y); + EXPECT_TRUE(y.IsNull()); + EXPECT_TRUE(z.IsArray()); + EXPECT_EQ(4u, z.Size()); +} +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS + TEST(Value, AssignmentOperator) { Value x(1234); Value y; @@ -63,6 +88,22 @@ TEST(Value, AssignmentOperator) { y = StringRef(mstr); EXPECT_TRUE(y.IsString()); EXPECT_EQ(y.GetString(),mstr); + +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + // C++11 move assignment + x = Value("World"); + EXPECT_TRUE(x.IsString()); + EXPECT_STREQ("World", x.GetString()); + + x = std::move(y); + EXPECT_TRUE(y.IsNull()); + EXPECT_TRUE(x.IsString()); + EXPECT_EQ(x.GetString(), mstr); + + y = std::move(Value().SetInt(1234)); + EXPECT_TRUE(y.IsInt()); + EXPECT_EQ(1234, y); +#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS } template @@ -646,6 +687,21 @@ TEST(Value, Array) { EXPECT_TRUE(y[4u].IsString()); EXPECT_STREQ("foo", y[4u].GetString()); +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + // PushBack(GenericValue&&, Allocator&); + { + Value y(kArrayType); + y.PushBack(Value(true), allocator); + y.PushBack(std::move(Value(kArrayType).PushBack(Value(1), allocator).PushBack("foo", allocator)), allocator); + EXPECT_EQ(2u, y.Size()); + EXPECT_TRUE(y[0u].IsTrue()); + EXPECT_TRUE(y[1u].IsArray()); + EXPECT_EQ(2u, y[1u].Size()); + EXPECT_TRUE(y[1u][0u].IsInt()); + EXPECT_TRUE(y[1u][1u].IsString()); + } +#endif + // iterator Value::ValueIterator itr = x.Begin(); EXPECT_TRUE(itr != x.End()); @@ -754,7 +810,6 @@ TEST(Value, Array) { } // Working in gcc without C++11, but VS2013 cannot compile. To be diagnosed. -#if 0 // http://en.wikipedia.org/wiki/Erase-remove_idiom x.Clear(); for (int i = 0; i < 10; i++) @@ -763,11 +818,11 @@ TEST(Value, Array) { else x.PushBack(Value(kNullType).Move(), allocator); - x.Erase(std::remove(x.Begin(), x.End(), Value(kNullType)), x.End()); + const Value null(kNullType); + x.Erase(std::remove(x.Begin(), x.End(), null), x.End()); EXPECT_EQ(5u, x.Size()); for (int i = 0; i < 5; i++) EXPECT_EQ(i * 2, x[i]); -#endif // SetArray() Value z; @@ -821,6 +876,22 @@ TEST(Value, Object) { EXPECT_EQ(8u, o.MemberCount()); } +#if RAPIDJSON_HAS_CXX11_RVALUE_REFS + // AddMember(GenericValue&&, ...) variants + { + Value o(kObjectType); + o.AddMember(Value("true"), Value(true), allocator); + o.AddMember(Value("false"), Value(false).Move(), allocator); // value is lvalue ref + o.AddMember(Value("int").Move(), Value(-1), allocator); // name is lvalue ref + o.AddMember("uint", std::move(Value().SetUint(1u)), allocator); // name is literal, value is rvalue + EXPECT_TRUE(o["true"].GetBool()); + EXPECT_FALSE(o["false"].GetBool()); + EXPECT_EQ(-1, o["int"].GetInt()); + EXPECT_EQ(1u, o["uint"].GetUint()); + EXPECT_EQ(4u, o.MemberCount()); + } +#endif + // Tests a member with null character Value name; const Value C0D("C\0D", 3); From 5672d24651284693f2d0ee1ed139ea7b50f9108b Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 31 Aug 2014 17:42:18 +0200 Subject: [PATCH 24/50] GenericValue: add RAPIDJSON_NOEXCEPT Added basic detection of `noexcept` support for some compilers, added corresponding RAPIDJSON_NOEXCEPT annotations to * non-allocating constructors * (move) assignment * Swap --- build/Doxyfile | 3 ++- include/rapidjson/document.h | 32 ++++++++++++++++---------------- include/rapidjson/rapidjson.h | 16 ++++++++++++++++ 3 files changed, 34 insertions(+), 17 deletions(-) diff --git a/build/Doxyfile b/build/Doxyfile index 6827b51..e34a3ff 100644 --- a/build/Doxyfile +++ b/build/Doxyfile @@ -2004,7 +2004,8 @@ PREDEFINED = \ # definition found in the source code. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -EXPAND_AS_DEFINED = +EXPAND_AS_DEFINED = \ + RAPIDJSON_NOEXCEPT # If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will # remove all references to function-like macros that are alone on a line, have diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index b6edb4a..0918799 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -416,11 +416,11 @@ public: //@{ //! Default constructor creates a null value. - GenericValue() : data_(), flags_(kNullFlag) {} + GenericValue() RAPIDJSON_NOEXCEPT : data_(), flags_(kNullFlag) {} #if RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Move constructor in C++11 - GenericValue(GenericValue&& rhs) : data_(rhs.data_), flags_(rhs.flags_) { + GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_), flags_(rhs.flags_) { rhs.flags_ = kNullFlag; // give up contents } #endif @@ -436,7 +436,7 @@ public: \param type Type of the value. \note Default content for number is zero. */ - GenericValue(Type type) : data_(), flags_() { + GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_(), flags_() { static const unsigned defaultFlags[7] = { kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kConstStringFlag, kNumberAnyFlag @@ -463,9 +463,9 @@ public: */ #ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen template - explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) RAPIDJSON_NOEXCEPT #else - explicit GenericValue(bool b) + explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT #endif : data_(), flags_(b ? kTrueFlag : kFalseFlag) { // safe-guard against failing SFINAE @@ -473,21 +473,21 @@ public: } //! Constructor for int value. - explicit GenericValue(int i) : data_(), flags_(kNumberIntFlag) { + explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberIntFlag) { data_.n.i64 = i; if (i >= 0) flags_ |= kUintFlag | kUint64Flag; } //! Constructor for unsigned value. - explicit GenericValue(unsigned u) : data_(), flags_(kNumberUintFlag) { + explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUintFlag) { data_.n.u64 = u; if (!(u & 0x80000000)) flags_ |= kIntFlag | kInt64Flag; } //! Constructor for int64_t value. - explicit GenericValue(int64_t i64) : data_(), flags_(kNumberInt64Flag) { + explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberInt64Flag) { data_.n.i64 = i64; if (i64 >= 0) { flags_ |= kNumberUint64Flag; @@ -501,7 +501,7 @@ public: } //! Constructor for uint64_t value. - explicit GenericValue(uint64_t u64) : data_(), flags_(kNumberUint64Flag) { + explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUint64Flag) { data_.n.u64 = u64; if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000))) flags_ |= kInt64Flag; @@ -512,13 +512,13 @@ public: } //! Constructor for double value. - explicit GenericValue(double d) : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; } + explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; } //! Constructor for constant string (i.e. do not make a copy of string) - GenericValue(const Ch* s, SizeType length) : data_(), flags_() { SetStringRaw(StringRef(s, length)); } + GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(StringRef(s, length)); } //! Constructor for constant string (i.e. do not make a copy of string) - explicit GenericValue(StringRefType s) : data_(), flags_() { SetStringRaw(s); } + explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_(), flags_() { 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); } @@ -569,7 +569,7 @@ public: //! Assignment with move semantics. /*! \param rhs Source of the assignment. It will become a null value after assignment. */ - GenericValue& operator=(GenericValue& rhs) { + GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT { RAPIDJSON_ASSERT(this != &rhs); this->~GenericValue(); RawAssign(rhs); @@ -578,7 +578,7 @@ public: #if RAPIDJSON_HAS_CXX11_RVALUE_REFS //! Move assignment in C++11 - GenericValue& operator=(GenericValue&& rhs) { + GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT { return *this = rhs.Move(); } #endif @@ -588,7 +588,7 @@ public: \note This overload is needed to avoid clashes with the generic primitive type assignment overload below. \see GenericStringRef, operator=(T) */ - GenericValue& operator=(StringRefType str) { + GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT { GenericValue s(str); return *this = s; } @@ -631,7 +631,7 @@ public: \param other Another value. \note Constant complexity. */ - GenericValue& Swap(GenericValue& other) { + GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT { GenericValue temp; temp.RawAssign(*this); RawAssign(other); diff --git a/include/rapidjson/rapidjson.h b/include/rapidjson/rapidjson.h index 7328c4e..9aa9204 100644 --- a/include/rapidjson/rapidjson.h +++ b/include/rapidjson/rapidjson.h @@ -372,6 +372,22 @@ template struct StaticAssertTest {}; #endif #endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS +#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT +#if defined(__clang__) +#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept) +#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) +// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1 +#else +#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0 +#endif +#endif +#if RAPIDJSON_HAS_CXX11_NOEXCEPT +#define RAPIDJSON_NOEXCEPT noexcept +#else +#define RAPIDJSON_NOEXCEPT /* noexcept */ +#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT + // no automatic detection, yet #ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS #define RAPIDJSON_HAS_CXX11_TYPETRAITS 0 From 47c32eee6b485c93b048af7b18351f6b2d29283d Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 31 Aug 2014 18:55:08 +0200 Subject: [PATCH 25/50] valuetest: add test for Uint64 comparisons --- test/unittest/valuetest.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 9324813..e165452 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -135,6 +135,11 @@ TEST(Value, equalto_operator) { TestEqual(x, y); TestEqual(y, z); TestEqual(z, x); + + // Issue #129: compare Uint64 + x.SetUint64(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFF0)); + y.SetUint64(RAPIDJSON_UINT64_C2(0xFFFFFFFF, 0xFFFFFFFF)); + TestUnequal(x, y); } template From fd2ba0bc44c97147694cce5c5e965bb7d2f370d5 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 31 Aug 2014 18:58:14 +0200 Subject: [PATCH 26/50] GenericValue: fix comparison of (Ui|I)nt64 numbers Some 64-bit integers cannot be represented losslessly as a double. Due to a typo in the operator==, the comparison has been performed after a double conversion in too many cases. --- 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 92c7316..ec7f8a6 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -662,7 +662,7 @@ public: return StringEqual(rhs); case kNumberType: - if (IsDouble() || rhs.GetDouble()) + if (IsDouble() || rhs.IsDouble()) return GetDouble() == rhs.GetDouble(); // May convert one operand from integer to double. else return data_.n.u64 == rhs.data_.n.u64; From 56625bd9f01232eea6d5a3331bb41e1fb62a3473 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 31 Aug 2014 22:00:39 +0200 Subject: [PATCH 27/50] GenericValue: add some more RAPIDJSON_NOEXCEPT * Move() * RawAssign() * SetStringRaw() --- include/rapidjson/document.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 0918799..4bdb60f 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -641,7 +641,7 @@ public: //! Prepare Value for move semantics /*! \return *this */ - GenericValue& Move() { return *this; } + GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; } //@} //!@name Equal-to and not-equal-to operators @@ -1494,7 +1494,7 @@ private: } //! Initialize this value as constant string, without calling destructor. - void SetStringRaw(StringRefType s) { + void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT { flags_ = kConstStringFlag; data_.s.str = s; data_.s.length = s.length; @@ -1510,7 +1510,7 @@ private: } //! Assignment without calling destructor - void RawAssign(GenericValue& rhs) { + void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT { data_ = rhs.data_; flags_ = rhs.flags_; rhs.flags_ = kNullFlag; From 8ae2266c3baa40fc2fa1b38244aad6f00cf2c235 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sun, 31 Aug 2014 22:01:57 +0200 Subject: [PATCH 28/50] GenericStringRef: add NOEXCEPT, add ASSERT * constructor from array is RAPIDJSON_NOEXCEPT * constructor from plain pointer missed an assert --- 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 4bdb60f..d139ea7 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -279,7 +279,7 @@ struct GenericStringRef { GenericValue instead. */ template - GenericStringRef(const CharType (&str)[N]) + GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT : s(str), length(N-1) {} //! Explicitly create string reference from \c const character pointer @@ -302,7 +302,7 @@ struct GenericStringRef { GenericValue instead. */ explicit GenericStringRef(const CharType* str) - : s(str), length(internal::StrLen(str)){} + : s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != NULL); } //! Create constant string reference from pointer and length /*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue From f4f432801ad40551f286e0de23c139795c2f8c2e Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Mon, 1 Sep 2014 08:57:21 +0200 Subject: [PATCH 29/50] GenericValue: reduce growth factor for array/object reallocations As mentioned by @kosta-github in http://git.io/0gkYSg, the currently used growth factor of 2 is suboptimal for memory performance. An extensive discussion can be found at [1]. This patch reduces the array/object capacity growth factor to 1.5, as many C++ implementations have chosen to use. In order to avoid floating-point arithmetics for computing the new capacity, I did not add any customization parameter for the factor and used a shift+add instead. [1] https://github.com/facebook/folly/blob/master/folly/docs/FBVector.md --- 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 ec7f8a6..44a492b 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -885,7 +885,7 @@ public: } else { SizeType oldCapacity = o.capacity; - o.capacity *= 2; + o.capacity += (oldCapacity >> 1); // grow by factor 1.5 o.members = reinterpret_cast(allocator.Realloc(o.members, oldCapacity * sizeof(Member), o.capacity * sizeof(Member))); } } @@ -1130,7 +1130,7 @@ int z = a[0u].GetInt(); // This works too. GenericValue& PushBack(GenericValue& value, Allocator& allocator) { RAPIDJSON_ASSERT(IsArray()); if (data_.a.size >= data_.a.capacity) - Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : data_.a.capacity * 2, allocator); + Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity >> 1)), allocator); data_.a.elements[data_.a.size++].RawAssign(value); return *this; } From 3caa86c9239ee0f88a9d5bc4a0bc655b37041307 Mon Sep 17 00:00:00 2001 From: Kosta Date: Mon, 1 Sep 2014 11:11:26 +0200 Subject: [PATCH 30/50] short string optimization Since the payload (the `Data` union) of the current implementation of `GenericValue` is `12 bytes` (32 bit) or `16 bytes` (64 bit) it could store `UTF8`-encoded strings up to `10` or `14` chars plus the `terminating zero` character plus the string length: ``` C++ struct ShortString { enum { MaxSize = sizeof(GenericValue::String) / sizeof(Ch) - sizeof(unsigned char) }; Ch str[MaxSize]; unsigned char length; }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode ``` This is achieved by introducing additional `kInlineStrFlag` and `kShortStringFlag` flags. When setting a new string value in `SetStringRaw(s, alloc)` it is first checked if the string is short enough to fit into the `inline string buffer` and if so the given source string will be copied into the new `ShortString` target instead of allocating additional memory for it. --- include/rapidjson/document.h | 48 +++++++++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 12 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index ec7f8a6..edab98d 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1247,12 +1247,12 @@ int z = a[0u].GetInt(); // This works too. //!@name String //@{ - const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return data_.s.str; } + const Ch* GetString() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? data_.ss.str : data_.s.str); } //! 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 data_.s.length; } + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((flags_ & kInlineStrFlag) ? data_.ss.length : 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. @@ -1320,7 +1320,7 @@ int z = a[0u].GetInt(); // This works too. if (!handler.StartObject()) return false; for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { - if (!handler.String(m->name.data_.s.str, m->name.data_.s.length, (m->name.flags_ & kCopyFlag) != 0)) + if (!handler.String(m->name.GetString(), m->name.GetStringLength(), (m->name.flags_ & kCopyFlag) != 0)) return false; if (!m->value.Accept(handler)) return false; @@ -1336,7 +1336,7 @@ int z = a[0u].GetInt(); // This works too. return handler.EndArray(data_.a.size); case kStringType: - return handler.String(data_.s.str, data_.s.length, (flags_ & kCopyFlag) != 0); + return handler.String(data_.GetString(), data_.GetStringLength(), (flags_ & kCopyFlag) != 0); case kNumberType: if (IsInt()) return handler.Int(data_.n.i.i); @@ -1365,6 +1365,7 @@ private: kDoubleFlag = 0x4000, kStringFlag = 0x100000, kCopyFlag = 0x200000, + kInlineStrFlag = 0x400000, // Initial flags of different types. kNullFlag = kNullType, @@ -1378,6 +1379,7 @@ private: kNumberAnyFlag = kNumberType | kNumberFlag | kIntFlag | kInt64Flag | kUintFlag | kUint64Flag | kDoubleFlag, kConstStringFlag = kStringType | kStringFlag, kCopyStringFlag = kStringType | kStringFlag | kCopyFlag, + kShortStringFlag = kStringType | kStringFlag | kCopyFlag | kInlineStrFlag, kObjectFlag = kObjectType, kArrayFlag = kArrayType, @@ -1393,6 +1395,12 @@ private: unsigned hashcode; //!< reserved }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + struct ShortString { + enum { MaxSize = sizeof(String) / sizeof(Ch) - sizeof(unsigned char) }; + Ch str[MaxSize]; + unsigned char length; + }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + // By using proper binary layout, retrieval of different integer types do not need conversions. union Number { #if RAPIDJSON_ENDIAN == RAPIDJSON_LITTLEENDIAN @@ -1433,6 +1441,7 @@ private: union Data { String s; + ShortString ss; Number n; Object o; Array a; @@ -1463,11 +1472,19 @@ private: //! Initialize this value as copy string with initial data, without calling destructor. void SetStringRaw(StringRefType s, Allocator& allocator) { - flags_ = kCopyStringFlag; - data_.s.str = (Ch *)allocator.Malloc((s.length + 1) * sizeof(Ch)); - data_.s.length = s.length; - memcpy(const_cast(data_.s.str), s, s.length * sizeof(Ch)); - const_cast(data_.s.str)[s.length] = '\0'; + Ch* str = NULL; + if(s.length < ShortString::MaxSize) { + flags_ = kShortStringFlag; + data_.ss.length = s.length; + str = data_.ss.str; + } else { + flags_ = kCopyStringFlag; + data_.s.length = s.length; + str = (Ch *)allocator.Malloc((s.length + 1) * sizeof(Ch)); + data_.s.str = str; + } + memcpy(str, s, s.length * sizeof(Ch)); + str[s.length] = '\0'; } //! Assignment without calling destructor @@ -1480,9 +1497,16 @@ private: bool StringEqual(const GenericValue& rhs) const { RAPIDJSON_ASSERT(IsString()); RAPIDJSON_ASSERT(rhs.IsString()); - return data_.s.length == rhs.data_.s.length && - (data_.s.str == rhs.data_.s.str // fast path for constant string - || memcmp(data_.s.str, rhs.data_.s.str, sizeof(Ch) * data_.s.length) == 0); + + const SizeType len1 = (flags_ == kShortStringFlag) ? data_.ss.length : data_.s.length; + const SizeType len2 = (rhs.flags_ == kShortStringFlag) ? rhs.data_.ss.length : rhs.data_.s.length; + if(len1 != len2) { return false; } + + const Ch* const str1 = (flags_ == kShortStringFlag) ? data_.ss.str : data_.s.str; + const Ch* const str2 = (rhs.flags_ == kShortStringFlag) ? rhs.data_.ss.str : rhs.data_.s.str; + if(str1 == str2) { return true; } // fast path for constant string + + return (memcmp(str1, str2, sizeof(Ch) * len1) == 0); } Data data_; From b92d0ebd1b8b96c11a50d88215a6ccd38013ee6f Mon Sep 17 00:00:00 2001 From: Kosta Date: Mon, 1 Sep 2014 11:15:52 +0200 Subject: [PATCH 31/50] code cleanup for `StringEqual()` Instead of replicating the functionality of `GetString()` and `GetStringLength()` in `StringEqual()` it now calls these methods instead. --- include/rapidjson/document.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index edab98d..20e4f4c 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1498,12 +1498,12 @@ private: RAPIDJSON_ASSERT(IsString()); RAPIDJSON_ASSERT(rhs.IsString()); - const SizeType len1 = (flags_ == kShortStringFlag) ? data_.ss.length : data_.s.length; - const SizeType len2 = (rhs.flags_ == kShortStringFlag) ? rhs.data_.ss.length : rhs.data_.s.length; + const SizeType len1 = GetStringLength(); + const SizeType len2 = rhs.GetStringLength(); if(len1 != len2) { return false; } - const Ch* const str1 = (flags_ == kShortStringFlag) ? data_.ss.str : data_.s.str; - const Ch* const str2 = (rhs.flags_ == kShortStringFlag) ? rhs.data_.ss.str : rhs.data_.s.str; + const Ch* const str1 = GetString(); + const Ch* const str2 = rhs.GetString(); if(str1 == str2) { return true; } // fast path for constant string return (memcmp(str1, str2, sizeof(Ch) * len1) == 0); From 0d2761a59c48cd5694db7e53d3a092e97f028b09 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Mon, 1 Sep 2014 11:31:25 +0200 Subject: [PATCH 32/50] GenericValue: round up during capacity growth Suggested-by: @miloyip --- 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 44a492b..9615004 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -885,7 +885,7 @@ public: } else { SizeType oldCapacity = o.capacity; - o.capacity += (oldCapacity >> 1); // grow by factor 1.5 + 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))); } } @@ -1130,7 +1130,7 @@ int z = a[0u].GetInt(); // This works too. GenericValue& PushBack(GenericValue& value, Allocator& allocator) { RAPIDJSON_ASSERT(IsArray()); if (data_.a.size >= data_.a.capacity) - Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity >> 1)), allocator); + Reserve(data_.a.capacity == 0 ? kDefaultArrayCapacity : (data_.a.capacity + (data_.a.capacity + 1) / 2), allocator); data_.a.elements[data_.a.size++].RawAssign(value); return *this; } From d2a374b40c916127b3ce390c00bc126334e60982 Mon Sep 17 00:00:00 2001 From: Kosta Date: Mon, 1 Sep 2014 11:52:09 +0200 Subject: [PATCH 33/50] allow the short string optimization to store one more character The `ShortString` can represent zero-terminated strings up to `MaxSize` chars (excluding the terminating zero) and store a value to determine the length of the contained string in the last character `str[LenPos]` by storing `MaxSize - length` there. If the string to store has the maximal length of `MaxSize` (excluding the terminating zero) then `str[LenPos]` will store `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). --- include/rapidjson/document.h | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 20e4f4c..b33ea07 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1252,7 +1252,7 @@ int z = a[0u].GetInt(); // This works too. //! 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.length : data_.s.length); } + SizeType GetStringLength() const { RAPIDJSON_ASSERT(IsString()); return ((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. @@ -1395,10 +1395,21 @@ private: unsigned hashcode; //!< reserved }; // 12 bytes in 32-bit mode, 16 bytes in 64-bit mode + // implementation detail: ShortString can represent zero-terminated strings up to MaxSize chars + // (excluding the terminating zero) and store a value to determine the length of the contained + // string in the last character str[LenPos] by storing "MaxSize - length" there. If the string + // 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). struct ShortString { - enum { MaxSize = sizeof(String) / sizeof(Ch) - sizeof(unsigned char) }; - Ch str[MaxSize]; - unsigned char length; + enum { MaxChars = sizeof(String) / sizeof(Ch), MaxSize = MaxChars - 1, LenPos = MaxSize }; + Ch str[MaxChars]; + + inline static bool Usable(SizeType len) { return (MaxSize >= len); } + inline void SetLength(SizeType len) { str[LenPos] = (Ch)(MaxSize - len); } + inline SizeType GetLength() const { return (SizeType)(MaxSize - str[LenPos]); } }; // at most as many bytes as "String" above => 12 bytes in 32-bit mode, 16 bytes in 64-bit mode // By using proper binary layout, retrieval of different integer types do not need conversions. @@ -1473,9 +1484,9 @@ private: //! Initialize this value as copy string with initial data, without calling destructor. void SetStringRaw(StringRefType s, Allocator& allocator) { Ch* str = NULL; - if(s.length < ShortString::MaxSize) { + if(ShortString::Usable(s.length)) { flags_ = kShortStringFlag; - data_.ss.length = s.length; + data_.ss.SetLength(s.length); str = data_.ss.str; } else { flags_ = kCopyStringFlag; From 697cf407c2312a4ab51ae10b5d7a9b3ea116debf Mon Sep 17 00:00:00 2001 From: Kosta Date: Mon, 1 Sep 2014 12:01:25 +0200 Subject: [PATCH 34/50] fixed a compiler error not caught by VS2012... --- 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 b33ea07..9c3d9ac 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1336,7 +1336,7 @@ int z = a[0u].GetInt(); // This works too. return handler.EndArray(data_.a.size); case kStringType: - return handler.String(data_.GetString(), data_.GetStringLength(), (flags_ & kCopyFlag) != 0); + return handler.String(GetString(), GetStringLength(), (flags_ & kCopyFlag) != 0); case kNumberType: if (IsInt()) return handler.Int(data_.n.i.i); From 056d0dafe4854a8136d7dc17dfbb5e4551176ce9 Mon Sep 17 00:00:00 2001 From: Kosta Date: Mon, 1 Sep 2014 12:34:43 +0200 Subject: [PATCH 35/50] add unit test for testing edge cases of the `short string optimization` --- test/unittest/valuetest.cpp | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index e165452..f5d06c3 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -1084,3 +1084,26 @@ TEST(Document, CrtAllocator) { V a(kArrayType); a.PushBack(1, allocator); // Should not call destructor on uninitialized Value of newly allocated elements. } + +static void TestShortStringOptimization(const char* str) { + const int len = (int)strlen(str); + + rapidjson::Document doc; + rapidjson::Document::AllocatorType& allocator = doc.GetAllocator(); + rapidjson::Value objVal(rapidjson::kObjectType); + + objVal.AddMember(str, str, allocator); + EXPECT_TRUE(objVal.HasMember(str)); + + const rapidjson::Value& member = objVal[str]; + EXPECT_EQ(member.GetStringLength(), strlen(str)); +} + +TEST(Value, AllocateShortString) { + TestShortStringOptimization(""); // edge case: empty string + TestShortStringOptimization("12345678"); // regular case for short strings: 8 chars + TestShortStringOptimization("12345678901"); // edge case: 11 chars in 32-bit mode (=> short string) + TestShortStringOptimization("123456789012"); // edge case: 12 chars in 32-bit mode (=> regular string) + TestShortStringOptimization("123456789012345"); // edge case: 15 chars in 64-bit mode (=> short string) + TestShortStringOptimization("1234567890123456"); // edge case: 16 chars in 64-bit mode (=> regular string) +} From 88debcf02edadd8ad7c0a2a8b566bb40414541d0 Mon Sep 17 00:00:00 2001 From: Kosta Date: Mon, 1 Sep 2014 12:40:28 +0200 Subject: [PATCH 36/50] typo fixed for the unit test implementation --- 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 f5d06c3..8e3fe8f 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -1092,7 +1092,7 @@ static void TestShortStringOptimization(const char* str) { rapidjson::Document::AllocatorType& allocator = doc.GetAllocator(); rapidjson::Value objVal(rapidjson::kObjectType); - objVal.AddMember(str, str, allocator); + objVal.AddMember(str, len, allocator); EXPECT_TRUE(objVal.HasMember(str)); const rapidjson::Value& member = objVal[str]; From 609997565ca355cb0abad60542293e47beebac03 Mon Sep 17 00:00:00 2001 From: Kosta Date: Mon, 1 Sep 2014 12:46:04 +0200 Subject: [PATCH 37/50] unit test simplification for `short string optimization` --- test/unittest/valuetest.cpp | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 8e3fe8f..0df5f34 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -1089,14 +1089,11 @@ static void TestShortStringOptimization(const char* str) { const int len = (int)strlen(str); rapidjson::Document doc; - rapidjson::Document::AllocatorType& allocator = doc.GetAllocator(); - rapidjson::Value objVal(rapidjson::kObjectType); + rapidjson::Value val; + val.SetString(str, len, doc.GetAllocator()); - objVal.AddMember(str, len, allocator); - EXPECT_TRUE(objVal.HasMember(str)); - - const rapidjson::Value& member = objVal[str]; - EXPECT_EQ(member.GetStringLength(), strlen(str)); + EXPECT_EQ(val.GetStringLength(), len); + EXPECT_STREQ(val.GetString(), str); } TEST(Value, AllocateShortString) { From ba05ea52cf3930143de8aee681519f186dc58aaa Mon Sep 17 00:00:00 2001 From: Kosta Date: Mon, 1 Sep 2014 12:52:36 +0200 Subject: [PATCH 38/50] use `rapidjson::Value::SizeType` as the type for storing and comparing the string length --- 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 0df5f34..1f698e7 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -1086,7 +1086,7 @@ TEST(Document, CrtAllocator) { } static void TestShortStringOptimization(const char* str) { - const int len = (int)strlen(str); + const rapidjson::Value::SizeType len = (rapidjson::Value::SizeType)strlen(str); rapidjson::Document doc; rapidjson::Value val; From 08e81097eb0debb5b1f3b7159401ba69f67a30af Mon Sep 17 00:00:00 2001 From: Kosta Date: Mon, 1 Sep 2014 12:54:50 +0200 Subject: [PATCH 39/50] final fix for the unit test case... --- 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 1f698e7..7cc81dc 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -1086,7 +1086,7 @@ TEST(Document, CrtAllocator) { } static void TestShortStringOptimization(const char* str) { - const rapidjson::Value::SizeType len = (rapidjson::Value::SizeType)strlen(str); + const rapidjson::SizeType len = (rapidjson::SizeType)strlen(str); rapidjson::Document doc; rapidjson::Value val; From 57e1c875534eb2e2c183c62e4421ad1a0bf8bf73 Mon Sep 17 00:00:00 2001 From: Kosta Date: Thu, 4 Sep 2014 15:14:18 +0200 Subject: [PATCH 40/50] add `Key()` method to the `Handler` concept For more details see: https://github.com/miloyip/rapidjson/issues/132 This commit tries to minimize the required code changes and forwards the `Handler::Key()` calls to `Handler::String()` wherever possible in order to not break existing code; or at least not code deriving from `BaseReaderHandler` when implementing a custom `Handler`. --- include/rapidjson/document.h | 4 +++- include/rapidjson/reader.h | 40 ++++++++++++++++++++++++++++-------- 2 files changed, 34 insertions(+), 10 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 13acb38..310fc71 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1386,7 +1386,7 @@ int z = a[0u].GetInt(); // This works too. if (!handler.StartObject()) return false; for (ConstMemberIterator m = MemberBegin(); m != MemberEnd(); ++m) { - if (!handler.String(m->name.GetString(), m->name.GetStringLength(), (m->name.flags_ & kCopyFlag) != 0)) + if (!handler.Key(m->name.GetString(), m->name.GetStringLength(), (m->name.flags_ & kCopyFlag) != 0)) return false; if (!m->value.Accept(handler)) return false; @@ -1794,6 +1794,8 @@ private: bool StartObject() { new (stack_.template Push()) ValueType(kObjectType); return true; } + bool Key(const Ch* str, SizeType length, bool copy) { return String(str, length, copy); } + bool EndObject(SizeType memberCount) { typename ValueType::Member* members = stack_.template Pop(memberCount); stack_.template Top()->SetObjectRaw(members, (SizeType)memberCount, GetAllocator()); diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 701255f..f02dd16 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -152,6 +152,7 @@ concept Handler { bool Double(double d); bool String(const Ch* str, SizeType length, bool copy); bool StartObject(); + bool Key(const Ch* str, SizeType length, bool copy); bool EndObject(SizeType memberCount); bool StartArray(); bool EndArray(SizeType elementCount); @@ -181,6 +182,7 @@ struct BaseReaderHandler { bool Double(double) { return static_cast(*this).Default(); } bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } bool StartObject() { return static_cast(*this).Default(); } + bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } bool EndObject(SizeType) { return static_cast(*this).Default(); } bool StartArray() { return static_cast(*this).Default(); } bool EndArray(SizeType) { return static_cast(*this).Default(); } @@ -471,7 +473,7 @@ private: if (is.Peek() != '"') RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); - ParseString(is, handler); + ParseKey(is, handler); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; SkipWhitespace(is); @@ -576,7 +578,7 @@ private: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); } - // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). + // Helper function to parse four hexidecimal digits in \uXXXX in ParseStringOrKey(). template unsigned ParseHex4(InputStream& is) { unsigned codepoint = 0; @@ -616,8 +618,8 @@ private: }; // Parse string and generate String event. Different code paths for kParseInsituFlag. - template - void ParseString(InputStream& is, Handler& handler) { + template + void ParseStringOrKey(InputStream& is, Handler& handler) { internal::StreamLocalCopy copy(is); InputStream& s(copy.s); @@ -627,18 +629,38 @@ private: RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; size_t length = s.PutEnd(head) - 1; RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); - if (!handler.String((typename TargetEncoding::Ch*)head, SizeType(length), false)) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + if (isKey) { + if (!handler.Key((typename TargetEncoding::Ch*)head, SizeType(length), false)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } else { + if (!handler.String((typename TargetEncoding::Ch*)head, SizeType(length), false)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } } else { StackStream stackStream(stack_); ParseStringToStream(s, stackStream); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (!handler.String(stack_.template Pop(stackStream.length_), stackStream.length_ - 1, true)) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + if (isKey) { + if (!handler.Key(stack_.template Pop(stackStream.length_), stackStream.length_ - 1, true)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } else { + if (!handler.String(stack_.template Pop(stackStream.length_), stackStream.length_ - 1, true)) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); + } } } + template + void ParseKey(InputStream& is, Handler& handler) { + return ParseStringOrKey(is, handler); + } + + template + void ParseString(InputStream& is, Handler& handler) { + return ParseStringOrKey(is, handler); + } + // Parse string to an output is // This function handles the prefix/suffix double quotes, escaping, and optional encoding validation. template @@ -1194,7 +1216,7 @@ private: } case IterativeParsingMemberKeyState: - ParseString(is, handler); + ParseKey(is, handler); if (HasParseError()) return IterativeParsingErrorState; else From 17b1a14d622f396838fd52ba8d4fac2407a37e75 Mon Sep 17 00:00:00 2001 From: Kosta Date: Thu, 4 Sep 2014 15:20:05 +0200 Subject: [PATCH 41/50] add `Key()` method to `Writer` and `PrettyWriter` --- include/rapidjson/prettywriter.h | 2 ++ include/rapidjson/writer.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h index f89be1b..2fc9b75 100644 --- a/include/rapidjson/prettywriter.h +++ b/include/rapidjson/prettywriter.h @@ -88,6 +88,8 @@ public: return Base::WriteStartObject(); } + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + bool EndObject(SizeType memberCount = 0) { (void)memberCount; RAPIDJSON_ASSERT(Base::level_stack_.GetSize() >= sizeof(typename Base::Level)); diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 73e4aff..2f46b66 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -131,6 +131,8 @@ public: return WriteStartObject(); } + bool Key(const Ch* str, SizeType length, bool copy = false) { return String(str, length, copy); } + bool EndObject(SizeType memberCount = 0) { (void)memberCount; RAPIDJSON_ASSERT(level_stack_.GetSize() >= sizeof(Level)); From 23b3d24355cf8c30593c747ad97bd4ee5a65e83c Mon Sep 17 00:00:00 2001 From: Kosta Date: Thu, 4 Sep 2014 15:24:52 +0200 Subject: [PATCH 42/50] add `Key()` to IterativeParsingReaderHandler` in `readertest.cpp` --- test/unittest/readertest.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 09560d8..4c3011c 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -848,6 +848,8 @@ struct IterativeParsingReaderHandler { bool StartObject() { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STARTOBJECT; return true; } + bool Key (const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } + bool EndObject(SizeType c) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_ENDOBJECT; From 0f4071046f73466efae6b0808e364de5e59ceb62 Mon Sep 17 00:00:00 2001 From: Kosta Date: Thu, 4 Sep 2014 15:30:44 +0200 Subject: [PATCH 43/50] fix `simplereader.cpp` and the `Key()->String()` forwarding in `BaseReaderHandler` --- example/simplereader/simplereader.cpp | 1 + include/rapidjson/reader.h | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/example/simplereader/simplereader.cpp b/example/simplereader/simplereader.cpp index 9914253..83b3f1d 100644 --- a/example/simplereader/simplereader.cpp +++ b/example/simplereader/simplereader.cpp @@ -17,6 +17,7 @@ struct MyHandler { return true; } bool StartObject() { cout << "StartObject()" << endl; return true; } + bool Key(const char* str, SizeType length, bool copy) { return String(str, length, copy); } bool EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; return true; } bool StartArray() { cout << "StartArray()" << endl; return true; } bool EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; return true; } diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index f02dd16..92fd781 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -182,7 +182,7 @@ struct BaseReaderHandler { bool Double(double) { return static_cast(*this).Default(); } bool String(const Ch*, SizeType, bool) { return static_cast(*this).Default(); } bool StartObject() { return static_cast(*this).Default(); } - bool Key(const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } + bool Key(const Ch* str, SizeType len, bool copy) { return static_cast(*this).String(str, len, copy); } bool EndObject(SizeType) { return static_cast(*this).Default(); } bool StartArray() { return static_cast(*this).Default(); } bool EndArray(SizeType) { return static_cast(*this).Default(); } From c8da4d86fc691429dd42bfbf2db27e67d06c4d3f Mon Sep 17 00:00:00 2001 From: Kosta Date: Thu, 4 Sep 2014 15:35:01 +0200 Subject: [PATCH 44/50] add `Key()->String()` forwarding to the `capitalize` example --- example/capitalize/capitalize.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/example/capitalize/capitalize.cpp b/example/capitalize/capitalize.cpp index b8d6086..dd94d24 100644 --- a/example/capitalize/capitalize.cpp +++ b/example/capitalize/capitalize.cpp @@ -31,6 +31,7 @@ struct CapitalizeFilter { return out_.String(&buffer_.front(), length, true); // true = output handler need to copy the string } bool StartObject() { return out_.StartObject(); } + bool Key(const char* str, SizeType length, bool copy) { return String(str, length, copy); } bool EndObject(SizeType memberCount) { return out_.EndObject(memberCount); } bool StartArray() { return out_.StartArray(); } bool EndArray(SizeType elementCount) { return out_.EndArray(elementCount); } From 8b2faff9818aca4be10589d31088ea1c1bae3bb2 Mon Sep 17 00:00:00 2001 From: Kosta Date: Thu, 4 Sep 2014 15:51:09 +0200 Subject: [PATCH 45/50] enhance `IterativeParsingReaderHandler` to check for `Key()` events --- test/unittest/readertest.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index 4c3011c..d7c8edb 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -819,9 +819,10 @@ struct IterativeParsingReaderHandler { const static int LOG_DOUBLE = -7; const static int LOG_STRING = -8; const static int LOG_STARTOBJECT = -9; - const static int LOG_ENDOBJECT = -10; - const static int LOG_STARTARRAY = -11; - const static int LOG_ENDARRAY = -12; + const static int LOG_KEY = -10; + const static int LOG_ENDOBJECT = -11; + const static int LOG_STARTARRAY = -12; + const static int LOG_ENDARRAY = -13; const static size_t LogCapacity = 256; int Logs[LogCapacity]; @@ -848,7 +849,7 @@ struct IterativeParsingReaderHandler { bool StartObject() { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STARTOBJECT; return true; } - bool Key (const Ch* str, SizeType len, bool copy) { return String(str, len, copy); } + bool Key (const Ch* str, SizeType len, bool copy) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_KEY; return true; } bool EndObject(SizeType c) { RAPIDJSON_ASSERT(LogCount < LogCapacity); @@ -882,7 +883,7 @@ TEST(Reader, IterativeParsing_General) { handler.LOG_STARTARRAY, handler.LOG_INT, handler.LOG_STARTOBJECT, - handler.LOG_STRING, + handler.LOG_KEY, handler.LOG_STARTARRAY, handler.LOG_INT, handler.LOG_INT, @@ -920,7 +921,7 @@ TEST(Reader, IterativeParsing_Count) { handler.LOG_STARTOBJECT, handler.LOG_ENDOBJECT, 0, handler.LOG_STARTOBJECT, - handler.LOG_STRING, + handler.LOG_KEY, handler.LOG_INT, handler.LOG_ENDOBJECT, 1, handler.LOG_STARTARRAY, From 25f4892aa1552259112c6a2bbbc93181f1287a43 Mon Sep 17 00:00:00 2001 From: Kosta Date: Thu, 4 Sep 2014 15:56:21 +0200 Subject: [PATCH 46/50] removed unused argument names --- test/unittest/readertest.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unittest/readertest.cpp b/test/unittest/readertest.cpp index d7c8edb..b42d832 100644 --- a/test/unittest/readertest.cpp +++ b/test/unittest/readertest.cpp @@ -849,7 +849,7 @@ struct IterativeParsingReaderHandler { bool StartObject() { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_STARTOBJECT; return true; } - bool Key (const Ch* str, SizeType len, bool copy) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_KEY; return true; } + bool Key (const Ch*, SizeType, bool) { RAPIDJSON_ASSERT(LogCount < LogCapacity); Logs[LogCount++] = LOG_KEY; return true; } bool EndObject(SizeType c) { RAPIDJSON_ASSERT(LogCount < LogCapacity); From 4a71dc6d584ac90870906de9a477af24eb5e1bde Mon Sep 17 00:00:00 2001 From: Kosta Date: Thu, 4 Sep 2014 16:54:01 +0200 Subject: [PATCH 47/50] reunified `ParseKey`, `ParseString`, and `ParseStringOrKey` again --- include/rapidjson/reader.h | 42 +++++++++++++------------------------- 1 file changed, 14 insertions(+), 28 deletions(-) diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 92fd781..d93c8c2 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -473,7 +473,7 @@ private: if (is.Peek() != '"') RAPIDJSON_PARSE_ERROR(kParseErrorObjectMissName, is.Tell()); - ParseKey(is, handler); + ParseString(is, handler, true); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; SkipWhitespace(is); @@ -578,7 +578,7 @@ private: RAPIDJSON_PARSE_ERROR(kParseErrorValueInvalid, is.Tell() - 1); } - // Helper function to parse four hexidecimal digits in \uXXXX in ParseStringOrKey(). + // Helper function to parse four hexidecimal digits in \uXXXX in ParseString(). template unsigned ParseHex4(InputStream& is) { unsigned codepoint = 0; @@ -618,47 +618,33 @@ private: }; // Parse string and generate String event. Different code paths for kParseInsituFlag. - template - void ParseStringOrKey(InputStream& is, Handler& handler) { + template + void ParseString(InputStream& is, Handler& handler, bool isKey = false) { internal::StreamLocalCopy copy(is); InputStream& s(copy.s); + const typename TargetEncoding::Ch* str = NULL; + SizeType len = 0; + if (parseFlags & kParseInsituFlag) { typename InputStream::Ch *head = s.PutBegin(); ParseStringToStream(s, s); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; size_t length = s.PutEnd(head) - 1; RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); - if (isKey) { - if (!handler.Key((typename TargetEncoding::Ch*)head, SizeType(length), false)) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); - } else { - if (!handler.String((typename TargetEncoding::Ch*)head, SizeType(length), false)) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); - } + str = (const typename TargetEncoding::Ch*)head; + len = SizeType(length); } else { StackStream stackStream(stack_); ParseStringToStream(s, stackStream); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - if (isKey) { - if (!handler.Key(stack_.template Pop(stackStream.length_), stackStream.length_ - 1, true)) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); - } else { - if (!handler.String(stack_.template Pop(stackStream.length_), stackStream.length_ - 1, true)) - RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); - } + str = stack_.template Pop(stackStream.length_); + len = stackStream.length_ - 1; } - } - template - void ParseKey(InputStream& is, Handler& handler) { - return ParseStringOrKey(is, handler); - } - - template - void ParseString(InputStream& is, Handler& handler) { - return ParseStringOrKey(is, handler); + if(!(isKey ? handler.Key(str, len, false) : handler.String(str, len, false))) + RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); } // Parse string to an output is @@ -1216,7 +1202,7 @@ private: } case IterativeParsingMemberKeyState: - ParseKey(is, handler); + ParseString(is, handler, true); if (HasParseError()) return IterativeParsingErrorState; else From e70494bc00a60e47be06cb625889b09fe63342ea Mon Sep 17 00:00:00 2001 From: Kosta Date: Thu, 4 Sep 2014 17:29:52 +0200 Subject: [PATCH 48/50] fix for `Reader::ParseString()` implementation plus some minor code cleanups and additions --- example/capitalize/capitalize.cpp | 3 +-- example/simplereader/simplereader.cpp | 7 +++++-- include/rapidjson/prettywriter.h | 1 + include/rapidjson/reader.h | 14 ++++++-------- include/rapidjson/writer.h | 1 + 5 files changed, 14 insertions(+), 12 deletions(-) diff --git a/example/capitalize/capitalize.cpp b/example/capitalize/capitalize.cpp index dd94d24..230bc28 100644 --- a/example/capitalize/capitalize.cpp +++ b/example/capitalize/capitalize.cpp @@ -14,7 +14,7 @@ using namespace rapidjson; template -struct CapitalizeFilter { +struct CapitalizeFilter : public BaseReaderHandler, OutputHandler> { CapitalizeFilter(OutputHandler& out) : out_(out), buffer_() {} bool Null() { return out_.Null(); } @@ -31,7 +31,6 @@ struct CapitalizeFilter { return out_.String(&buffer_.front(), length, true); // true = output handler need to copy the string } bool StartObject() { return out_.StartObject(); } - bool Key(const char* str, SizeType length, bool copy) { return String(str, length, copy); } bool EndObject(SizeType memberCount) { return out_.EndObject(memberCount); } bool StartArray() { return out_.StartArray(); } bool EndArray(SizeType elementCount) { return out_.EndArray(elementCount); } diff --git a/example/simplereader/simplereader.cpp b/example/simplereader/simplereader.cpp index 83b3f1d..b47d9b2 100644 --- a/example/simplereader/simplereader.cpp +++ b/example/simplereader/simplereader.cpp @@ -4,7 +4,7 @@ using namespace rapidjson; using namespace std; -struct MyHandler { +struct MyHandler : public BaseReaderHandler, MyHandler> { bool Null() { cout << "Null()" << endl; return true; } bool Bool(bool b) { cout << "Bool(" << boolalpha << b << ")" << endl; return true; } bool Int(int i) { cout << "Int(" << i << ")" << endl; return true; } @@ -17,7 +17,10 @@ struct MyHandler { return true; } bool StartObject() { cout << "StartObject()" << endl; return true; } - bool Key(const char* str, SizeType length, bool copy) { return String(str, length, copy); } + bool Key(const char* str, SizeType length, bool copy) { + cout << "Key(" << str << ", " << length << ", " << boolalpha << copy << ")" << endl; + return true; + } bool EndObject(SizeType memberCount) { cout << "EndObject(" << memberCount << ")" << endl; return true; } bool StartArray() { cout << "StartArray()" << endl; return true; } bool EndArray(SizeType elementCount) { cout << "EndArray(" << elementCount << ")" << endl; return true; } diff --git a/include/rapidjson/prettywriter.h b/include/rapidjson/prettywriter.h index 2fc9b75..4eac8d7 100644 --- a/include/rapidjson/prettywriter.h +++ b/include/rapidjson/prettywriter.h @@ -137,6 +137,7 @@ public: //! Simpler but slower overload. bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } //@} protected: diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index d93c8c2..e853bea 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -623,27 +623,25 @@ private: internal::StreamLocalCopy copy(is); InputStream& s(copy.s); - const typename TargetEncoding::Ch* str = NULL; - SizeType len = 0; - + bool success = false; if (parseFlags & kParseInsituFlag) { typename InputStream::Ch *head = s.PutBegin(); ParseStringToStream(s, s); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; size_t length = s.PutEnd(head) - 1; RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); - str = (const typename TargetEncoding::Ch*)head; - len = SizeType(length); + const typename TargetEncoding::Ch* const str = (const typename TargetEncoding::Ch*)head; + success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); } else { StackStream stackStream(stack_); ParseStringToStream(s, stackStream); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - str = stack_.template Pop(stackStream.length_); - len = stackStream.length_ - 1; + const typename TargetEncoding::Ch* const str = stack_.template Pop(stackStream.length_); + success = (isKey ? handler.Key(str, stackStream.length_ - 1, false) : handler.String(str, stackStream.length_ - 1, false)); } - if(!(isKey ? handler.Key(str, len, false) : handler.String(str, len, false))) + if(!success) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); } diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 2f46b66..fb6601e 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -167,6 +167,7 @@ public: //! Simpler but slower overload. bool String(const Ch* str) { return String(str, internal::StrLen(str)); } + bool Key(const Ch* str) { return Key(str, internal::StrLen(str)); } //@} From 00ac1024eee997815f422f67055415cc1b3ccb3b Mon Sep 17 00:00:00 2001 From: Kosta Date: Thu, 4 Sep 2014 17:37:13 +0200 Subject: [PATCH 49/50] more changes --- example/simplereader/simplereader.cpp | 2 +- include/rapidjson/reader.h | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/example/simplereader/simplereader.cpp b/example/simplereader/simplereader.cpp index b47d9b2..edbdb63 100644 --- a/example/simplereader/simplereader.cpp +++ b/example/simplereader/simplereader.cpp @@ -4,7 +4,7 @@ using namespace rapidjson; using namespace std; -struct MyHandler : public BaseReaderHandler, MyHandler> { +struct MyHandler { bool Null() { cout << "Null()" << endl; return true; } bool Bool(bool b) { cout << "Bool(" << boolalpha << b << ")" << endl; return true; } bool Int(int i) { cout << "Int(" << i << ")" << endl; return true; } diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index e853bea..885fe22 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -637,8 +637,10 @@ private: StackStream stackStream(stack_); ParseStringToStream(s, stackStream); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - const typename TargetEncoding::Ch* const str = stack_.template Pop(stackStream.length_); - success = (isKey ? handler.Key(str, stackStream.length_ - 1, false) : handler.String(str, stackStream.length_ - 1, false)); + success = (isKey + ? handler.Key( stack_.template Pop(stackStream.length_), stackStream.length_ - 1, false) + : handler.String(stack_.template Pop(stackStream.length_), stackStream.length_ - 1, false) + ); } if(!success) From f0d9ab4ec93034f66485c70f851b8872c8d6fde0 Mon Sep 17 00:00:00 2001 From: Kosta Date: Thu, 4 Sep 2014 18:00:05 +0200 Subject: [PATCH 50/50] finally fixing `Reader::ParseString()` It was a copy-n-paste error for the last argument of `Key()` and `String()`... --- example/capitalize/capitalize.cpp | 3 ++- include/rapidjson/reader.h | 11 ++++------- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/example/capitalize/capitalize.cpp b/example/capitalize/capitalize.cpp index 230bc28..dd94d24 100644 --- a/example/capitalize/capitalize.cpp +++ b/example/capitalize/capitalize.cpp @@ -14,7 +14,7 @@ using namespace rapidjson; template -struct CapitalizeFilter : public BaseReaderHandler, OutputHandler> { +struct CapitalizeFilter { CapitalizeFilter(OutputHandler& out) : out_(out), buffer_() {} bool Null() { return out_.Null(); } @@ -31,6 +31,7 @@ struct CapitalizeFilter : public BaseReaderHandler, OutputHandler> { return out_.String(&buffer_.front(), length, true); // true = output handler need to copy the string } bool StartObject() { return out_.StartObject(); } + bool Key(const char* str, SizeType length, bool copy) { return String(str, length, copy); } bool EndObject(SizeType memberCount) { return out_.EndObject(memberCount); } bool StartArray() { return out_.StartArray(); } bool EndArray(SizeType elementCount) { return out_.EndArray(elementCount); } diff --git a/include/rapidjson/reader.h b/include/rapidjson/reader.h index 885fe22..f41ba2f 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -630,20 +630,17 @@ private: RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; size_t length = s.PutEnd(head) - 1; RAPIDJSON_ASSERT(length <= 0xFFFFFFFF); - const typename TargetEncoding::Ch* const str = (const typename TargetEncoding::Ch*)head; + const typename TargetEncoding::Ch* const str = (typename TargetEncoding::Ch*)head; success = (isKey ? handler.Key(str, SizeType(length), false) : handler.String(str, SizeType(length), false)); } else { StackStream stackStream(stack_); ParseStringToStream(s, stackStream); RAPIDJSON_PARSE_ERROR_EARLY_RETURN_VOID; - success = (isKey - ? handler.Key( stack_.template Pop(stackStream.length_), stackStream.length_ - 1, false) - : handler.String(stack_.template Pop(stackStream.length_), stackStream.length_ - 1, false) - ); + const typename TargetEncoding::Ch* const str = stack_.template Pop(stackStream.length_); + success = (isKey ? handler.Key(str, stackStream.length_ - 1, true) : handler.String(str, stackStream.length_ - 1, true)); } - - if(!success) + if (!success) RAPIDJSON_PARSE_ERROR(kParseErrorTermination, s.Tell()); }