Commit bc9d7866 authored by Milo Yip's avatar Milo Yip

Merge pull request #128 from pah/feature/cxx11-move

Initial C++11 move support
parents 15d70d6a 8ae2266c
......@@ -2004,7 +2004,8 @@ PREDEFINED = \
# definition found in the source code.
# This tag requires that the tag ENABLE_PREPROCESSING is set to YES.
EXPAND_AS_DEFINED =
EXPAND_AS_DEFINED = \
RAPIDJSON_NOEXCEPT
# If the SKIP_FUNCTION_MACROS tag is set to YES then doxygen's preprocessor will
# remove all references to function-like macros that are alone on a line, have
......
......@@ -24,6 +24,7 @@
/*! \file document.h */
#include "reader.h"
#include "internal/meta.h"
#include "internal/strfunc.h"
#include <new> // placement new
......@@ -38,15 +39,17 @@ RAPIDJSON_DIAG_OFF(effc++)
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_HAS_STDSTRING
#ifndef RAPIDJSON_HAS_STDSTRING
#ifdef RAPIDJSON_DOXYGEN_RUNNING
#define RAPIDJSON_HAS_STDSTRING 1 // force generation of documentation
#else
#define RAPIDJSON_HAS_STDSTRING 0 // no std::string support by default
#endif
#ifdef RAPIDJSON_HAS_STDSTRING
/*! \def RAPIDJSON_HAS_STDSTRING
\ingroup RAPIDJSON_CONFIG
\brief Enable RapidJSON support for \c std::string
By defining this preprocessor symbol, several convenience functions for using
By defining this preprocessor symbol to \c 1, several convenience functions for using
\ref rapidjson::GenericValue with \c std::string are enabled, especially
for construction and comparison.
......@@ -56,7 +59,6 @@ RAPIDJSON_DIAG_OFF(effc++)
#endif // RAPIDJSON_HAS_STDSTRING
#ifndef RAPIDJSON_NOMEMBERITERATORCLASS
#include "internal/meta.h"
#include <iterator> // std::iterator, std::random_access_iterator_tag
#endif
......@@ -277,7 +279,7 @@ struct GenericStringRef {
GenericValue instead.
*/
template<SizeType N>
GenericStringRef(const CharType (&str)[N])
GenericStringRef(const CharType (&str)[N]) RAPIDJSON_NOEXCEPT
: s(str), length(N-1) {}
//! Explicitly create string reference from \c const character pointer
......@@ -300,7 +302,7 @@ struct GenericStringRef {
GenericValue instead.
*/
explicit GenericStringRef(const CharType* str)
: s(str), length(internal::StrLen(str)){}
: s(str), length(internal::StrLen(str)){ RAPIDJSON_ASSERT(s != NULL); }
//! Create constant string reference from pointer and length
/*! \param str constant string, lifetime assumed to be longer than the use of the string in e.g. a GenericValue
......@@ -363,7 +365,7 @@ inline GenericStringRef<CharType> StringRef(const CharType* str, size_t length)
return GenericStringRef<CharType>(str, SizeType(length));
}
#ifdef RAPIDJSON_HAS_STDSTRING
#if RAPIDJSON_HAS_STDSTRING
//! Mark a string object as constant string
/*! Mark a string object (e.g. \c std::string) as a "string literal".
This function can be used to avoid copying a string to be referenced as a
......@@ -430,7 +432,14 @@ public:
//@{
//! Default constructor creates a null value.
GenericValue() : data_(), flags_(kNullFlag) {}
GenericValue() RAPIDJSON_NOEXCEPT : data_(), flags_(kNullFlag) {}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
//! Move constructor in C++11
GenericValue(GenericValue&& rhs) RAPIDJSON_NOEXCEPT : data_(rhs.data_), flags_(rhs.flags_) {
rhs.flags_ = kNullFlag; // give up contents
}
#endif
private:
//! Copy constructor is not permitted.
......@@ -443,7 +452,7 @@ public:
\param type Type of the value.
\note Default content for number is zero.
*/
GenericValue(Type type) : data_(), flags_() {
GenericValue(Type type) RAPIDJSON_NOEXCEPT : data_(), flags_() {
static const unsigned defaultFlags[7] = {
kNullFlag, kFalseFlag, kTrueFlag, kObjectFlag, kArrayFlag, kConstStringFlag,
kNumberAnyFlag
......@@ -470,9 +479,9 @@ public:
*/
#ifndef RAPIDJSON_DOXYGEN_RUNNING // hide SFINAE from Doxygen
template <typename T>
explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame<T,bool>)))
explicit GenericValue(T b, RAPIDJSON_ENABLEIF((internal::IsSame<T,bool>))) RAPIDJSON_NOEXCEPT
#else
explicit GenericValue(bool b)
explicit GenericValue(bool b) RAPIDJSON_NOEXCEPT
#endif
: data_(), flags_(b ? kTrueFlag : kFalseFlag) {
// safe-guard against failing SFINAE
......@@ -480,21 +489,21 @@ public:
}
//! Constructor for int value.
explicit GenericValue(int i) : data_(), flags_(kNumberIntFlag) {
explicit GenericValue(int i) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberIntFlag) {
data_.n.i64 = i;
if (i >= 0)
flags_ |= kUintFlag | kUint64Flag;
}
//! Constructor for unsigned value.
explicit GenericValue(unsigned u) : data_(), flags_(kNumberUintFlag) {
explicit GenericValue(unsigned u) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUintFlag) {
data_.n.u64 = u;
if (!(u & 0x80000000))
flags_ |= kIntFlag | kInt64Flag;
}
//! Constructor for int64_t value.
explicit GenericValue(int64_t i64) : data_(), flags_(kNumberInt64Flag) {
explicit GenericValue(int64_t i64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberInt64Flag) {
data_.n.i64 = i64;
if (i64 >= 0) {
flags_ |= kNumberUint64Flag;
......@@ -508,7 +517,7 @@ public:
}
//! Constructor for uint64_t value.
explicit GenericValue(uint64_t u64) : data_(), flags_(kNumberUint64Flag) {
explicit GenericValue(uint64_t u64) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberUint64Flag) {
data_.n.u64 = u64;
if (!(u64 & RAPIDJSON_UINT64_C2(0x80000000, 0x00000000)))
flags_ |= kInt64Flag;
......@@ -519,13 +528,13 @@ public:
}
//! Constructor for double value.
explicit GenericValue(double d) : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; }
explicit GenericValue(double d) RAPIDJSON_NOEXCEPT : data_(), flags_(kNumberDoubleFlag) { data_.n.d = d; }
//! Constructor for constant string (i.e. do not make a copy of string)
GenericValue(const Ch* s, SizeType length) : data_(), flags_() { SetStringRaw(StringRef(s, length)); }
GenericValue(const Ch* s, SizeType length) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(StringRef(s, length)); }
//! Constructor for constant string (i.e. do not make a copy of string)
explicit GenericValue(StringRefType s) : data_(), flags_() { SetStringRaw(s); }
explicit GenericValue(StringRefType s) RAPIDJSON_NOEXCEPT : data_(), flags_() { SetStringRaw(s); }
//! Constructor for copy-string (i.e. do make a copy of string)
GenericValue(const Ch* s, SizeType length, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s, length), allocator); }
......@@ -533,7 +542,7 @@ public:
//! Constructor for copy-string (i.e. do make a copy of string)
GenericValue(const Ch*s, Allocator& allocator) : data_(), flags_() { SetStringRaw(StringRef(s), allocator); }
#ifdef RAPIDJSON_HAS_STDSTRING
#if RAPIDJSON_HAS_STDSTRING
//! Constructor for copy-string from a string object (i.e. do make a copy of string)
/*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
*/
......@@ -576,19 +585,26 @@ public:
//! Assignment with move semantics.
/*! \param rhs Source of the assignment. It will become a null value after assignment.
*/
GenericValue& operator=(GenericValue& rhs) {
GenericValue& operator=(GenericValue& rhs) RAPIDJSON_NOEXCEPT {
RAPIDJSON_ASSERT(this != &rhs);
this->~GenericValue();
RawAssign(rhs);
return *this;
}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
//! Move assignment in C++11
GenericValue& operator=(GenericValue&& rhs) RAPIDJSON_NOEXCEPT {
return *this = rhs.Move();
}
#endif
//! Assignment of constant string reference (no copy)
/*! \param str Constant string reference to be assigned
\note This overload is needed to avoid clashes with the generic primitive type assignment overload below.
\see GenericStringRef, operator=(T)
*/
GenericValue& operator=(StringRefType str) {
GenericValue& operator=(StringRefType str) RAPIDJSON_NOEXCEPT {
GenericValue s(str);
return *this = s;
}
......@@ -631,7 +647,7 @@ public:
\param other Another value.
\note Constant complexity.
*/
GenericValue& Swap(GenericValue& other) {
GenericValue& Swap(GenericValue& other) RAPIDJSON_NOEXCEPT {
GenericValue temp;
temp.RawAssign(*this);
RawAssign(other);
......@@ -641,7 +657,7 @@ public:
//! Prepare Value for move semantics
/*! \return *this */
GenericValue& Move() { return *this; }
GenericValue& Move() RAPIDJSON_NOEXCEPT { return *this; }
//@}
//!@name Equal-to and not-equal-to operators
......@@ -693,7 +709,7 @@ public:
//! Equal-to operator with const C-string pointer
bool operator==(const Ch* rhs) const { return *this == GenericValue(StringRef(rhs)); }
#ifdef RAPIDJSON_HAS_STDSTRING
#if RAPIDJSON_HAS_STDSTRING
//! Equal-to operator with string object
/*! \note Requires the definition of the preprocessor symbol \ref RAPIDJSON_HAS_STDSTRING.
*/
......@@ -921,6 +937,23 @@ public:
return *this;
}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
GenericValue& AddMember(GenericValue&& name, GenericValue&& value, Allocator& allocator) {
return AddMember(name, value, allocator);
}
GenericValue& AddMember(GenericValue&& name, GenericValue& value, Allocator& allocator) {
return AddMember(name, value, allocator);
}
GenericValue& AddMember(GenericValue& name, GenericValue&& value, Allocator& allocator) {
return AddMember(name, value, allocator);
}
GenericValue& AddMember(StringRefType name, GenericValue&& value, Allocator& allocator) {
GenericValue n(name);
return AddMember(n, value, allocator);
}
#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
//! Add a member (name-value pair) to the object.
/*! \param name A constant string reference as name of member.
\param value Value of any type.
......@@ -1162,6 +1195,12 @@ int z = a[0u].GetInt(); // This works too.
return *this;
}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
GenericValue& PushBack(GenericValue&& value, Allocator& allocator) {
return PushBack(value, allocator);
}
#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
//! Append a constant string reference at the end of the array.
/*! \param value Constant string reference to be appended.
\param allocator Allocator for reallocating memory. It must be the same one used previously. Commonly use GenericDocument::GetAllocator().
......@@ -1316,7 +1355,7 @@ int z = a[0u].GetInt(); // This works too.
*/
GenericValue& SetString(const Ch* s, Allocator& allocator) { return SetString(s, internal::StrLen(s), allocator); }
#ifdef RAPIDJSON_HAS_STDSTRING
#if RAPIDJSON_HAS_STDSTRING
//! Set this value as a string by copying from source string.
/*! \param s source string.
\param allocator Allocator for allocating copied buffer. Commonly use GenericDocument::GetAllocator().
......@@ -1502,7 +1541,7 @@ private:
}
//! Initialize this value as constant string, without calling destructor.
void SetStringRaw(StringRefType s) {
void SetStringRaw(StringRefType s) RAPIDJSON_NOEXCEPT {
flags_ = kConstStringFlag;
data_.s.str = s;
data_.s.length = s.length;
......@@ -1526,7 +1565,7 @@ private:
}
//! Assignment without calling destructor
void RawAssign(GenericValue& rhs) {
void RawAssign(GenericValue& rhs) RAPIDJSON_NOEXCEPT {
data_ = rhs.data_;
flags_ = rhs.flags_;
rhs.flags_ = kNullFlag;
......
......@@ -21,6 +21,10 @@
#ifndef RAPIDJSON_INTERNAL_META_H_
#define RAPIDJSON_INTERNAL_META_H_
#ifndef RAPIDJSON_RAPIDJSON_H_
#error <rapidjson.h> not yet included. Do not include this file directly.
#endif
#ifdef __GNUC__
RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(effc++)
......@@ -30,7 +34,7 @@ RAPIDJSON_DIAG_PUSH
RAPIDJSON_DIAG_OFF(6334)
#endif
#ifdef RAPIDJSON_HAS_CXX11_TYPETRAITS
#if RAPIDJSON_HAS_CXX11_TYPETRAITS
#include <type_traits>
#endif
......@@ -100,7 +104,7 @@ template <typename T> struct IsPointer<T*> : TrueType {};
///////////////////////////////////////////////////////////////////////////////
// IsBaseOf
//
#ifdef RAPIDJSON_HAS_CXX11_TYPETRAITS
#if RAPIDJSON_HAS_CXX11_TYPETRAITS
template <typename B, typename D> struct IsBaseOf
: BoolType< ::std::is_base_of<B,D>::value> {};
......
......@@ -318,7 +318,12 @@ template<int x> struct StaticAssertTest {};
///////////////////////////////////////////////////////////////////////////////
// RAPIDJSON_DIAG_PUSH/POP, RAPIDJSON_DIAG_OFF
#if defined(__clang__) || (defined(__GNUC__) && RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) >= RAPIDJSON_VERSION_CODE(4,2,0))
#if defined(__GNUC__)
#define RAPIDJSON_GNUC \
RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__)
#endif
#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,2,0))
#define RAPIDJSON_PRAGMA(x) _Pragma(RAPIDJSON_STRINGIFY(x))
#define RAPIDJSON_DIAG_PRAGMA(x) RAPIDJSON_PRAGMA(GCC diagnostic x)
......@@ -326,7 +331,7 @@ template<int x> struct StaticAssertTest {};
RAPIDJSON_DIAG_PRAGMA(ignored RAPIDJSON_STRINGIFY(RAPIDJSON_JOIN(-W,x)))
// push/pop support in Clang and GCC>=4.6
#if defined(__clang__) || (defined(__GNUC__) && RAPIDJSON_VERSION_CODE(__GNUC__,__GNUC_MINOR__,__GNUC_PATCHLEVEL__) >= RAPIDJSON_VERSION_CODE(4,6,0))
#if defined(__clang__) || (defined(RAPIDJSON_GNUC) && RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0))
#define RAPIDJSON_DIAG_PUSH RAPIDJSON_DIAG_PRAGMA(push)
#define RAPIDJSON_DIAG_POP RAPIDJSON_DIAG_PRAGMA(pop)
#else // GCC >= 4.2, < 4.6
......@@ -352,6 +357,42 @@ template<int x> struct StaticAssertTest {};
#endif // RAPIDJSON_DIAG_*
///////////////////////////////////////////////////////////////////////////////
// C++11 features
#ifndef RAPIDJSON_HAS_CXX11_RVALUE_REFS
#if defined(__clang__)
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS __has_feature(cxx_rvalue_references)
#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,3,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__)) || \
(defined(_MSC_VER) && _MSC_VER >= 1600)
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 1
#else
#define RAPIDJSON_HAS_CXX11_RVALUE_REFS 0
#endif
#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
#ifndef RAPIDJSON_HAS_CXX11_NOEXCEPT
#if defined(__clang__)
#define RAPIDJSON_HAS_CXX11_NOEXCEPT __has_feature(cxx_noexcept)
#elif (defined(RAPIDJSON_GNUC) && (RAPIDJSON_GNUC >= RAPIDJSON_VERSION_CODE(4,6,0)) && defined(__GXX_EXPERIMENTAL_CXX0X__))
// (defined(_MSC_VER) && _MSC_VER >= ????) // not yet supported
#define RAPIDJSON_HAS_CXX11_NOEXCEPT 1
#else
#define RAPIDJSON_HAS_CXX11_NOEXCEPT 0
#endif
#endif
#if RAPIDJSON_HAS_CXX11_NOEXCEPT
#define RAPIDJSON_NOEXCEPT noexcept
#else
#define RAPIDJSON_NOEXCEPT /* noexcept */
#endif // RAPIDJSON_HAS_CXX11_NOEXCEPT
// no automatic detection, yet
#ifndef RAPIDJSON_HAS_CXX11_TYPETRAITS
#define RAPIDJSON_HAS_CXX11_TYPETRAITS 0
#endif
//!@endcond
///////////////////////////////////////////////////////////////////////////////
......
......@@ -24,7 +24,7 @@
using namespace rapidjson;
TEST(Value, default_constructor) {
TEST(Value, DefaultConstructor) {
Value x;
EXPECT_EQ(kNullType, x.GetType());
EXPECT_TRUE(x.IsNull());
......@@ -38,7 +38,32 @@ TEST(Value, default_constructor) {
// Value y = x;
//}
TEST(Value, assignment_operator) {
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
TEST(Value, MoveConstructor) {
typedef GenericValue<UTF8<>, CrtAllocator> Value;
Value::AllocatorType allocator;
Value x((Value(kArrayType)));
x.Reserve(4u, allocator);
x.PushBack(1, allocator).PushBack(2, allocator).PushBack(3, allocator).PushBack(4, allocator);
EXPECT_TRUE(x.IsArray());
EXPECT_EQ(4u, x.Size());
// Value y(x); // should not compile
Value y(std::move(x));
EXPECT_TRUE(x.IsNull());
EXPECT_TRUE(y.IsArray());
EXPECT_EQ(4u, y.Size());
// Value z = y; // should not compile
Value z = std::move(y);
EXPECT_TRUE(y.IsNull());
EXPECT_TRUE(z.IsArray());
EXPECT_EQ(4u, z.Size());
}
#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
TEST(Value, AssignmentOperator) {
Value x(1234);
Value y;
y = x;
......@@ -63,6 +88,22 @@ TEST(Value, assignment_operator) {
y = StringRef(mstr);
EXPECT_TRUE(y.IsString());
EXPECT_EQ(y.GetString(),mstr);
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
// C++11 move assignment
x = Value("World");
EXPECT_TRUE(x.IsString());
EXPECT_STREQ("World", x.GetString());
x = std::move(y);
EXPECT_TRUE(y.IsNull());
EXPECT_TRUE(x.IsString());
EXPECT_EQ(x.GetString(), mstr);
y = std::move(Value().SetInt(1234));
EXPECT_TRUE(y.IsInt());
EXPECT_EQ(1234, y);
#endif // RAPIDJSON_HAS_CXX11_RVALUE_REFS
}
template <typename A, typename B>
......@@ -81,7 +122,7 @@ void TestUnequal(const A& a, const B& b) {
EXPECT_TRUE (b != a);
}
TEST(Value, equalto_operator) {
TEST(Value, EqualtoOperator) {
Value::AllocatorType allocator;
Value x(kObjectType);
x.AddMember("hello", "world", allocator)
......@@ -555,7 +596,7 @@ TEST(Value, String) {
EXPECT_STREQ("World", w.GetString());
EXPECT_EQ(5u, w.GetStringLength());
#ifdef RAPIDJSON_HAS_STDSTRING
#if RAPIDJSON_HAS_STDSTRING
{
std::string str = "Hello World";
str[5] = '\0';
......@@ -643,6 +684,21 @@ TEST(Value, Array) {
EXPECT_TRUE(y[4u].IsString());
EXPECT_STREQ("foo", y[4u].GetString());
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
// PushBack(GenericValue&&, Allocator&);
{
Value y(kArrayType);
y.PushBack(Value(true), allocator);
y.PushBack(std::move(Value(kArrayType).PushBack(Value(1), allocator).PushBack("foo", allocator)), allocator);
EXPECT_EQ(2u, y.Size());
EXPECT_TRUE(y[0u].IsTrue());
EXPECT_TRUE(y[1u].IsArray());
EXPECT_EQ(2u, y[1u].Size());
EXPECT_TRUE(y[1u][0u].IsInt());
EXPECT_TRUE(y[1u][1u].IsString());
}
#endif
// iterator
Value::ValueIterator itr = x.Begin();
EXPECT_TRUE(itr != x.End());
......@@ -751,7 +807,6 @@ TEST(Value, Array) {
}
// Working in gcc without C++11, but VS2013 cannot compile. To be diagnosed.
#if 0
// http://en.wikipedia.org/wiki/Erase-remove_idiom
x.Clear();
for (int i = 0; i < 10; i++)
......@@ -760,11 +815,11 @@ TEST(Value, Array) {
else
x.PushBack(Value(kNullType).Move(), allocator);
x.Erase(std::remove(x.Begin(), x.End(), Value(kNullType)), x.End());
const Value null(kNullType);
x.Erase(std::remove(x.Begin(), x.End(), null), x.End());
EXPECT_EQ(5u, x.Size());
for (int i = 0; i < 5; i++)
EXPECT_EQ(i * 2, x[i]);
#endif
// SetArray()
Value z;
......@@ -818,6 +873,22 @@ TEST(Value, Object) {
EXPECT_EQ(8u, o.MemberCount());
}
#if RAPIDJSON_HAS_CXX11_RVALUE_REFS
// AddMember(GenericValue&&, ...) variants
{
Value o(kObjectType);
o.AddMember(Value("true"), Value(true), allocator);
o.AddMember(Value("false"), Value(false).Move(), allocator); // value is lvalue ref
o.AddMember(Value("int").Move(), Value(-1), allocator); // name is lvalue ref
o.AddMember("uint", std::move(Value().SetUint(1u)), allocator); // name is literal, value is rvalue
EXPECT_TRUE(o["true"].GetBool());
EXPECT_FALSE(o["false"].GetBool());
EXPECT_EQ(-1, o["int"].GetInt());
EXPECT_EQ(1u, o["uint"].GetUint());
EXPECT_EQ(4u, o.MemberCount());
}
#endif
// Tests a member with null character
Value name;
const Value C0D("C\0D", 3);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment