From a9add7bd2ad6ed0711c4059c4e9dbce2913e498c Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Tue, 26 Aug 2014 20:06:25 +0200 Subject: [PATCH 1/7] GenericValue: add non-template overload for operator!= As reported in #113, recent versions of Clang complain about ambiguous overloads for some comparison operator instantiations, especially if the deduced template type is a GenericValue. Add an explicit, non-templated version for now (which is a better match). This only solves part of the problem, as comparisons between * GenericValue & GenericDocument * GenericValue with different SourceAllocator types will still cause ambiguities. --- include/rapidjson/document.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 965cefe..5d8e079 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -685,6 +685,11 @@ public: */ template RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer, bool) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + //! Not-equal-to operator + /*! \return !(*this == rhs) + */ + bool operator!=(const GenericValue& rhs) const { return !(*this == rhs); } + //! Not-equal-to operator with arbitrary types /*! \return !(*this == rhs) */ From d63a40a05d5f56ed42df92d94ddc90b9eecc6752 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Wed, 27 Aug 2014 15:38:19 +0200 Subject: [PATCH 2/7] valuetest: extended value comparison tests Prepare equalto_operator tests to test comparisons between * GenericValue and GenericDocument * GenericValue with different SourceAllocator types Both combinations currently fail due to ambiguities with the templated operators on several compilers. --- test/unittest/valuetest.cpp | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index c9241d4..ce8fce7 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -106,15 +106,35 @@ TEST(Value, equalto_operator) { TestEqual(x["pi"], 3.14); // Test operator==() +#ifdef RAPIDJSON_COMPARE_DIFFERENT_ALLOCATORS + CrtAllocator crtAllocator; + GenericValue, CrtAllocator> y; + GenericDocument, CrtAllocator> z(&crtAllocator); + CrtAllocator& yAllocator = crtAllocator; +#else + Value::AllocatorType& yAllocator = allocator; Value y; - y.CopyFrom(x, allocator); + Document z; +#endif // RAPIDJSON_COMPARE_DIFFERENT_ALLOCATORS + y.CopyFrom(x, yAllocator); + z.CopyFrom(y, z.GetAllocator()); + TestEqual(x, y); + TestEqual(y, z); + TestEqual(z, x); // Swapping member order should be fine. - y.RemoveMember("t"); + EXPECT_TRUE(y.RemoveMember("t")); TestUnequal(x, y); - y.AddMember("t", Value(true).Move(), allocator); + TestUnequal(z, y); + EXPECT_TRUE(z.RemoveMember("t")); + TestUnequal(x, z); + TestEqual(y, z); + y.AddMember("t", true, yAllocator); + z.AddMember("t", true, z.GetAllocator()); TestEqual(x, y); + TestEqual(y, z); + TestEqual(z, x); } template From b56641106b87de1fc513d08bd6febf207ea7addf Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 30 Aug 2014 12:52:36 +0200 Subject: [PATCH 3/7] improve EN/DISABLEIF macros to support complex templated parameters --- build/Doxyfile | 4 +++- include/rapidjson/document.h | 8 ++++---- include/rapidjson/internal/meta.h | 16 +++++++++++++++- 3 files changed, 22 insertions(+), 6 deletions(-) diff --git a/build/Doxyfile b/build/Doxyfile index 65de7e9..6827b51 100644 --- a/build/Doxyfile +++ b/build/Doxyfile @@ -1993,7 +1993,9 @@ INCLUDE_FILE_PATTERNS = PREDEFINED = \ RAPIDJSON_DOXYGEN_RUNNING \ - RAPIDJSON_DISABLEIF_RETURN(cond,returntype)=returntype + RAPIDJSON_REMOVEFPTR_(x)=x \ + RAPIDJSON_ENABLEIF_RETURN(cond,returntype)="RAPIDJSON_REMOVEFPTR_ returntype" \ + RAPIDJSON_DISABLEIF_RETURN(cond,returntype)="RAPIDJSON_REMOVEFPTR_ returntype" # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 5d8e079..43f85f5 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -588,7 +588,7 @@ public: use \ref SetBool() instead. */ template - RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) operator=(T value) { GenericValue v(value); return *this = v; @@ -683,7 +683,7 @@ public: //! Equal-to operator with primitive types /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false */ - template RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer, bool) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + template RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } //! Not-equal-to operator /*! \return !(*this == rhs) @@ -934,7 +934,7 @@ public: \note Amortized Constant time complexity. */ template - RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) AddMember(StringRefType name, T value, Allocator& allocator) { GenericValue n(name); GenericValue v(value); @@ -1147,7 +1147,7 @@ int z = a[0u].GetInt(); // This works too. \note Amortized constant time complexity. */ template - RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) + RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (GenericValue&)) PushBack(T value, Allocator& allocator) { GenericValue v(value); return PushBack(v, allocator); diff --git a/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h index b19a9f4..91e20dc 100644 --- a/include/rapidjson/internal/meta.h +++ b/include/rapidjson/internal/meta.h @@ -66,6 +66,9 @@ struct IsMoreConst { }; }; +////////////////////////////////////////////////////////////////////////// +// EnableIf / DisableIf +// template struct EnableIfCond; template struct EnableIfCond { typedef T Type; }; template struct EnableIfCond { /* empty */ }; @@ -92,8 +95,19 @@ template struct RemoveSfinaeFptr { typedef typename ::rapidjson::internal::EnableIf \ ::Type * = NULL +#define RAPIDJSON_DISABLEIF(cond) \ + typename ::rapidjson::internal::DisableIf \ + ::Type * = NULL + +#define RAPIDJSON_ENABLEIF_RETURN(cond,returntype) \ + typename ::rapidjson::internal::EnableIf \ + ::Type + #define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ - typename ::rapidjson::internal::DisableIf::Type + typename ::rapidjson::internal::DisableIf \ + ::Type } // namespace internal } // namespace rapidjson From ed282b8141d65adaf8e92c7cfb691076b28b0e59 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 30 Aug 2014 13:10:56 +0200 Subject: [PATCH 4/7] meta.h: simplify meta programming helpers Some (older) compilers have problems with compile-time constants. This commit simplifies the implementation of the helper classes in order to improve compiler support, especially be removing the need of partial template specialisation. No functional changes. --- include/rapidjson/internal/meta.h | 82 ++++++++++++++++++++----------- 1 file changed, 53 insertions(+), 29 deletions(-) diff --git a/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h index 91e20dc..068a37d 100644 --- a/include/rapidjson/internal/meta.h +++ b/include/rapidjson/internal/meta.h @@ -30,51 +30,75 @@ RAPIDJSON_DIAG_OFF(effc++) namespace rapidjson { namespace internal { -template struct IntegralC { enum { Value = N }; }; -template struct BoolType : IntegralC {}; -struct TrueType : BoolType {}; -struct FalseType : BoolType {}; +/////////////////////////////////////////////////////////////////////////////// +// BoolType, TrueType, FalseType +// +template struct BoolType { + static const bool Value = Cond; + typedef BoolType Type; +}; +typedef BoolType TrueType; +typedef BoolType FalseType; + + +/////////////////////////////////////////////////////////////////////////////// +// SelectIf, BoolExpr, NotExpr, AndExpr, OrExpr +// + +template struct SelectIfImpl { template struct Apply { typedef T1 Type; }; }; +template <> struct SelectIfImpl { template struct Apply { typedef T2 Type; }; }; +template struct SelectIfCond : SelectIfImpl::template Apply {}; +template struct SelectIf : SelectIfCond {}; + +template struct AndExprCond : FalseType {}; +template <> struct AndExprCond : TrueType {}; +template struct OrExprCond : TrueType {}; +template <> struct OrExprCond : FalseType {}; + +template struct BoolExpr : SelectIf::Type {}; +template struct NotExpr : SelectIf::Type {}; +template struct AndExpr : AndExprCond::Type {}; +template struct OrExpr : OrExprCond::Type {}; + + +/////////////////////////////////////////////////////////////////////////////// +// AddConst, MaybeAddConst, RemoveConst template struct AddConst { typedef const T Type; }; +template struct MaybeAddConst : SelectIfCond {}; template struct RemoveConst { typedef T Type; }; template struct RemoveConst { typedef T Type; }; -template struct SelectIfCond; -template struct SelectIfCond { typedef T1 Type; }; -template struct SelectIfCond { typedef T2 Type; }; -template -struct SelectIf : SelectIfCond {}; - -template -struct MaybeAddConst : SelectIfCond {}; - -template struct IsSame : FalseType {}; -template struct IsSame : TrueType {}; +/////////////////////////////////////////////////////////////////////////////// +// IsSame, IsConst, IsMoreConst, IsPointer +// +template struct IsSame : FalseType {}; +template struct IsSame : TrueType {}; template struct IsConst : FalseType {}; template struct IsConst : TrueType {}; +template +struct IsMoreConst + : AndExpr::Type, typename RemoveConst::Type>, + BoolType::Value >= IsConst::Value> >::Type {}; + template struct IsPointer : FalseType {}; template struct IsPointer : TrueType {}; -template -struct IsMoreConst { - enum { Value = - ( IsSame< typename RemoveConst::Type, typename RemoveConst::Type>::Value - && ( IsConst::Value >= IsConst::Value ) ) }; }; + ////////////////////////////////////////////////////////////////////////// // EnableIf / DisableIf // -template struct EnableIfCond; -template struct EnableIfCond { typedef T Type; }; +template struct EnableIfCond { typedef T Type; }; template struct EnableIfCond { /* empty */ }; -template -struct DisableIfCond : EnableIfCond {}; +template struct DisableIfCond { typedef T Type; }; +template struct DisableIfCond { /* empty */ }; template struct EnableIf : EnableIfCond {}; @@ -83,13 +107,13 @@ template struct DisableIf : DisableIfCond {}; // SFINAE helpers -struct SfinaeResultTag {}; -template struct RemoveSfinaeFptr {}; -template struct RemoveSfinaeFptr { typedef T Type; }; +struct SfinaeTag {}; +template struct RemoveSfinaeTag; +template struct RemoveSfinaeTag { typedef T Type; }; #define RAPIDJSON_REMOVEFPTR_(type) \ - typename ::rapidjson::internal::RemoveSfinaeFptr \ - < ::rapidjson::internal::SfinaeResultTag&(*) type>::Type + typename ::rapidjson::internal::RemoveSfinaeTag \ + < ::rapidjson::internal::SfinaeTag&(*) type>::Type #define RAPIDJSON_ENABLEIF(cond) \ typename ::rapidjson::internal::EnableIf \ From f076faca3d1c086789d30ae67505c6f2e696d328 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 30 Aug 2014 13:14:07 +0200 Subject: [PATCH 5/7] meta.h: add IsBaseOf meta function In order to match GenericValue and its derived classes for the SFINAE-implementation of some of the operators/functions, this meta-function matches all types equal to or derived from a given class. See std::is_base_of available in C++11. Define RAPIDJSON_HAS_CXX_TYPETRAITS to use the C++11 implementation. --- include/rapidjson/internal/meta.h | 41 ++++++++++++++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h index 068a37d..bcff1a5 100644 --- a/include/rapidjson/internal/meta.h +++ b/include/rapidjson/internal/meta.h @@ -25,6 +25,14 @@ RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_OFF(effc++) #endif +#if defined(_MSC_VER) +RAPIDJSON_DIAG_PUSH +RAPIDJSON_DIAG_OFF(6334) +#endif + +#ifdef RAPIDJSON_HAS_CXX11_TYPETRAITS +#include +#endif //@cond RAPIDJSON_INTERNAL namespace rapidjson { @@ -87,9 +95,40 @@ struct IsMoreConst template struct IsPointer : FalseType {}; template struct IsPointer : TrueType {}; +/////////////////////////////////////////////////////////////////////////////// +// IsBaseOf +// +#ifdef RAPIDJSON_HAS_CXX11_TYPETRAITS + +template struct IsBaseOf + : BoolType< ::std::is_base_of::value> {}; + +#else // simplified version adopted from Boost + +template struct IsBaseOfImpl { + RAPIDJSON_STATIC_ASSERT(sizeof(B) != 0); + RAPIDJSON_STATIC_ASSERT(sizeof(D) != 0); + + typedef char (&Yes)[1]; + typedef char (&No) [2]; + + template + static Yes Check(const D*, T); + static No Check(const B*, int); + + struct Host { + operator const B*() const; + operator const D*(); }; + + enum { Value = (sizeof(Check(Host(), 0)) == sizeof(Yes)) }; }; +template struct IsBaseOf + : OrExpr, BoolExpr > >::Type {}; + +#endif // RAPIDJSON_HAS_CXX11_TYPETRAITS + ////////////////////////////////////////////////////////////////////////// // EnableIf / DisableIf @@ -137,7 +176,7 @@ template struct RemoveSfinaeTag { typedef T Type; } // namespace rapidjson //@endcond -#ifdef __GNUC__ +#if defined(__GNUC__) || defined(_MSC_VER) RAPIDJSON_DIAG_POP #endif From fdd7a763865efdd2e5ca3b1998dd3de87cd56b75 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 30 Aug 2014 16:01:06 +0200 Subject: [PATCH 6/7] GenericValue: use IsBaseOf to support comparisons between Value/Document This finally fixes #113. --- include/rapidjson/document.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 43f85f5..83e984a 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -683,7 +683,7 @@ public: //! Equal-to operator with primitive types /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t, \c double, \c true, \c false */ - template RAPIDJSON_DISABLEIF_RETURN((internal::IsPointer), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } + template RAPIDJSON_DISABLEIF_RETURN((internal::OrExpr,internal::IsBaseOf >), (bool)) operator==(const T& rhs) const { return *this == GenericValue(rhs); } //! Not-equal-to operator /*! \return !(*this == rhs) @@ -693,17 +693,17 @@ public: //! Not-equal-to operator with arbitrary types /*! \return !(*this == rhs) */ - template bool operator!=(const T& rhs) const { return !(*this == rhs); } + template RAPIDJSON_DISABLEIF_RETURN((internal::IsBaseOf), (bool)) operator!=(const T& rhs) const { return !(*this == rhs); } //! Equal-to operator with arbitrary types (symmetric version) /*! \return (rhs == lhs) */ - template friend bool operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsBaseOf), (bool)) operator==(const T& lhs, const GenericValue& rhs) { return rhs == lhs; } //! Not-Equal-to operator with arbitrary types (symmetric version) /*! \return !(rhs == lhs) */ - template friend bool operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } + template friend RAPIDJSON_DISABLEIF_RETURN((internal::IsBaseOf), (bool)) operator!=(const T& lhs, const GenericValue& rhs) { return !(rhs == lhs); } //@} //!@name Type From 804b7fcb2f12419d0900a20970c35ef3a1abd60d Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Sat, 30 Aug 2014 16:39:15 +0200 Subject: [PATCH 7/7] GenericValue: add static assert to catch broken SFINAE Before applying the simplifications in ed282b814, the SFINAE check for the GenericValue(bool) constructor has been broken in MSVC 2005. Add a static assert as a safe-guard against future reappearance of this problem. --- include/rapidjson/document.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 83e984a..cd0ca22 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -458,7 +458,10 @@ public: #else explicit GenericValue(bool b) #endif - : data_(), flags_(b ? kTrueFlag : kFalseFlag) {} + : data_(), flags_(b ? kTrueFlag : kFalseFlag) { + // safe-guard against failing SFINAE + RAPIDJSON_STATIC_ASSERT((internal::IsSame::Value)); + } //! Constructor for int value. explicit GenericValue(int i) : data_(), flags_(kNumberIntFlag) {