diff --git a/src/misc/pv/sharedVector.h b/src/misc/pv/sharedVector.h index e1a2f10..7e77f4a 100644 --- a/src/misc/pv/sharedVector.h +++ b/src/misc/pv/sharedVector.h @@ -15,6 +15,10 @@ #include #include +#if __cplusplus>=201103L +# include +#endif + #include #include "pv/sharedPtr.h" @@ -70,18 +74,24 @@ namespace detail { */ public: - +#if __cplusplus>=201103L + //! @brief Empty vector (not very interesting) + constexpr shared_vector_base() noexcept + :m_sdata(), m_offset(0), m_count(0), m_total(0) + {} +#else //! @brief Empty vector (not very interesting) shared_vector_base() :m_sdata(), m_offset(0), m_count(0), m_total(0) {} +#endif protected: // helper for constructors // Ensure that offset and size are zero when we are constructed with NULL void _null_input() { - if(!m_sdata.get()) { + if(!m_sdata) { m_offset = m_total = m_count = 0; } else { // ensure we won't have integer overflows later @@ -90,19 +100,12 @@ namespace detail { } public: -#ifdef _WIN32 template shared_vector_base(A* v, size_t o, size_t c) :m_sdata(v, detail::default_array_deleter()) ,m_offset(o), m_count(c), m_total(c) {_null_input();} -#else - template - shared_vector_base(A v, size_t o, size_t c) - :m_sdata(v, detail::default_array_deleter()) - ,m_offset(o), m_count(c), m_total(c) - {_null_input();} -#endif + shared_vector_base(const std::tr1::shared_ptr& d, size_t o, size_t c) :m_sdata(d), m_offset(o), m_count(c), m_total(c) {_null_input();} @@ -118,6 +121,17 @@ namespace detail { ,m_count(O.m_count), m_total(O.m_total) {} +#if __cplusplus >= 201103L + shared_vector_base(shared_vector_base &&O) + :m_sdata(std::move(O.m_sdata)) + ,m_offset(O.m_offset) + ,m_count(O.m_count) + ,m_total(O.m_total) + { + O.clear(); + } +#endif + protected: typedef typename meta::strip_const::type _E_non_const; public: @@ -132,7 +146,11 @@ namespace detail { { if(!O.unique()) throw std::runtime_error("Can't freeze non-unique vector"); +#if __cplusplus >= 201103L + m_sdata = std::move(O.m_sdata); +#else m_sdata = O.m_sdata; +#endif O.clear(); } @@ -146,7 +164,11 @@ namespace detail { ,m_total(O.m_total) { O.make_unique(); +#if __cplusplus >= 201103L + m_sdata = std::move(std::tr1::const_pointer_cast(O.m_sdata)); +#else m_sdata = std::tr1::const_pointer_cast(O.m_sdata); +#endif O.clear(); } @@ -162,6 +184,21 @@ namespace detail { return *this; } +#if __cplusplus >= 201103L + //! @brief Move an existing vector + shared_vector_base& operator=(shared_vector_base&& o) + { + if(&o!=this) { + m_sdata=std::move(o.m_sdata); + m_offset=o.m_offset; + m_count=o.m_count; + m_total=o.m_total; + o.clear(); + } + return *this; + } +#endif + //! @brief Swap the contents of this vector with another void swap(shared_vector_base& o) { if(&o!=this) { @@ -180,11 +217,12 @@ namespace detail { } //! @brief Data is not shared? - bool unique() const {return !m_sdata || m_sdata.unique();} + bool unique() const {return !m_sdata || m_sdata.use_count()<=1;} //! @brief Number of elements visible through this vector size_t size() const{return m_count;} + //! @brief shorthand for size()==0 bool empty() const{return !m_count;} @@ -272,9 +310,22 @@ public: // allow specialization for all E to be friends template friend class shared_vector; - //! @brief Empty vector (not very interesting) +#if __cplusplus>=201103L + constexpr shared_vector() noexcept :base_t() {} +#else shared_vector() :base_t() {} +#endif + +#if __cplusplus>=201103L + template + shared_vector(std::initializer_list L) + :base_t(new _E_non_const[L.size()], 0, L.size()) + { + _E_non_const *raw = const_cast<_E_non_const*>(data()); + std::copy(L.begin(), L.end(), raw); + } +#endif //! @brief Allocate (with new[]) a new vector of size c explicit shared_vector(size_t c) @@ -321,6 +372,11 @@ public: //! @brief Copy an existing vector of same type shared_vector(const shared_vector& o) :base_t(o) {} +#if __cplusplus>=201103L + //! @brief Move an existing vector of same type + shared_vector(shared_vector&& o) :base_t(std::move(o)) {} +#endif + //! @internal //! Internal for static_shared_vector_cast template @@ -342,6 +398,20 @@ public: :base_t(O,t) {} + inline shared_vector& operator=(const shared_vector& o) + { + this->base_t::operator=(o); + return *this; + } + +#if __cplusplus>=201103L + inline shared_vector& operator=(shared_vector&& o) + { + this->base_t::operator=(std::move(o)); + return *this; + } +#endif + size_t max_size() const{return ((size_t)-1)/sizeof(E);} size_t capacity() const { return this->m_total; } @@ -353,6 +423,9 @@ public: * does not increase. * * For notes on copying see docs for make_unique(). + * + * @throws std::bad_alloc if requested allocation can not be made + * @throws other exceptions from element copy ctor */ void reserve(size_t i) { if(this->unique() && i<=this->m_total) @@ -377,13 +450,16 @@ public: * as if make_unique() were called. This holds even if the size does not change. * * For notes on copying see docs for make_unique(). + * + * @throws std::bad_alloc if requested allocation can not be made + * @throws other exceptions from element copy ctor */ void resize(size_t i) { if(i==this->m_count) { make_unique(); return; } - if(this->m_sdata && this->m_sdata.unique()) { + if(this->m_sdata && this->m_sdata.use_count()==1) { // we have data and exclusive ownership of it if(i<=this->m_total) { // We have room to grow (or shrink)! @@ -438,10 +514,14 @@ public: } assert(original.unique()); @endcode + * + * @throws std::bad_alloc if requested allocation can not be made + * @throws other exceptions from element copy ctor */ void make_unique() { if(this->unique()) return; + // at this point we know that !!m_sdata, so get()!=NULL _E_non_const *d = new _E_non_const[this->m_total]; try { std::copy(this->m_sdata.get()+this->m_offset, @@ -527,10 +607,15 @@ public: // data access + //! @brief Return Base pointer pointer data() const{return this->m_sdata.get()+this->m_offset;} + //! @brief Member access + //! Undefined if empty()==true. reference operator[](size_t i) const {return this->m_sdata.get()[this->m_offset+i];} + //! @brief Member access + //! @throws std::out_of_range if i>=size(). reference at(size_t i) const { if(i>this->m_count) @@ -577,7 +662,11 @@ public: typedef std::tr1::shared_ptr shared_pointer_type; +#if __cplusplus>=201103L + constexpr shared_vector() noexcept :base_t(), m_vtype((ScalarType)-1) {} +#else shared_vector() :base_t(), m_vtype((ScalarType)-1) {} +#endif shared_vector(pointer v, size_t o, size_t c) :base_t(v,o,c), m_vtype((ScalarType)-1) {} @@ -593,6 +682,11 @@ public: shared_vector(const shared_vector& o) :base_t(o), m_vtype(o.m_vtype) {} +#if __cplusplus>=201103L + shared_vector(shared_vector&& o) + :base_t(std::move(o)), m_vtype(o.m_vtype) {} +#endif + //! @internal //! Internal for static_shared_vector_cast template @@ -623,6 +717,17 @@ public: return *this; } +#if __cplusplus>=201103L + shared_vector& operator=(shared_vector&& o) + { + if(&o!=this) { + this->base_t::operator=(std::move(o)); + m_vtype = o.m_vtype; + } + return *this; + } +#endif + size_t max_size() const{return (size_t)-1;} pointer data() const{ diff --git a/testApp/misc/testSharedVector.cpp b/testApp/misc/testSharedVector.cpp index f1bad00..7006030 100644 --- a/testApp/misc/testSharedVector.cpp +++ b/testApp/misc/testSharedVector.cpp @@ -12,7 +12,7 @@ #include -#include +#include #include #include "pv/sharedVector.h" @@ -20,16 +20,21 @@ using std::string; using namespace epics::pvData; -static void testEmpty() +namespace { + +void testEmpty() { testDiag("Test empty vector"); - epics::pvData::shared_vector empty, empty2; + epics::pvData::shared_vector empty, empty2, empty3(0u); testOk1(empty.size()==0); testOk1(empty.empty()); testOk1(empty.begin()==empty.end()); testOk1(empty.unique()); + testOk1(empty3.empty()); + testOk1(empty3.unique()); + testOk1(empty.data()==NULL); testOk1(empty==empty); @@ -38,7 +43,7 @@ static void testEmpty() testOk1(!(empty!=empty2)); } -static void testInternalAlloc() +void testInternalAlloc() { testDiag("Test vector alloc w/ new[]"); @@ -75,19 +80,17 @@ static void testInternalAlloc() testOk1(internal.data()==NULL); } -namespace { - //Note: STL shared_ptr requires that deletors be copy constructable - template - struct callCounter { - std::tr1::shared_ptr count; - callCounter():count(new int32){*count=0;} - callCounter(const callCounter& o):count(o.count) {}; - callCounter& operator=(const callCounter& o){count=o.count;} - void operator()(E){*count=1;} - }; -} +//Note: STL shared_ptr requires that deletors be copy constructable +template +struct callCounter { + std::tr1::shared_ptr count; + callCounter():count(new int32){*count=0;} + callCounter(const callCounter& o):count(o.count) {} + callCounter& operator=(const callCounter& o){count=o.count;} + void operator()(E){(*count)++;} +}; -static void testExternalAlloc() +void testExternalAlloc() { testDiag("Test vector external alloc"); @@ -133,7 +136,7 @@ static void testExternalAlloc() testOk1(*tracker.count==1); } -static void testShare() +void testShare() { testDiag("Test vector Sharing"); @@ -195,7 +198,7 @@ static void testShare() testOk1(two[19]==5000); } -static void testConst() +void testConst() { testDiag("Test constant vector"); @@ -236,7 +239,7 @@ static void testConst() testOk1(rodata.data()!=rodata2.data()); } -static void testSlice() +void testSlice() { testDiag("Test vector slicing"); @@ -286,7 +289,7 @@ static void testSlice() testOk1(half2.data()==NULL); } -static void testCapacity() +void testCapacity() { testDiag("Test vector capacity"); @@ -328,7 +331,7 @@ static void testCapacity() testOk1(vect[1]==124); } -static void testPush() +void testPush() { epics::pvData::shared_vector vect; @@ -353,7 +356,7 @@ static void testPush() testOk1(nallocs==26); } -static void testVoid() +void testVoid() { testDiag("Test vector cast to/from void"); @@ -373,7 +376,7 @@ static void testVoid() VV.clear(); } -static void testConstVoid() +void testConstVoid() { testDiag("Test vector cast to/from const void"); @@ -401,7 +404,7 @@ static void testConstVoid() struct dummyStruct {}; -static void testNonPOD() +void testNonPOD() { testDiag("Test vector of non-POD types"); @@ -429,7 +432,7 @@ static void testNonPOD() testOk1(structs2[1].get()==temp); } -static void testVectorConvert() +void testVectorConvert() { testDiag("Test shared_vector_convert"); @@ -474,7 +477,7 @@ static void testVectorConvert() testOk1(strings.at(0)=="42"); } -static void testWeak() +void testWeak() { testDiag("Test weak_ptr counting"); @@ -499,7 +502,7 @@ static void testWeak() testOk1(!data.unique()); } -static void testICE() +void testICE() { testDiag("Test freeze and thaw"); @@ -563,7 +566,6 @@ static void testICE() } } -static void testBad() { epics::pvData::shared_vector I; @@ -620,7 +622,6 @@ void testBad() //I = epics::pvData::that(CF); } -static void testAutoSwap() { epics::auto_ptr A(new int(42)), B(new int(43)); @@ -631,9 +632,71 @@ void testAutoSwap() testOk1(42==*B); } +void testCXX11Move() +{ +#if __cplusplus>=201103L + testDiag("Check std::move()"); + shared_vector A(4, 42), + B(std::move(A)); + + testOk1(A.unique()); + testOk1(B.unique()); + testOk1(A.empty()); + testOk1(B.size()==4); + testOk1(!B.empty() && B[0]==42); + + A = std::move(B); + + testOk1(A.unique()); + testOk1(B.unique()); + testOk1(B.empty()); + testOk1(A.size()==4); + testOk1(!A.empty() && A[0]==42); + + shared_vector C(shared_vector_convert(A)), + D(std::move(C)); + A.clear(); + + testOk1(C.unique()); + testOk1(D.unique()); + testOk1(C.empty()); + testOk1(D.size()==4*4); + + C = std::move(D); + + testOk1(C.unique()); + testOk1(D.unique()); + testOk1(C.size()==4*4); + testOk1(D.empty()); +#else + testSkip(18, "Not -std=c++11"); +#endif +} + +void testCXX11Init() +{ +#if __cplusplus>=201103L + testDiag("Check c++11 style array initialization"); + + shared_vector A = {1.0, 2.0, 3.0}; + + testOk1(A.size()==3); + + int32 sum = 0; + for (auto V: A) { + sum += V; + } + testOk1(sum==6); +#else + testSkip(2, "Not -std=c++11"); +#endif +} + +} // namespace + MAIN(testSharedVector) { - testPlan(169); + testPlan(191); testDiag("Tests for shared_vector"); testDiag("sizeof(shared_vector)=%lu", @@ -655,5 +718,7 @@ MAIN(testSharedVector) testICE(); testBad(); testAutoSwap(); + testCXX11Move(); + testCXX11Init(); return testDone(); }