diff --git a/build/Doxyfile b/build/Doxyfile index c74b79d..d2a57d5 100644 --- a/build/Doxyfile +++ b/build/Doxyfile @@ -1990,7 +1990,9 @@ 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_DOXYGEN_RUNNING \ + 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 diff --git a/doc/tutorial.md b/doc/tutorial.md index f35c4d3..ee5f873 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. @@ -339,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`. @@ -357,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()`: @@ -372,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. @@ -390,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()`. diff --git a/include/rapidjson/document.h b/include/rapidjson/document.h index 43f5049..74e5c66 100644 --- a/include/rapidjson/document.h +++ b/include/rapidjson/document.h @@ -174,6 +174,150 @@ struct GenericMemberIterator { #endif // RAPIDJSON_NOMEMBERITERATORCLASS +/////////////////////////////////////////////////////////////////////////////// +// 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); } + + //! implicit conversion to plain CharType pointer + operator const Ch *() const { return s; } + + 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 */; +}; + +//! 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)); +} + /////////////////////////////////////////////////////////////////////////////// // GenericValue @@ -196,6 +340,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,8 +352,8 @@ 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); public: @@ -231,14 +376,25 @@ 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 > 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) { @@ -283,16 +439,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 +495,30 @@ public: return *this; } + //! 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 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. + + \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 - GenericValue& operator=(T value) { + RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) + operator=(T value) { this->~GenericValue(); new (this) GenericValue(value); return *this; @@ -377,6 +551,9 @@ public: return *this; } + //! Prepare Value for move semantics + /*! \return *this */ + GenericValue& Move() { return *this; } //@} //!@name Type @@ -410,6 +587,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; } //@} @@ -418,6 +597,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. @@ -428,7 +608,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 +661,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); } @@ -504,9 +684,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()); @@ -530,19 +712,53 @@ 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. + /*! \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); } - 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. + /*! \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 - 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 +769,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); } @@ -599,6 +815,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. @@ -645,7 +862,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) { @@ -657,12 +874,14 @@ int z = a[0u].GetInt(); // This works too. return *this; } - //! Append a value 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. + //! Append a GenericValue at the end of the array. + /*! \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()); @@ -672,8 +891,37 @@ int z = a[0u].GetInt(); // This works too. return *this; } + //! 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(.) + /*! \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 - GenericValue& PushBack(T value, Allocator& allocator) { + RAPIDJSON_DISABLEIF_RETURN(internal::IsPointer,GenericValue&) + PushBack(T value, Allocator& allocator) { GenericValue v(value); return PushBack(v, allocator); } @@ -727,30 +975,35 @@ 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) { 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. + \post IsString() == true && GetString() == s && GetStringLength() == s.length */ - 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. \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(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(). + \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) { SetString(s, internal::StrLen(s), allocator); return *this; } + GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); } //@} @@ -906,21 +1159,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 0861a48..b8d3295 100644 --- a/include/rapidjson/internal/meta.h +++ b/include/rapidjson/internal/meta.h @@ -24,11 +24,14 @@ 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 IsPointer : FalseType {}; +template struct IsPointer : TrueType {}; template struct IsMoreConst { @@ -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 diff --git a/test/unittest/valuetest.cpp b/test/unittest/valuetest.cpp index 93d4eb0..fcf36a3 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,34 @@ 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); + + // 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); name.SetString(C0D.GetString(), 3); value.SetString("CherryD", 7); @@ -523,7 +625,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 +719,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 +732,7 @@ TEST(Value, BigNestedObject) { object.AddMember(name, number, allocator); } + // x.AddMember(name1, object, allocator); // should not compile x.AddMember(name, object, allocator); }