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 965cefee2..5d8e079ad 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -685,6 +685,11 @@ class GenericValue { */ 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 c9241d44e..ce8fce74d 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 65de7e944..6827b51d1 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 5d8e079ad..43f85f515 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -588,7 +588,7 @@ class GenericValue { 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 @@ class GenericValue { //! 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 @@ class GenericValue { \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 b19a9f4b7..91e20dc59 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 91e20dc59..068a37d0e 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 068a37d0e..bcff1a506 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 43f85f515..83e984a12 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -683,7 +683,7 @@ class GenericValue { //! 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 @@ class GenericValue { //! 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 83e984a12..cd0ca2267 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -458,7 +458,10 @@ class GenericValue { #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 92c73161c..8adfd7d7d 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -444,7 +444,7 @@ class GenericValue { \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 @@ class GenericValue { \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 @@ class GenericValue { \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 @@ class GenericValue { 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 @@ class GenericValue { //! 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 @@ class GenericValue { // 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 @@ class GenericValue { 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 @@ class GenericValue { \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 @@ class GenericValue { \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 @@ class GenericValue { 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 @@ class GenericValue { 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 @@ int z = a[0u].GetInt(); // This works too. 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 @@ class GenericDocument : public GenericValue { // 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 8adfd7d7d..e206d8462 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 @@ class GenericValue { //! 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 @@ class GenericValue { //! 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 @@ class GenericValue { \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 bcff1a506..578b67025 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 932481348..d57f943ea 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 e206d8462..1eb506cbd 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -711,6 +711,9 @@ class GenericValue { 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 d57f943ea..9b2658f75 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 9b2658f75..670553824 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 a476db721..6f5230a8e 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 92c73161c..044a8fd0a 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -416,6 +416,13 @@ class GenericValue { //! 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 @@ class GenericValue { 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 044a8fd0a..8631c5444 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 bcff1a506..d83e1f1cb 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 e2173a44f..752744c0f 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 8631c5444..1ce4ef221 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -416,7 +416,7 @@ class GenericValue { //! 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 @@ class GenericValue { 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 d83e1f1cb..3e6c93136 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 752744c0f..7328c4e19 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 1ce4ef221..5487918d3 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 @@ class GenericValue { //! 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 @@ class GenericValue { //! 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 932481348..53a196f22 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 53a196f22..7e54bbf81 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 5487918d3..b6edb4ab3 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -911,6 +911,23 @@ class GenericValue { 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 7e54bbf81..0023a7cfe 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 6827b51d1..e34a3ffe9 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 b6edb4ab3..091879908 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -416,11 +416,11 @@ class GenericValue { //@{ //! 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 @@ class GenericValue { \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 @@ class GenericValue { */ #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 @@ class GenericValue { } //! 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 @@ class GenericValue { } //! 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 @@ class GenericValue { } //! 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 @@ class GenericValue { //! 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 @@ class GenericValue { #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 @@ class GenericValue { \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 @@ class GenericValue { \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 7328c4e19..9aa9204a1 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 932481348..e1654529f 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 92c73161c..ec7f8a6fc 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -662,7 +662,7 @@ class GenericValue { 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 091879908..4bdb60f29 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -641,7 +641,7 @@ class GenericValue { //! 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 @@ int z = a[0u].GetInt(); // This works too. } //! 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 @@ int z = a[0u].GetInt(); // This works too. } //! 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 4bdb60f29..d139ea75f 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 ec7f8a6fc..44a492bc6 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -885,7 +885,7 @@ class GenericValue { } 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 ec7f8a6fc..edab98df0 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 @@ int z = a[0u].GetInt(); // This works too. kDoubleFlag = 0x4000, kStringFlag = 0x100000, kCopyFlag = 0x200000, + kInlineStrFlag = 0x400000, // Initial flags of different types. kNullFlag = kNullType, @@ -1378,6 +1379,7 @@ int z = a[0u].GetInt(); // This works too. 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 @@ int z = a[0u].GetInt(); // This works too. 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 @@ int z = a[0u].GetInt(); // This works too. union Data { String s; + ShortString ss; Number n; Object o; Array a; @@ -1463,11 +1472,19 @@ int z = a[0u].GetInt(); // This works too. //! 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 @@ int z = a[0u].GetInt(); // This works too. 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 edab98df0..20e4f4c9c 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -1498,12 +1498,12 @@ int z = a[0u].GetInt(); // This works too. 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 44a492bc6..9615004bc 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -885,7 +885,7 @@ class GenericValue { } 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 20e4f4c9c..b33ea07e1 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 @@ int z = a[0u].GetInt(); // This works too. 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 @@ int z = a[0u].GetInt(); // This works too. //! 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 b33ea07e1..9c3d9acc8 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 e1654529f..f5d06c319 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 f5d06c319..8e3fe8f2c 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 8e3fe8f2c..0df5f34a8 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); - - objVal.AddMember(str, len, allocator); - EXPECT_TRUE(objVal.HasMember(str)); + rapidjson::Value val; + val.SetString(str, len, doc.GetAllocator()); - 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 0df5f34a8..1f698e703 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 1f698e703..7cc81dc1c 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 13acb3812..310fc7104 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 @@ class GenericDocument : public GenericValue { 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 701255faa..f02dd1614 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 @@ class GenericReader { 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 @@ class GenericReader { 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 @@ class GenericReader { }; // 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 @@ class GenericReader { 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 @@ class GenericReader { } 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 f89be1b10..2fc9b7571 100644 --- a/include/rapidjson/prettywriter.h +++ b/include/rapidjson/prettywriter.h @@ -88,6 +88,8 @@ class PrettyWriter : public Writer= sizeof(typename Base::Level)); diff --git a/include/rapidjson/writer.h b/include/rapidjson/writer.h index 73e4aff59..2f46b66d4 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -131,6 +131,8 @@ class Writer { 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 09560d828..4c3011ca5 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 9914253d9..83b3f1dc2 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 f02dd1614..92fd7817c 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 b8d60860a..dd94d2438 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 4c3011ca5..d7c8edbaf 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 d7c8edbaf..b42d8323e 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 92fd7817c..d93c8c26c 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -473,7 +473,7 @@ class GenericReader { 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 @@ class GenericReader { 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 @@ class GenericReader { }; // 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 @@ class GenericReader { } 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 dd94d2438..230bc28eb 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 83b3f1dc2..b47d9b2bd 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 2fc9b7571..4eac8d76f 100644 --- a/include/rapidjson/prettywriter.h +++ b/include/rapidjson/prettywriter.h @@ -137,6 +137,7 @@ class PrettyWriter : public Writer 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 2f46b66d4..fb6601ef9 100644 --- a/include/rapidjson/writer.h +++ b/include/rapidjson/writer.h @@ -167,6 +167,7 @@ class Writer { //! 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 b47d9b2bd..edbdb6352 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 e853bea4b..885fe229f 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -637,8 +637,10 @@ class GenericReader { 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 230bc28eb..dd94d2438 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 885fe229f..f41ba2fd7 100644 --- a/include/rapidjson/reader.h +++ b/include/rapidjson/reader.h @@ -630,20 +630,17 @@ class GenericReader { 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()); }