From e82d3af67e8b8aab2e1fbb4bbb557c8e17677aa8 Mon Sep 17 00:00:00 2001 From: vng Date: Thu, 26 Mar 2015 21:10:54 +0300 Subject: [PATCH] Added move actor and move push_back to the buffer_vector. --- base/base_tests/buffer_vector_test.cpp | 64 ++++++++++++++- base/buffer_vector.hpp | 108 +++++++++++++++++++++---- std/type_traits.hpp | 7 ++ 3 files changed, 163 insertions(+), 16 deletions(-) diff --git a/base/base_tests/buffer_vector_test.cpp b/base/base_tests/buffer_vector_test.cpp index 4e843a1b2c..4e85d4ca96 100644 --- a/base/base_tests/buffer_vector_test.cpp +++ b/base/base_tests/buffer_vector_test.cpp @@ -1,8 +1,7 @@ -#include "../SRC_FIRST.hpp" - #include "../../testing/testing.hpp" #include "../buffer_vector.hpp" +#include "../string_utils.hpp" namespace @@ -244,3 +243,64 @@ UNIT_TEST(BufferVectorEquality) v1.push_back(999); TEST_NOT_EQUAL(v1, v2, ()); } + +namespace +{ + +struct CopyCtorChecker +{ + string m_s; + + CopyCtorChecker() = default; + CopyCtorChecker(char const * s) : m_s(s) {} + CopyCtorChecker(CopyCtorChecker const & rhs) + { + TEST(rhs.m_s.empty(), ("Copy ctor is called only in resize with default element")); + } + CopyCtorChecker(CopyCtorChecker && rhs) = default; + CopyCtorChecker & operator=(CopyCtorChecker const &) + { + TEST(false, ("Assigment operator should not be called")); + return *this; + } +}; + +void swap(CopyCtorChecker & r1, CopyCtorChecker & r2) +{ + r1.m_s.swap(r2.m_s); +} + +typedef buffer_vector VectorT; + +VectorT GetVector() +{ + VectorT v; + v.push_back("0"); + v.push_back("1"); + return v; +} + +void TestVector(VectorT const & v, size_t sz) +{ + TEST_EQUAL(v.size(), sz, ()); + for (size_t i = 0; i < sz; ++i) + TEST_EQUAL(v[i].m_s, strings::to_string(i), ()); +} + +} + +UNIT_TEST(BufferVectorMove) +{ + VectorT v1 = GetVector(); + TestVector(v1, 2); + + v1.push_back("2"); + TestVector(v1, 3); + + VectorT v2(move(v1)); + TestVector(v2, 3); + + VectorT().swap(v1); + v1 = move(v2); + TestVector(v1, 3); +} diff --git a/base/buffer_vector.hpp b/base/buffer_vector.hpp index 035a412f55..61e45fc300 100644 --- a/base/buffer_vector.hpp +++ b/base/buffer_vector.hpp @@ -1,9 +1,13 @@ #pragma once #include "assert.hpp" #include "swap.hpp" +#include "stl_iterator.hpp" #include "../std/algorithm.hpp" #include "../std/vector.hpp" +#include "../std/type_traits.hpp" +#include "../std/utility.hpp" +#include "../std/cstring.hpp" // for memcpy template class buffer_vector @@ -14,6 +18,39 @@ private: size_t m_size; vector m_dynamic; + inline bool IsDynamic() const { return m_size == USE_DYNAMIC; } + + /// @todo clang on linux doesn't have is_trivially_copyable. +#ifndef OMIM_OS_LINUX + template + typename enable_if::value, void>::type + MoveStatic(buffer_vector & rhs) + { + memcpy(m_static, rhs.m_static, rhs.m_size*sizeof(T)); + } + template + typename enable_if::value, void>::type + MoveStatic(buffer_vector & rhs) + { + for (size_t i = 0; i < rhs.m_size; ++i) + Swap(m_static[i], rhs.m_static[i]); + } +#else + template + typename enable_if::value, void>::type + MoveStatic(buffer_vector & rhs) + { + memcpy(m_static, rhs.m_static, rhs.m_size*sizeof(T)); + } + template + typename enable_if::value, void>::type + MoveStatic(buffer_vector & rhs) + { + for (size_t i = 0; i < rhs.m_size; ++i) + Swap(m_static[i], rhs.m_static[i]); + } +#endif + public: typedef T value_type; typedef T const & const_reference; @@ -34,15 +71,39 @@ public: } template - explicit buffer_vector(IterT beg, IterT end) : m_size(0) + buffer_vector(IterT beg, IterT end) : m_size(0) { assign(beg, end); } + buffer_vector(buffer_vector const &) = default; + buffer_vector & operator=(buffer_vector const &) = default; + + buffer_vector(buffer_vector && rhs) + : m_size(rhs.m_size), m_dynamic(move(rhs.m_dynamic)) + { + if (!IsDynamic()) + MoveStatic(rhs); + + rhs.m_size = 0; + } + + buffer_vector & operator=(buffer_vector && rhs) + { + m_size = rhs.m_size; + m_dynamic = move(rhs.m_dynamic); + + if (!IsDynamic()) + MoveStatic(rhs); + + rhs.m_size = 0; + return *this; + } + template void append(IterT beg, IterT end) { - if (m_size == USE_DYNAMIC) + if (IsDynamic()) m_dynamic.insert(m_dynamic.end(), beg, end); else { @@ -64,7 +125,7 @@ public: template void assign(IterT beg, IterT end) { - if (m_size == USE_DYNAMIC) + if (IsDynamic()) m_dynamic.assign(beg, end); else { @@ -75,13 +136,13 @@ public: void reserve(size_t n) { - if (m_size == USE_DYNAMIC || n > N) + if (IsDynamic() || n > N) m_dynamic.reserve(n); } void resize_no_init(size_t n) { - if (m_size == USE_DYNAMIC) + if (IsDynamic()) m_dynamic.resize(n); else { @@ -99,7 +160,7 @@ public: void resize(size_t n, T c = T()) { - if (m_size == USE_DYNAMIC) + if (IsDynamic()) m_dynamic.resize(n, c); else { @@ -122,7 +183,7 @@ public: void clear() { - if (m_size == USE_DYNAMIC) + if (IsDynamic()) m_dynamic.clear(); else m_size = 0; @@ -136,7 +197,7 @@ public: //@{ T const * data() const { - if (m_size == USE_DYNAMIC) + if (IsDynamic()) { ASSERT ( !m_dynamic.empty(), () ); return &m_dynamic[0]; @@ -147,7 +208,7 @@ public: T * data() { - if (m_size == USE_DYNAMIC) + if (IsDynamic()) { ASSERT ( !m_dynamic.empty(), () ); return &m_dynamic[0]; @@ -163,8 +224,8 @@ public: T * end() { return data() + size(); } //@} - bool empty() const { return (m_size == USE_DYNAMIC ? m_dynamic.empty() : m_size == 0); } - size_t size() const { return (m_size == USE_DYNAMIC ? m_dynamic.size() : m_size); } + bool empty() const { return (IsDynamic() ? m_dynamic.empty() : m_size == 0); } + size_t size() const { return (IsDynamic() ? m_dynamic.size() : m_size); } T const & front() const { @@ -208,7 +269,7 @@ public: void push_back(T const & t) { - if (m_size == USE_DYNAMIC) + if (IsDynamic()) m_dynamic.push_back(t); else { @@ -225,9 +286,28 @@ public: } } + void push_back(T && t) + { + if (IsDynamic()) + m_dynamic.push_back(move(t)); + else + { + if (m_size < N) + Swap(m_static[m_size++], t); + else + { + ASSERT_EQUAL(m_size, N, ()); + m_dynamic.reserve(N + 1); + SwitchToDynamic(); + m_dynamic.push_back(move(t)); + ASSERT_EQUAL(m_dynamic.size(), N + 1, ()); + } + } + } + void pop_back() { - if (m_size == USE_DYNAMIC) + if (IsDynamic()) m_dynamic.pop_back(); else { @@ -242,7 +322,7 @@ public: ASSERT_GREATER_OR_EQUAL(pos, 0, ()); ASSERT_LESS_OR_EQUAL(pos, static_cast(size()), ()); - if (m_size == USE_DYNAMIC) + if (IsDynamic()) m_dynamic.insert(m_dynamic.begin() + pos, beg, end); else { diff --git a/std/type_traits.hpp b/std/type_traits.hpp index 659b6ba275..1704426238 100644 --- a/std/type_traits.hpp +++ b/std/type_traits.hpp @@ -26,6 +26,13 @@ using std::is_signed; using std::is_unsigned; using std::is_floating_point; using std::is_integral; +using std::is_arithmetic; +using std::is_pod; + +/// @todo clang on linux doesn't have is_trivially_copyable. +#ifndef OMIM_OS_LINUX +using std::is_trivially_copyable; +#endif #ifdef DEBUG_NEW #define new DEBUG_NEW