From 3bfffa3cf9afd167e03076c6946c5f580768efab Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Fri, 4 Jul 2014 15:08:10 +0200 Subject: [PATCH 01/11] meta.h: inherit from TrueType/FalseType where applicable To reduce repetition and to provide a proper type hierarchy, let type traits inherit from TrueType and FalseType. --- include/rapidjson/internal/meta.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h index 0861a48..5b9a8c0 100644 --- a/include/rapidjson/internal/meta.h +++ b/include/rapidjson/internal/meta.h @@ -24,11 +24,11 @@ struct SelectIf : SelectIfCond {}; template struct MaybeAddConst : SelectIfCond {}; -template struct IsSame { enum { Value = false }; }; -template struct IsSame { enum { Value = true }; }; +template struct IsSame : FalseType {}; +template struct IsSame : TrueType {}; -template struct IsConst { enum { Value = false }; }; -template struct IsConst { enum { Value = true }; }; +template struct IsConst : FalseType {}; +template struct IsConst : TrueType {}; template struct IsMoreConst { From 4007301e1f7adebef70c92bca9be177e445beaed Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Fri, 4 Jul 2014 16:53:10 +0200 Subject: [PATCH 02/11] Add a wrapper for constant string references --- include/rapidjson/document.h | 128 ++++++++++++++++++++++-------- include/rapidjson/internal/meta.h | 6 ++ 2 files changed, 101 insertions(+), 33 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 43f5049..7f56086 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -174,6 +174,51 @@ struct GenericMemberIterator { #endif // RAPIDJSON_NOMEMBERITERATORCLASS +/////////////////////////////////////////////////////////////////////////////// +// GenericStringRef + +//! Reference to a constant string (not taking a copy) +template +struct GenericStringRef { + typedef CharType Ch; //!< character type of the string + + //! Create string reference from \c const character array + template + GenericStringRef(const CharType (&str)[N]) + : s(str), length(N-1) {} + + //! Explicitly create string reference from \c const character pointer + explicit GenericStringRef(const CharType* str) + : s(str), length(internal::StrLen(str)){} + + //! Create constant string reference from pointer and length + GenericStringRef(const CharType* str, SizeType len) + : s(str), length(len) { RAPIDJSON_ASSERT(s != NULL); } + + //! implicit conversion to plain CharType pointer + operator const Ch *() const { return s; } + + const Ch* s; //!< plain CharType pointer + const SizeType length; //!< length of the string (excluding the trailing NULL terminator) + +private: + //! Disallow construction from non-const array + template + GenericStringRef(CharType (&str)[N]) /* = delete */; +}; + +//! Mark a character pointer as constant string +template +inline GenericStringRef StringRef(const CharType* str) { + return GenericStringRef(str, internal::StrLen(str)); +} + +//! Mark a character pointer as constant string +template +inline GenericStringRef StringRef(const CharType* str, size_t length) { + return GenericStringRef(str, SizeType(length)); +} + /////////////////////////////////////////////////////////////////////////////// // GenericValue @@ -196,6 +241,7 @@ public: typedef Encoding EncodingType; //!< Encoding type from template parameter. typedef Allocator AllocatorType; //!< Allocator type from template parameter. typedef typename Encoding::Ch Ch; //!< Character type derived from Encoding. + typedef GenericStringRef StringRefType; //!< Reference to a constant string typedef typename GenericMemberIterator::Iterator MemberIterator; //!< Member iterator for iterating in object. typedef typename GenericMemberIterator::Iterator ConstMemberIterator; //!< Constant member iterator for iterating in object. typedef GenericValue* ValueIterator; //!< Value iterator for iterating in array. @@ -207,9 +253,11 @@ public: //! Default constructor creates a null value. GenericValue() : data_(), flags_(kNullFlag) {} - //! Copy constructor is not permitted. private: + //! Copy constructor is not permitted. GenericValue(const GenericValue& rhs); + //! Disabled constructor for arbitrary pointers. + template explicit GenericValue(T*); public: @@ -283,16 +331,16 @@ public: explicit GenericValue(double d) : 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(s, length); } + GenericValue(const Ch* s, SizeType length) : data_(), flags_() { SetStringRaw(StringRef(s, length)); } //! Constructor for constant string (i.e. do not make a copy of string) - explicit GenericValue(const Ch* s) : data_(), flags_() { SetStringRaw(s, internal::StrLen(s)); } + explicit GenericValue(StringRefType s) : 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(s, length, allocator); } + GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s, length), allocator); } //! Constructor for copy-string (i.e. do make a copy of string) - GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(s, internal::StrLen(s), allocator); } + GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); } //! Destructor. /*! Need to destruct elements of array, members of object, or copy-string. @@ -339,12 +387,17 @@ public: return *this; } + //! Assignment of constant string reference (no copy) + GenericValue& operator=(StringRefType str) { + return (*this).template operator=(str); + } //! Assignment with primitive types. - /*! \tparam T Either Type, int, unsigned, int64_t, uint64_t, const Ch* + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t \param value The value to be assigned. */ template - GenericValue& operator=(T value) { + RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) + operator=(T value) { this->~GenericValue(); new (this) GenericValue(value); return *this; @@ -428,7 +481,7 @@ public: A better approach is to use the now public FindMember(). */ GenericValue& operator[](const Ch* name) { - GenericValue n(name, internal::StrLen(name)); + GenericValue n(StringRef(name)); return (*this)[n]; } const GenericValue& operator[](const Ch* name) const { return const_cast(*this)[name]; } @@ -481,7 +534,7 @@ public: \c std::map, this has been changed to MemberEnd() now. */ MemberIterator FindMember(const Ch* name) { - GenericValue n(name, internal::StrLen(name)); + GenericValue n(StringRef(name)); return FindMember(n); } @@ -530,19 +583,23 @@ public: return *this; } - GenericValue& AddMember(const Ch* name, Allocator& nameAllocator, GenericValue& value, Allocator& allocator) { - GenericValue n(name, internal::StrLen(name), nameAllocator); + //! Add a member (name-value pair) to the object. + GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { + GenericValue n(name); return AddMember(n, value, allocator); } - GenericValue& AddMember(const Ch* name, GenericValue& value, Allocator& allocator) { - GenericValue n(name, internal::StrLen(name)); - return AddMember(n, value, allocator); + //! Add a constant string value as member (name-value pair) to the object. + GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { + GenericValue v(value); + return AddMember(name, v, allocator); } + //! Add any primitive value as member (name-value pair) to the object. template - GenericValue& AddMember(const Ch* name, T value, Allocator& allocator) { - GenericValue n(name, internal::StrLen(name)); + RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) + AddMember(StringRefType name, T value, Allocator& allocator) { + GenericValue n(name); GenericValue v(value); return AddMember(n, v, allocator); } @@ -553,7 +610,7 @@ public: \note Removing member is implemented by moving the last member. So the ordering of members is changed. */ bool RemoveMember(const Ch* name) { - GenericValue n(name, internal::StrLen(name)); + GenericValue n(StringRef(name)); return RemoveMember(n); } @@ -657,7 +714,7 @@ int z = a[0u].GetInt(); // This works too. return *this; } - //! Append a value at the end of the array. + //! Append a GenericValue at the end of the array. /*! \param value The value to be appended. \param allocator The allocator for allocating memory. It must be the same one use previously. \return The value itself for fluent API. @@ -672,8 +729,15 @@ int z = a[0u].GetInt(); // This works too. return *this; } + //! Append a constant string reference at the end of the array. + GenericValue& PushBack(StringRefType value, Allocator& allocator) { + return (*this).template PushBack(value, allocator); + } + + //! Append a primitive value at the end of the array. template - GenericValue& PushBack(T value, Allocator& allocator) { + RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) + PushBack(T value, Allocator& allocator) { GenericValue v(value); return PushBack(v, allocator); } @@ -728,13 +792,13 @@ int z = a[0u].GetInt(); // This works too. \param length The length of source string, excluding the trailing null terminator. \return The value itself for fluent API. */ - GenericValue& SetString(const Ch* s, SizeType length) { this->~GenericValue(); SetStringRaw(s, length); return *this; } + GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } //! Set this value as a string without copying source string. - /*! \param s source string pointer. + /*! \param s source string reference \return The value itself for fluent API. */ - GenericValue& SetString(const Ch* s) { return SetString(s, internal::StrLen(s)); } + GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } //! Set this value as a string by copying from source string. /*! This version has better performance with supplied length, and also support string containing null character. @@ -743,14 +807,14 @@ int z = a[0u].GetInt(); // This works too. \param allocator Allocator for allocating copied buffer. Commonly use document.GetAllocator(). \return The value itself for fluent API. */ - GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(s, length, allocator); return *this; } + GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; } //! Set this value as a string by copying from source string. /*! \param s source string. \param allocator Allocator for allocating copied buffer. Commonly use document.GetAllocator(). \return The value itself for fluent API. */ - GenericValue& SetString(const Ch* s, Allocator& allocator) { SetString(s, internal::StrLen(s), allocator); return *this; } + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } //@} @@ -906,21 +970,19 @@ private: } //! Initialize this value as constant string, without calling destructor. - void SetStringRaw(const Ch* s, SizeType length) { - RAPIDJSON_ASSERT(s != NULL); + void SetStringRaw(StringRefType s) { flags_ = kConstStringFlag; data_.s.str = s; - data_.s.length = length; + data_.s.length = s.length; } //! Initialize this value as copy string with initial data, without calling destructor. - void SetStringRaw(const Ch* s, SizeType length, Allocator& allocator) { - RAPIDJSON_ASSERT(s != NULL); + void SetStringRaw(StringRefType s, Allocator& allocator) { flags_ = kCopyStringFlag; - data_.s.str = (Ch *)allocator.Malloc((length + 1) * sizeof(Ch)); - data_.s.length = length; - memcpy(const_cast(data_.s.str), s, length * sizeof(Ch)); - const_cast(data_.s.str)[length] = '\0'; + 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'; } //! Assignment without calling destructor diff --git a/include/rapidjson/internal/meta.h b/include/rapidjson/internal/meta.h index 5b9a8c0..b8d3295 100644 --- a/include/rapidjson/internal/meta.h +++ b/include/rapidjson/internal/meta.h @@ -30,6 +30,9 @@ template struct IsSame : TrueType {}; template struct IsConst : FalseType {}; template struct IsConst : TrueType {}; +template struct IsPointer : FalseType {}; +template struct IsPointer : TrueType {}; + template struct IsMoreConst { enum { Value = @@ -64,6 +67,9 @@ template struct RemoveSfinaeFptr { typedef typename ::rapidjson::internal::EnableIf \ ::Type * = NULL +#define RAPIDJSON_DISABLEIF_RETURN(cond,returntype) \ + typename ::rapidjson::internal::DisableIf::Type + } // namespace internal } // namespace rapidjson //@endcond From 524362242b49c329e09c5b1331504565b7171b3c Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Fri, 4 Jul 2014 16:55:19 +0200 Subject: [PATCH 03/11] Add documentation to new string API --- include/rapidjson/document.h | 201 ++++++++++++++++++++++++++++++++--- 1 file changed, 188 insertions(+), 13 deletions(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 7f56086..44cf532 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -178,20 +178,92 @@ struct GenericMemberIterator { // GenericStringRef //! Reference to a constant string (not taking a copy) +/*! + \tparam CharType character type of the string + + This helper class is used to automatically infer constant string + references for string literals, especially from \c const \b (!) + character arrays. + + The main use is for creating JSON string values without copying the + source string via an \ref Allocator. This requires that the referenced + string pointers have a sufficient lifetime, which exceeds the lifetime + of the associated GenericValue. + + \b Example + \code + Value v("foo"); // ok, no need to copy & calculate length + const char foo[] = "foo"; + v.SetString(foo); // ok + + const char* bar = foo; + // Value x(bar); // not ok, can't rely on bar's lifetime + Value x(StringRef(bar)); // lifetime explicitly guaranteed by user + Value y(StringRef(bar, 3)); // ok, explicitly pass length + \endcode + + \see StringRef, GenericValue::SetString +*/ template struct GenericStringRef { typedef CharType Ch; //!< character type of the string //! Create string reference from \c const character array + /*! + This constructor implicitly creates a constant string reference from + a \c const character array. It has better performance than + \ref StringRef(const CharType*) by inferring the string \ref length + from the array length, and also supports strings containing null + characters. + + \tparam N length of the string, automatically inferred + + \param str Constant character array, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note Constant complexity. + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ template GenericStringRef(const CharType (&str)[N]) : s(str), length(N-1) {} //! Explicitly create string reference from \c const character pointer + /*! + This constructor can be used to \b explicitly create a reference to + a constant string pointer. + + \see StringRef(const CharType*) + + \param str Constant character pointer, lifetime assumed to be longer + than the use of the string in e.g. a GenericValue + + \post \ref s == str + + \note There is a hidden, private overload to disallow references to + non-const character arrays to be created via this constructor. + By this, e.g. function-scope arrays used to be filled via + \c snprintf are excluded from consideration. + In such cases, the referenced string should be \b copied to the + GenericValue instead. + */ explicit GenericStringRef(const CharType* str) : s(str), length(internal::StrLen(str)){} //! 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 + \param len length of the string, excluding the trailing NULL terminator + + \post \ref s == str && \ref length == len + \note Constant complexity. + */ GenericStringRef(const CharType* str, SizeType len) : s(str), length(len) { RAPIDJSON_ASSERT(s != NULL); } @@ -208,12 +280,37 @@ private: }; //! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + \tparam CharType Character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \return GenericStringRef string reference object + \relatesalso GenericStringRef + + \see GenericValue::GenericValue(StringRefType), GenericValue::operator=(StringRefType), GenericValue::SetString(StringRefType), GenericValue::PushBack(StringRefType, Allocator&), GenericValue::AddMember +*/ template inline GenericStringRef StringRef(const CharType* str) { return GenericStringRef(str, internal::StrLen(str)); } //! Mark a character pointer as constant string +/*! Mark a plain character pointer as a "string literal". This function + can be used to avoid copying a character string to be referenced as a + value in a JSON GenericValue object, if the string's lifetime is known + to be valid long enough. + + This version has better performance with supplied length, and also + supports string containing null characters. + + \tparam CharType character type of the string + \param str Constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue + \param length The length of source string. + \return GenericStringRef string reference object + \relatesalso GenericStringRef +*/ template inline GenericStringRef StringRef(const CharType* str, size_t length) { return GenericStringRef(str, SizeType(length)); @@ -279,7 +376,7 @@ public: /*! Creates a copy of a Value by using the given Allocator \tparam SourceAllocator allocator of \c rhs \param rhs Value to copy from (read-only) - \param allocator Allocator to use for copying + \param allocator Allocator for allocating copied elements and buffers. Commonly use GenericDocument::GetAllocator(). \see CopyFrom() */ template< typename SourceAllocator > @@ -388,12 +485,25 @@ public: } //! 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. + \see GenericStringRef, operator=(T) + */ GenericValue& operator=(StringRefType str) { return (*this).template operator=(str); } + //! Assignment with primitive types. /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t \param value The value to be assigned. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref SetString(const Ch*, Allocator&) (for copying) or + \ref StringRef() (to explicitly mark the pointer as constant) instead. + All other pointer types would implicitly convert to \c bool, + use \ref SetBool() instead. */ template RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) @@ -463,6 +573,8 @@ public: //@{ bool GetBool() const { RAPIDJSON_ASSERT(IsBool()); return flags_ == kTrueFlag; } + //!< Set boolean value + /*! \post IsBool() == true */ GenericValue& SetBool(bool b) { this->~GenericValue(); new (this) GenericValue(b); return *this; } //@} @@ -471,6 +583,7 @@ public: //@{ //! Set this value as an empty object. + /*! \post IsObject() == true */ GenericValue& SetObject() { this->~GenericValue(); new (this) GenericValue(kObjectType); return *this; } //! Get the value associated with the name. @@ -557,9 +670,11 @@ public: //! Add a member (name-value pair) to the object. /*! \param name A string value as name of member. \param value Value of any type. - \param allocator Allocator for reallocating memory. - \return The value itself for fluent API. - \note The ownership of name and value will be transfered to this object if success. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c name and \c value will be transferred to this object on success. + \pre IsObject() && name.IsString() + \post name.IsNull() && value.IsNull() */ GenericValue& AddMember(GenericValue& name, GenericValue& value, Allocator& allocator) { RAPIDJSON_ASSERT(IsObject()); @@ -584,18 +699,48 @@ public: } //! 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. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this object on success. + \pre IsObject() + \post value.IsNull() + */ GenericValue& AddMember(StringRefType name, GenericValue& value, Allocator& allocator) { GenericValue n(name); return AddMember(n, value, allocator); } //! Add a constant string value as member (name-value pair) to the object. + /*! \param name A constant string reference as name of member. + \param value constant string reference as value of member. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + \note This overload is needed to avoid clashes with the generic primitive type AddMember(StringRefType,T,Allocator&) overload below. + */ GenericValue& AddMember(StringRefType name, StringRefType value, Allocator& allocator) { GenericValue v(value); return AddMember(name, v, allocator); } //! Add any primitive value as member (name-value pair) to the object. + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param name A constant string reference as name of member. + \param value Value of primitive type \c T as value of member + \param allocator Allocator for reallocating memory. Commonly use GenericDocument::GetAllocator(). + \return The value itself for fluent API. + \pre IsObject() + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref AddMember(StringRefType, GenericValue&, Allocator&) or \ref + AddMember(StringRefType, StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + */ template RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) AddMember(StringRefType name, T value, Allocator& allocator) { @@ -656,6 +801,7 @@ public: //@{ //! Set this value as an empty array. + /*! \post IsArray == true */ GenericValue& SetArray() { this->~GenericValue(); new (this) GenericValue(kArrayType); return *this; } //! Get the number of elements in array. @@ -702,7 +848,7 @@ int z = a[0u].GetInt(); // This works too. //! Request the array to have enough capacity to store elements. /*! \param newCapacity The capacity that the array at least need to have. - \param allocator The allocator for allocating memory. It must be the same one use previously. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). \return The value itself for fluent API. */ GenericValue& Reserve(SizeType newCapacity, Allocator &allocator) { @@ -715,11 +861,13 @@ int z = a[0u].GetInt(); // This works too. } //! Append a GenericValue at the end of the array. - /*! \param value The value to be appended. - \param allocator The allocator for allocating memory. It must be the same one use previously. - \return The value itself for fluent API. - \note The ownership of the value will be transfered to this object if success. - \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + /*! \param value Value to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \post value.IsNull() == true + \return The value itself for fluent API. + \note The ownership of \c value will be transferred to this array on success. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. */ GenericValue& PushBack(GenericValue& value, Allocator& allocator) { RAPIDJSON_ASSERT(IsArray()); @@ -730,11 +878,33 @@ int z = a[0u].GetInt(); // This works too. } //! 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(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + \see GenericStringRef + */ GenericValue& PushBack(StringRefType value, Allocator& allocator) { return (*this).template PushBack(value, allocator); } - //! Append a primitive value at the end of the array. + //! Append a primitive value at the end of the array(.) + /*! \tparam T Either \ref Type, \c int, \c unsigned, \c int64_t, \c uint64_t + \param value Value of primitive type T to be appended. + \param allocator Allocator for reallocating memory. It must be the same one as used before. Commonly use GenericDocument::GetAllocator(). + \pre IsArray() == true + \return The value itself for fluent API. + \note If the number of elements to be appended is known, calls Reserve() once first may be more efficient. + + \note The source type \c T explicitly disallows all pointer types, + especially (\c const) \ref Ch*. This helps avoiding implicitly + referencing character strings with insufficient lifetime, use + \ref PushBack(GenericValue&, Allocator&) or \ref + PushBack(StringRefType, Allocator&). + All other pointer types would implicitly convert to \c bool, + use an explicit cast instead, if needed. + */ template RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) PushBack(T value, Allocator& allocator) { @@ -791,12 +961,15 @@ int z = a[0u].GetInt(); // This works too. \param s source string pointer. \param length The length of source string, excluding the trailing null terminator. \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == length + \see SetString(StringRefType) */ GenericValue& SetString(const Ch* s, SizeType length) { return SetString(StringRef(s, length)); } //! Set this value as a string without copying source string. /*! \param s source string reference \return The value itself for fluent API. + \post IsString() == true && GetString() == s && GetStringLength() == s.length */ GenericValue& SetString(StringRefType s) { this->~GenericValue(); SetStringRaw(s); return *this; } @@ -804,15 +977,17 @@ int z = a[0u].GetInt(); // This works too. /*! This version has better performance with supplied length, and also support string containing null character. \param s source string. \param length The length of source string, excluding the trailing null terminator. - \param allocator Allocator for allocating copied buffer. Commonly use document.GetAllocator(). + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ GenericValue& SetString(const Ch* s, SizeType length, Allocator& allocator) { this->~GenericValue(); SetStringRaw(StringRef(s, length), allocator); return *this; } //! Set this value as a string by copying from source string. /*! \param s source string. - \param allocator Allocator for allocating copied buffer. Commonly use document.GetAllocator(). + \param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator(). \return The value itself for fluent API. + \post IsString() == true && GetString() != s && strcmp(GetString(),s) == 0 && GetStringLength() == length */ GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } From db8f45573f812a4d070c589c91fccf7e8e2211f2 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Fri, 4 Jul 2014 15:43:57 +0200 Subject: [PATCH 04/11] Doxyfile: expand internal RAPIDJSON_DISABLEIF_RETURN macro --- build/Doxyfile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/build/Doxyfile b/build/Doxyfile index c74b79d..9d504b5 100644 --- a/build/Doxyfile +++ b/build/Doxyfile @@ -1990,7 +1990,8 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = +PREDEFINED = \ + RAPIDJSON_DISABLEIF_RETURN(cond,returntype)=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 From 8bbf41f7c7058c3f7231a14fb47f6677b20a694d Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Fri, 4 Jul 2014 16:34:10 +0200 Subject: [PATCH 05/11] update string API tests --- test/unittest/valuetest.cpp | 117 ++++++++++++++++++++++++++++++------ 1 file changed, 100 insertions(+), 17 deletions(-) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 93d4eb0..39500d0 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -23,6 +23,25 @@ TEST(Value, assignment_operator) { y = x; EXPECT_TRUE(x.IsNull()); // move semantic EXPECT_EQ(1234, y.GetInt()); + + y = 5678; + EXPECT_TRUE(y.IsInt()); + EXPECT_EQ(5678, y.GetInt()); + + x = "Hello"; + EXPECT_TRUE(x.IsString()); + EXPECT_STREQ(x.GetString(),"Hello"); + + y = StringRef(x.GetString(),x.GetStringLength()); + EXPECT_TRUE(y.IsString()); + EXPECT_EQ(y.GetString(),x.GetString()); + EXPECT_EQ(y.GetStringLength(),x.GetStringLength()); + + static char mstr[] = "mutable"; + // y = mstr; // should not compile + y = StringRef(mstr); + EXPECT_TRUE(y.IsString()); + EXPECT_EQ(y.GetString(),mstr); } template @@ -350,8 +369,8 @@ TEST(Value, Double) { } TEST(Value, String) { - // Constructor with const string - Value x("Hello", 5); + // Construction with const string + Value x("Hello", 5); // literal EXPECT_EQ(kStringType, x.GetType()); EXPECT_TRUE(x.IsString()); EXPECT_STREQ("Hello", x.GetString()); @@ -365,9 +384,41 @@ TEST(Value, String) { EXPECT_FALSE(x.IsObject()); EXPECT_FALSE(x.IsArray()); + static const char cstr[] = "World"; // const array + Value(cstr).Swap(x); + EXPECT_TRUE(x.IsString()); + EXPECT_EQ(x.GetString(), cstr); + EXPECT_EQ(x.GetStringLength(), sizeof(cstr)-1); + + static char mstr[] = "Howdy"; // non-const array + // Value(mstr).Swap(x); // should not compile + Value(StringRef(mstr)).Swap(x); + EXPECT_TRUE(x.IsString()); + EXPECT_EQ(x.GetString(), mstr); + EXPECT_EQ(x.GetStringLength(), sizeof(mstr)-1); + strncpy(mstr,"Hello", sizeof(mstr)); + EXPECT_STREQ(x.GetString(), "Hello"); + + const char* pstr = cstr; + //Value(pstr).Swap(x); // should not compile + Value(StringRef(pstr)).Swap(x); + EXPECT_TRUE(x.IsString()); + EXPECT_EQ(x.GetString(), cstr); + EXPECT_EQ(x.GetStringLength(), sizeof(cstr)-1); + + char* mpstr = mstr; + Value(StringRef(mpstr,sizeof(mstr)-1)).Swap(x); + EXPECT_TRUE(x.IsString()); + EXPECT_EQ(x.GetString(), mstr); + EXPECT_EQ(x.GetStringLength(), 5u); + EXPECT_STREQ(x.GetString(), "Hello"); + // Constructor with copy string MemoryPoolAllocator<> allocator; Value c(x.GetString(), x.GetStringLength(), allocator); + EXPECT_NE(x.GetString(), c.GetString()); + EXPECT_EQ(x.GetStringLength(), c.GetStringLength()); + EXPECT_STREQ(x.GetString(), c.GetString()); //x.SetString("World"); x.SetString("World", 5); EXPECT_STREQ("Hello", c.GetString()); @@ -381,11 +432,31 @@ TEST(Value, String) { // SetConsttring() Value z; - //z.SetString("Hello"); + z.SetString("Hello"); + EXPECT_TRUE(x.IsString()); z.SetString("Hello", 5); EXPECT_STREQ("Hello", z.GetString()); + EXPECT_STREQ("Hello", z.GetString()); EXPECT_EQ(5u, z.GetStringLength()); + z.SetString("Hello"); + EXPECT_TRUE(z.IsString()); + EXPECT_STREQ("Hello", z.GetString()); + + //z.SetString(mstr); // should not compile + //z.SetString(pstr); // should not compile + z.SetString(StringRef(mstr)); + EXPECT_TRUE(z.IsString()); + EXPECT_STREQ(z.GetString(), mstr); + + z.SetString(cstr); + EXPECT_TRUE(z.IsString()); + EXPECT_EQ(cstr, z.GetString()); + + z = cstr; + EXPECT_TRUE(z.IsString()); + EXPECT_EQ(cstr, z.GetString()); + // SetString() char s[] = "World"; Value w; @@ -424,11 +495,13 @@ TEST(Value, Array) { x.PushBack(v, allocator); v.SetInt(123); x.PushBack(v, allocator); + //x.PushBack((const char*)"foo", allocator); // should not compile + x.PushBack("foo", allocator); EXPECT_FALSE(x.Empty()); - EXPECT_EQ(4u, x.Size()); + EXPECT_EQ(5u, x.Size()); EXPECT_FALSE(y.Empty()); - EXPECT_EQ(4u, y.Size()); + EXPECT_EQ(5u, y.Size()); EXPECT_TRUE(x[SizeType(0)].IsNull()); EXPECT_TRUE(x[1u].IsTrue()); EXPECT_TRUE(x[2u].IsFalse()); @@ -439,6 +512,8 @@ TEST(Value, Array) { EXPECT_TRUE(y[2u].IsFalse()); EXPECT_TRUE(y[3u].IsInt()); EXPECT_EQ(123, y[3u].GetInt()); + EXPECT_TRUE(y[4u].IsString()); + EXPECT_STREQ("foo", y[4u].GetString()); // iterator Value::ValueIterator itr = x.Begin(); @@ -454,6 +529,10 @@ TEST(Value, Array) { EXPECT_TRUE(itr != x.End()); EXPECT_TRUE(itr->IsInt()); EXPECT_EQ(123, itr->GetInt()); + ++itr; + EXPECT_TRUE(itr != x.End()); + EXPECT_TRUE(itr->IsString()); + EXPECT_STREQ("foo", itr->GetString()); // const iterator Value::ConstValueIterator citr = y.Begin(); @@ -469,13 +548,18 @@ TEST(Value, Array) { EXPECT_TRUE(citr != y.End()); EXPECT_TRUE(citr->IsInt()); EXPECT_EQ(123, citr->GetInt()); + ++citr; + EXPECT_TRUE(citr != y.End()); + EXPECT_TRUE(citr->IsString()); + EXPECT_STREQ("foo", citr->GetString()); // PopBack() x.PopBack(); - EXPECT_EQ(3u, x.Size()); + EXPECT_EQ(4u, x.Size()); EXPECT_TRUE(y[SizeType(0)].IsNull()); - EXPECT_TRUE(y[1].IsTrue()); - EXPECT_TRUE(y[2].IsFalse()); + EXPECT_TRUE(y[1u].IsTrue()); + EXPECT_TRUE(y[2u].IsFalse()); + EXPECT_TRUE(y[3u].IsInt()); // Clear() x.Clear(); @@ -502,16 +586,13 @@ TEST(Value, Object) { EXPECT_TRUE(y.IsObject()); // AddMember() - Value name("A", 1); - Value value("Apple", 5); - x.AddMember(name, value, allocator); - //name.SetString("B"); - name.SetString("B", 1); - //value.SetString("Banana"); - value.SetString("Banana", 6); - x.AddMember(name, value, allocator); + x.AddMember("A", "Apple", allocator); + + Value value("Banana", 6); + x.AddMember("B", "Banana", allocator); // Tests a member with null character + Value name; const Value C0D("C\0D", 3); name.SetString(C0D.GetString(), 3); value.SetString("CherryD", 7); @@ -523,7 +604,7 @@ TEST(Value, Object) { EXPECT_TRUE(y.HasMember("A")); EXPECT_TRUE(y.HasMember("B")); - name.SetString("C\0D", 3); + name.SetString("C\0D"); EXPECT_TRUE(x.HasMember(name)); EXPECT_TRUE(y.HasMember(name)); @@ -617,6 +698,7 @@ TEST(Value, BigNestedObject) { char name1[10]; sprintf(name1, "%d", i); + // Value name(name1); // should not compile Value name(name1, (SizeType)strlen(name1), allocator); Value object(kObjectType); @@ -629,6 +711,7 @@ TEST(Value, BigNestedObject) { object.AddMember(name, number, allocator); } + // x.AddMember(name1, object, allocator); // should not compile x.AddMember(name, object, allocator); } From ed9cdbc2f7f78d9936e5236632608c2589e84125 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Mon, 7 Jul 2014 11:26:19 +0200 Subject: [PATCH 06/11] GenericStringRef: disallow assignment, const string pointer --- include/rapidjson/document.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 44cf532..33c97f4 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -270,10 +270,12 @@ struct GenericStringRef { //! implicit conversion to plain CharType pointer operator const Ch *() const { return s; } - const Ch* s; //!< plain CharType pointer + const Ch* const s; //!< plain CharType pointer const SizeType length; //!< length of the string (excluding the trailing NULL terminator) private: + //! Disallow copy-assignment + GenericStringRef operator=(const GenericStringRef&); //! Disallow construction from non-const array template GenericStringRef(CharType (&str)[N]) /* = delete */; From 9b3969d0e1879d4661f163dba63922a78036fa71 Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Tue, 8 Jul 2014 20:41:59 +0200 Subject: [PATCH 07/11] GenericValue: fixup bool constructor With the new string handling API, the constructor taking a `bool` parameter matches in some unwanted cases, as pointers can be casted to `bool` implicitly. Add a SFINAE helper to this constructor to avoid matching arbitrary pointers. To avoid confusion for the user, this mechanism is hidden from the Doxygen documentation. --- build/Doxyfile | 1 + include/rapidjson/document.h | 15 ++++++++++++--- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/build/Doxyfile b/build/Doxyfile index 9d504b5..d2a57d5 100644 --- a/build/Doxyfile +++ b/build/Doxyfile @@ -1991,6 +1991,7 @@ INCLUDE_FILE_PATTERNS = # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. PREDEFINED = \ + RAPIDJSON_DOXYGEN_RUNNING \ RAPIDJSON_DISABLEIF_RETURN(cond,returntype)=returntype # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 33c97f4..66df387 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -355,8 +355,6 @@ public: private: //! Copy constructor is not permitted. GenericValue(const GenericValue& rhs); - //! Disabled constructor for arbitrary pointers. - template explicit GenericValue(T*); public: @@ -385,7 +383,18 @@ public: GenericValue(const GenericValue& rhs, Allocator & allocator); //! Constructor for boolean value. - explicit GenericValue(bool b) : data_(), flags_(b ? kTrueFlag : kFalseFlag) {} + /*! \param b Boolean value + \note This constructor is limited to \em real boolean values and rejects + implicitly converted types like arbitrary pointers. Use an explicit cast + to \c bool, if you want to construct a boolean JSON value in such cases. + */ +#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen + template + explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame))) +#else + explicit GenericValue(bool b) +#endif + : data_(), flags_(b ? kTrueFlag : kFalseFlag) {} //! Constructor for int value. explicit GenericValue(int i) : data_(), flags_(kNumberIntFlag) { From 9a89eed11a1c559a73570c1ec423eca858a4415b Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Wed, 9 Jul 2014 09:24:34 +0200 Subject: [PATCH 08/11] adopt AddMember tests from #47 (726f986,b7bf73c) --- test/unittest/valuetest.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 39500d0..fcf36a3 100644 --- a/test/unittest/valuetest.cpp +++ b/test/unittest/valuetest.cpp @@ -591,6 +591,27 @@ TEST(Value, Object) { Value value("Banana", 6); x.AddMember("B", "Banana", allocator); + // AddMember(StringRefType, T, Allocator) + { + Value o(kObjectType); + o.AddMember("true", true, allocator); + o.AddMember("false", false, allocator); + o.AddMember("int", -1, allocator); + o.AddMember("uint", 1u, allocator); + o.AddMember("int64", INT64_C(-4294967296), allocator); + o.AddMember("uint64", UINT64_C(4294967296), allocator); + o.AddMember("double", 3.14, allocator); + o.AddMember("string", "Jelly", allocator); + + EXPECT_TRUE(o["true"].GetBool()); + EXPECT_FALSE(o["false"].GetBool()); + EXPECT_EQ(-1, o["int"].GetInt()); + EXPECT_EQ(1u, o["uint"].GetUint()); + EXPECT_EQ(INT64_C(-4294967296), o["int64"].GetInt64()); + EXPECT_EQ(UINT64_C(4294967296), o["uint64"].GetUint64()); + EXPECT_STREQ("Jelly",o["string"].GetString()); + } + // Tests a member with null character Value name; const Value C0D("C\0D", 3); From edf8a0aa1da966d6fc37dc79428b2863ce8538ad Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Wed, 9 Jul 2014 11:25:38 +0200 Subject: [PATCH 09/11] GenericValue: add Move() --- include/rapidjson/document.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 66df387..74e5c66 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -551,6 +551,9 @@ public: return *this; } + //! Prepare Value for move semantics + /*! \return *this */ + GenericValue& Move() { return *this; } //@} //!@name Type From 58b741ac4b2c60f3e96a0bf9d76939bef207747c Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Wed, 9 Jul 2014 11:25:55 +0200 Subject: [PATCH 10/11] tutorial.md: document GenericValue::Move() --- doc/tutorial.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/doc/tutorial.md b/doc/tutorial.md index f35c4d3..cc858f8 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -313,6 +313,17 @@ Value o(kObjectType); This is called move assignment operator in C++11. As RapidJSON supports C++03, it adopts move semantics using assignment operator, and all other modifying function like `AddMember()`, `PushBack()`. +### Move semantics and temporary values {#TemporaryValues} + +Sometimes, it is convenient to construct a Value in place, before passing it to one of the "moving" functions, like `PushBack()` or `AddMember()`. As temporary objects can't be converted to proper Value references, the convenience function `Move()` is available: + +~~~~~~~~~~cpp +Value a(kArrayType); +// a.PushBack(Value(42)); // will not compile +a.PushBack(Value().SetInt(42)); // fluent API +a.PushBack(Value(42).Move()); // same as above +~~~~~~~~~~ + ## Create String {#CreateString} RapidJSON provide two strategies for storing string. From 12c5805c7edc8119b26badd3904cac5417c5f70d Mon Sep 17 00:00:00 2001 From: "Philipp A. Hartmann" Date: Wed, 9 Jul 2014 11:26:22 +0200 Subject: [PATCH 11/11] tutorial.md: update for new string API --- doc/tutorial.md | 55 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 48 insertions(+), 7 deletions(-) diff --git a/doc/tutorial.md b/doc/tutorial.md index cc858f8..ee5f873 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -350,15 +350,28 @@ In this example, we get the allocator from a `Document` instance. This is a comm Besides, the above `SetString()` requires length. This can handle null characters within a string. There is another `SetString()` overloaded function without the length parameter. And it assumes the input is null-terminated and calls a `strlen()`-like function to obtain the length. -Finally, for literal string or string with safe life-cycle can use const-string version of `SetString()`, which lacks allocator parameter: +Finally, for literal string or string with safe life-cycle can use const-string version of `SetString()`, which lacks allocator parameter. For string literals (or constant character arrays), simply passing the literal as parameter is safe and efficient: ~~~~~~~~~~cpp Value s; -s.SetString("rapidjson", 9); // faster, can contain null character -s.SetString("rapidjson"); // slower, assumes null-terminated +s.SetString("rapidjson"); // can contain null character, length derived at compile time s = "rapidjson"; // shortcut, same as above ~~~~~~~~~~ +For plain string pointers, the RapidJSON requires to mark a string as safe before using it without copying. This can be achieved by using the `StringRef` function: + +~~~~~~~~~cpp +const char * cstr = getenv("USER"); +size_t cstr_len = ...; // in case length is available +Value s; +// s.SetString(cstr); // will not compile +s.SetString(StringRef(cstr)); // ok, assume safe lifetime, null-terminated +s = StringRef(cstr); // shortcut, same as above +s.SetString(StringRef(cstr,cstr_len)); // faster, can contain null character +s = StringRef(cstr,cstr_len); // shortcut, same as above + +~~~~~~~~~ + ## Modify Array {#ModifyArray} Value with array type provides similar APIs as `std::vector`. @@ -368,7 +381,7 @@ Value with array type provides similar APIs as `std::vector`. * `template GenericValue& PushBack(T, Allocator&)` * `Value& PopBack()` -Note that, `Reserve(...)` and `PushBack(...)` may allocate memory, therefore requires an allocator. +Note that, `Reserve(...)` and `PushBack(...)` may allocate memory for the array elements, therefore require an allocator. Here is an example of `PushBack()`: @@ -383,14 +396,26 @@ for (int i = 5; i <= 10; i++) a.PushBack("Lua", allocator).PushBack("Mio", allocator); ~~~~~~~~~~ -Differs from STL, `PushBack()`/`PopBack()` returns the array reference itself. This is called fluent interface. +Differs from STL, `PushBack()`/`PopBack()` returns the array reference itself. This is called _fluent interface_. + +If you want to add a non-constant string or a string without sufficient lifetime (see [Create String](#CreateString)) to the array, you need to create a string Value by using the copy-string API. To avoid the need for an intermediate variable, you can use a [temporary value](#TemporaryValues) in place: + +~~~~~~~~~~cpp +// in-place Value parameter +contact.PushBack(Value("copy", document.GetAllocator()).Move(), // copy string + document.GetAllocator()); + +// explicit parameters +Value val("key", document.GetAllocator()); // copy string +contact.PushBack(val, document.GetAllocator()); +~~~~~~~~~~ ## Modify Object {#ModifyObject} Object is a collection of key-value pairs. Each key must be a string value. The way to manipulating object is to add/remove members: * `Value& AddMember(Value&, Value&, Allocator& allocator)` -* `Value& AddMember(const Ch*, Value&, Allocator&)` -* `template Value& AddMember(const Ch*, T value, Allocator&)` +* `Value& AddMember(StringRefType, Value&, Allocator&)` +* `template Value& AddMember(StringRefType, T value, Allocator&)` * `bool RemoveMember(const Ch*)` Here is an example. @@ -401,6 +426,22 @@ contact.AddMember("name", "Milo", document.GetAllocator()); contact.AddMember("married", true, document.GetAllocator()); ~~~~~~~~~~ +The `StringRefType` used as name parameter assumes the same interface as the `SetString` function for string values. These overloads are used to avoid the need for copying the `name` string, as constant key names are very common in JSON objects. + +If you need to create a name from a non-constant string or a string without sufficient lifetime (see [Create String](#CreateString)), you need to create a string Value by using the copy-string API. To avoid the need for an intermediate variable, you can use a [temporary value](#TemporaryValues) in place: + +~~~~~~~~~~cpp +// in-place Value parameter +contact.AddMember(Value("copy", document.GetAllocator()).Move(), // copy string + Value().Move(), // null value + document.GetAllocator()); + +// explicit parameters +Value key("key", document.GetAllocator()); // copy name string +Value val(42); // some value +contact.AddMember(key, val, document.GetAllocator()); +~~~~~~~~~~ + ## Deep Copy Value {#DeepCopyValue} If we really need to copy a DOM tree, we can use two APIs for deep copy: constructor with allocator, and `CopyFrom()`.