diff --git a/src/misc/pv/sharedVector.h b/src/misc/pv/sharedVector.h index e3da8d9..b092d91 100644 --- a/src/misc/pv/sharedVector.h +++ b/src/misc/pv/sharedVector.h @@ -326,7 +326,7 @@ public: //! Internal for static_shared_vector_cast template shared_vector(const shared_vector &src, - typename meta::is_void::type) + detail::_shared_vector_cast_tag) :base_t(std::tr1::static_pointer_cast(src.dataPtr()), src.dataOffset()/sizeof(E), src.dataCount()/sizeof(E)) @@ -432,8 +432,8 @@ public: shared_vector original(...); if(!original.unique()){ - shared_vector temp(myallocator(original.size()), - 0, original.size()); + std::tr1::shared_ptr sptr(myalloc(original.size()), myfree); + shared_vector temp(sptr, 0, original.size()); std::copy(original.begin(), original.end(), temp.begin()); original.swap(temp); } @@ -532,6 +532,20 @@ public: * * Does not allow access or iteration of contents * other than as void* or const void* + * + * In order to support shared_vector_convert<>() + * information about the type of the underlying allocation + * is stored. + * This is implicitly set by static_shared_vector_cast<>() + * and may be explicitly checked/changed using + * original_type()/set_original_type(). + * + * A shared_vector directly constructed + * from a smart pointer does not have an associated + * original_type(). + * Use epics::pvData::ScalarTypeFunc::allocArray() + * to convienently allocate an array with a known + * original_type(). */ template class shared_vector::type > @@ -539,7 +553,11 @@ class shared_vector::type > { typedef detail::shared_vector_base base_t; ScalarType m_vtype; + + // allow specialization for all E to be friends + template friend class shared_vector; public: + typedef E value_type; typedef E* pointer; typedef ptrdiff_t difference_type; typedef size_t size_type; @@ -566,7 +584,7 @@ public: //! Internal for static_shared_vector_cast template shared_vector(const shared_vector &src, - typename meta::is_not_void::type) + detail::_shared_vector_cast_tag) :base_t(std::tr1::static_pointer_cast(src.dataPtr()), src.dataOffset()*sizeof(FROM), src.dataCount()*sizeof(FROM)) @@ -575,12 +593,12 @@ public: shared_vector(shared_vector& O, detail::_shared_vector_freeze_tag t) - :base_t(O,t) + :base_t(O,t), m_vtype(O.m_vtype) {} shared_vector(shared_vector& O, detail::_shared_vector_thaw_tag t) - :base_t(O,t) + :base_t(O,t), m_vtype(O.m_vtype) {} shared_vector& operator=(const shared_vector& o) @@ -602,6 +620,64 @@ public: ScalarType original_type() const {return m_vtype;} }; +namespace detail { + template + struct static_shared_vector_caster { /* no default */ }; + // from void to non-void with same const-ness + template + struct static_shared_vector_caster, meta::is_not_void >::type> { + static inline shared_vector op(const shared_vector& src) { + return shared_vector(src, detail::_shared_vector_cast_tag()); + } + }; + template + struct static_shared_vector_caster, meta::is_not_void >::type> { + static inline shared_vector op(const shared_vector& src) { + return shared_vector(src, detail::_shared_vector_cast_tag()); + } + }; + // from non-void to void with same const-ness + template + struct static_shared_vector_caster, meta::is_not_void >::type> { + static FORCE_INLINE shared_vector op(const shared_vector& src) { + return shared_vector(src, detail::_shared_vector_cast_tag()); + } + }; + template + struct static_shared_vector_caster, meta::is_not_void >::type> { + static FORCE_INLINE shared_vector op(const shared_vector& src) { + return shared_vector(src, detail::_shared_vector_cast_tag()); + } + }; + + // cast to same type, no-op + template + struct static_shared_vector_caster { + static FORCE_INLINE const shared_vector& op(const shared_vector& src) { + return src; + } + }; +} // namespace detail + +/** @brief Allow casting of shared_vector between types + * + * Currently only to/from void is implemented. + * + @warning Casting from void is undefined unless the offset and length + * are integer multiples of the size of the destination type. + */ +template +static FORCE_INLINE +shared_vector +static_shared_vector_cast(const shared_vector& src) +{ + return detail::static_shared_vector_caster::op(src); +} + namespace detail { // Default to type conversion using castUnsafe (C++ type casting) on each element @@ -634,7 +710,7 @@ namespace detail { return shared_vector(src, detail::_shared_vector_cast_tag()); } }; - + // convert from void uses original type or throws an exception. template struct shared_vector_converter -static FORCE_INLINE -shared_vector -static_shared_vector_cast(const shared_vector& src, - typename meta::same_const::type = 0) -{ - return shared_vector(src, detail::_shared_vector_cast_tag()); -} - /** @brief Allow converting of shared_vector between types * * Conversion utilizes castUnsafe(). * * Converting to/from void is supported. Convert to void * is an alias for static_shared_vector_cast(). - * Convert from void utilizes shared_vector::original_type() - * and throws std::runtime_error if this is not valid. + * Convert from void utilizes shared_vector::original_type(). + * + * @throws std::runtime_error if cast is not valid. + * @throws std::bad_alloc for out of memory condition */ template static FORCE_INLINE @@ -861,8 +923,11 @@ std::ostream& operator<<(std::ostream& strm, const epics::pvData::shared_vector< * shared_vector has additional constructors from raw pointers * and shared_ptr s. * - * The copy constructor and assignment operator allow implicit - * casting from type 'shared_vector' to 'shared_vector'. + * Implicit casting is not allowed. Instead use + * const_shared_vector_cast()/freeze()/thaw() (@ref vectorconst) + * to casting between 'T' and 'const T'. + * Use static_shared_vector_cast() to cast between + * void and non-void (same const-ness). * * To facilitate safe modification the methods unique() and * make_unique() are provided. @@ -964,17 +1029,15 @@ Type #2 is constant reference to a mutable value. Type #3 is a mutable reference to a constant value. Type #4 is a constant reference to a constant value. -Casting between const and non-const references of the same value type -is governed by the normal C++ casting rules. - Casting between const and non-const values does @b not follow the normal -C++ casting rules. +C++ casting rules (no implicit cast). For casting between shared_vector and shared_vector explicit casting operations are required. These operations are @b freeze() (non-const to const) and @b thaw() (const to non-const). -A shared_vector is "frozen" as its value can not be modified. +A 'shared_vector' is "frozen" as its value can not be modified. +However it can still be sliced because the reference is not const. These functions are defined like: @@ -998,17 +1061,18 @@ The following guarantees are provided by both functions: # The returned reference points to a value which is only referenced by shared_vectors with the same value const-ness as the returned reference. -Please note that the argument of both freeze and thaw is a non-const +@note The argument of both freeze() and thaw() is a non-const reference which will always be cleared. @section vfreeze Freezing The act of freezing a shared_vector requires that the shared_vector -passed in must be unique() or an exception is thrown. This is -done to reduce the possibility of accidental copying. +passed in must be unique() or an exception is thrown. +No copy is made. -This possibility can be avoided by calling the make_unique() on a +The possibility of an exception can be avoided by calling the make_unique() on a shared_vector before passing it to freeze(). +This will make a copy if necessary. @section vthaw Thawing diff --git a/testApp/misc/testSharedVector.cpp b/testApp/misc/testSharedVector.cpp index 414e28a..f0a4b29 100644 --- a/testApp/misc/testSharedVector.cpp +++ b/testApp/misc/testSharedVector.cpp @@ -358,19 +358,46 @@ static void testVoid() { testDiag("Test vector cast to/from void"); - epics::pvData::shared_vector typed(4); + epics::pvData::shared_vector IV(4); - epics::pvData::shared_vector untyped2(epics::pvData::static_shared_vector_cast(typed)); + epics::pvData::shared_vector VV(epics::pvData::static_shared_vector_cast(IV)); - testOk1(typed.dataPtr().get()==untyped2.dataPtr().get()); - testOk1(typed.size()*sizeof(int)==untyped2.size()); + testOk1(IV.dataPtr().get()==VV.dataPtr().get()); + testOk1(IV.size()*sizeof(int)==VV.size()); - untyped2.slice(sizeof(int), 2*sizeof(int)); + VV.slice(sizeof(int), 2*sizeof(int)); - typed = epics::pvData::static_shared_vector_cast(untyped2); + IV = epics::pvData::static_shared_vector_cast(VV); - testOk1(typed.dataOffset()==1); - testOk1(typed.size()==2); + testOk1(IV.dataOffset()==1); + testOk1(IV.size()==2); + VV.clear(); +} + +static void testConstVoid() +{ + testDiag("Test vector cast to/from const void"); + + epics::pvData::shared_vector CIV(4); + + epics::pvData::shared_vector CVV(epics::pvData::static_shared_vector_cast(CIV)); + // case const void to const void + epics::pvData::shared_vector CVV2(epics::pvData::static_shared_vector_cast(CVV)); + + testOk1(CIV.dataPtr().get()==CVV2.dataPtr().get()); + testOk1(CIV.size()*sizeof(int)==CVV2.size()); + + CVV2.slice(sizeof(int), 2*sizeof(int)); + + CIV = epics::pvData::static_shared_vector_cast(CVV2); + + testOk1(CIV.dataOffset()==1); + testOk1(CIV.size()==2); + + epics::pvData::shared_vector VV; + // not possible to thaw() void as shared_vector has no make_unique() + //VV = thaw(CVV); + CVV = freeze(VV); } struct dummyStruct {}; @@ -537,9 +564,66 @@ static void testICE() } } +static +void testBad() +{ + epics::pvData::shared_vector I; + epics::pvData::shared_vector CI; + epics::pvData::shared_vector F; + epics::pvData::shared_vector CF; + epics::pvData::shared_vector V; + epics::pvData::shared_vector CV; + (void)I; + (void)CI; + (void)F; + (void)CF; + (void)V; + (void)CV; + + // Tests which should result in compile failure. + // as there is no established way to test this automatically, + // uncomment one at a time + + // No copy from const to non-const + //CI = I; + //I = CI; + //epics::pvData::shared_vector CI2(I); + //epics::pvData::shared_vector I2(CI); + + // shared_vector_convert can't thaw() + //I = epics::pvData::shared_vector_convert(CI); + //V = epics::pvData::shared_vector_convert(CV); + + // shared_vector_convert can't freeze() + //CI = epics::pvData::shared_vector_convert(I); + //CV = epics::pvData::shared_vector_convert(V); + + // static_shared_vector_cast can't thaw() + //I = epics::pvData::static_shared_vector_cast(CI); + //V = epics::pvData::static_shared_vector_cast(CV); + + // static_shared_vector_cast can't freeze() + //CI = epics::pvData::static_shared_vector_cast(I); + //CV = epics::pvData::static_shared_vector_cast(V); + + // freeze() can't change type. + // the error here will be with the assignment + //I = epics::pvData::freeze(CV); + //I = epics::pvData::freeze(CF); + //CI = epics::pvData::freeze(V); + //CI = epics::pvData::freeze(F); + + // that() can't change type. + // the error here will be with the assignment + //CI = epics::pvData::thaw(V); + //CI = epics::pvData::thaw(F); + //I = epics::pvData::thaw(CV); + //I = epics::pvData::that(CF); +} + MAIN(testSharedVector) { - testPlan(163); + testPlan(167); testDiag("Tests for shared_vector"); testDiag("sizeof(shared_vector)=%lu", @@ -554,9 +638,11 @@ MAIN(testSharedVector) testSlice(); testPush(); testVoid(); + testConstVoid(); testNonPOD(); testVectorConvert(); testWeak(); testICE(); + testBad(); return testDone(); } diff --git a/testApp/pv/testPVScalarArray.cpp b/testApp/pv/testPVScalarArray.cpp index 084266e..7085f67 100644 --- a/testApp/pv/testPVScalarArray.cpp +++ b/testApp/pv/testPVScalarArray.cpp @@ -177,11 +177,32 @@ static void testShare() testOk1(!cdata.unique()); } +static void testVoid() +{ + testDiag("Check PVScalarArray put/get from void"); + + PVIntArrayPtr iarr = static_pointer_cast(getPVDataCreate()->createPVScalarArray(pvInt)); + + PVIntArray::const_svector idata(4, 1); + iarr->PVScalarArray::putFrom(idata); + idata.clear(); + + shared_vector cvbuf; + iarr->PVScalarArray::getAs(cvbuf); + + idata = static_shared_vector_cast(cvbuf); + testOk1(idata.size()==4); + + iarr->PVScalarArray::putFrom(cvbuf); + + testOk1(iarr->getLength()==4); +} + } // end namespace MAIN(testPVScalarArray) { - testPlan(156); + testPlan(158); testFactory(); testBasic(); testBasic(); @@ -189,5 +210,6 @@ MAIN(testPVScalarArray) testBasic(); testBasic(); testShare(); + testVoid(); return testDone(); }