From 7dd33a0c7187f43693aeee7c0d09fe3277002658 Mon Sep 17 00:00:00 2001 From: Michael Davidsaver Date: Sat, 14 Dec 2019 09:50:33 -0800 Subject: [PATCH] shared_array redo freeze/cast as members --- src/pvxs/sharedArray.h | 179 ++++++++++++++++++++++++++--------------- test/testshared.cpp | 21 ++++- 2 files changed, 133 insertions(+), 67 deletions(-) diff --git a/src/pvxs/sharedArray.h b/src/pvxs/sharedArray.h index 82a8fa3..2251191 100644 --- a/src/pvxs/sharedArray.h +++ b/src/pvxs/sharedArray.h @@ -21,7 +21,46 @@ class Value; template class shared_array; +enum class ArrayType : uint8_t { + Null = 0xff, + Bool = 0x08, + Int8 = 0x28, + Int16 = 0x29, + Int32 = 0x2a, + Int64 = 0x2b, + UInt8 = 0x2c, + UInt16= 0x2d, + UInt32= 0x2e, + UInt64= 0x2f, + Float = 0x4a, + Double= 0x4b, + String= 0x68, + Value = 0x88, // also used for 0x89 and 0x8a +}; + +PVXS_API +std::ostream& operator<<(std::ostream& strm, ArrayType code); + namespace detail { +template +struct CaptureCode; + +#define CASE(TYPE, CODE) \ +template<> struct CaptureCode { static constexpr ArrayType code{ArrayType::CODE}; } +CASE(bool, Bool); +CASE(int8_t, Int8); +CASE(int16_t, Int16); +CASE(int32_t, Int32); +CASE(int64_t, Int64); +CASE(uint8_t, UInt8); +CASE(uint16_t, UInt16); +CASE(uint32_t, UInt32); +CASE(uint64_t, UInt64); +CASE(float, Float); +CASE(double, Double); +CASE(std::string, String); +CASE(Value, Value); +#undef CASE template struct sizeofx { @@ -249,51 +288,42 @@ public: throw std::out_of_range("Index out of bounds"); return (*this)[i]; } + + //! Cast to const, consuming this + //! @pre unique()==true + //! @post empty()==true + shared_array::type> + freeze() { + if(!this->unique()) + throw std::logic_error("Can't freeze non-unique shared_array"); + + // alias w/ implied cast to const. + shared_array::type> ret(this->_data, this->_data.get(), this->_size); + + // c++20 provides a move()-able alternative to the aliasing constructor. + // until this stops being the future, we consume the src ref. and + // inc. + dec. the ref counter... + this->clear(); + return ret; + } + + // static_cast() to non-void, preserving const-ness + template{} && (std::is_const{} == std::is_const{}), int>::type =0> + shared_array + castTo() const { + auto alen = this->_size*sizeof(E)/sizeof(TO); + return shared_array(this->_data, static_cast(this->_data.get()), alen); + } + + // static_cast() to void, preserving const-ness + template{} && (std::is_const{} == std::is_const{}), int>::type =0> + shared_array + castTo() const { + auto alen = this->_size*sizeof(E); + return shared_array(this->_data, this->_data.get(), alen); // implied cast to void* + } }; -enum class ArrayType : uint8_t { - Null = 0xff, - Bool = 0x08, - Int8 = 0x28, - Int16 = 0x29, - Int32 = 0x2a, - Int64 = 0x2b, - UInt8 = 0x2c, - UInt16= 0x2d, - UInt32= 0x2e, - UInt64= 0x2f, - Float = 0x4a, - Double= 0x4b, - String= 0x68, - Value = 0x88, // also used for 0x89 and 0x8a -}; - -PVXS_API -std::ostream& operator<<(std::ostream& strm, ArrayType code); - -namespace detail { -template -struct CaptureCode; - -#define CASE(TYPE, CODE) \ -template<> struct CaptureCode { static constexpr ArrayType code{ArrayType::CODE}; } -CASE(bool, Bool); -CASE(int8_t, Int8); -CASE(int16_t, Int16); -CASE(int32_t, Int32); -CASE(int64_t, Int64); -CASE(uint8_t, UInt8); -CASE(uint16_t, UInt16); -CASE(uint32_t, UInt32); -CASE(uint64_t, UInt64); -CASE(float, Float); -CASE(double, Double); -CASE(std::string, String); -CASE(Value, Value); -#undef CASE - -} // namespace detail - template class shared_array{}>::type > @@ -363,6 +393,14 @@ public: ,_type(detail::CaptureCode::type>::code) {} +private: + template + shared_array(const std::shared_ptr& a, E* b, size_t len, ArrayType code) + :base_t(a, b, len) + ,_type(code) + {} +public: + //! clear data and become untyped void clear() noexcept { base_t::clear(); @@ -378,6 +416,38 @@ public: size_t max_size() const noexcept{return (size_t)-1;} inline ArrayType original_type() const { return _type; } + + shared_array::type> + freeze() { + if(!this->unique()) + throw std::logic_error("Can't freeze non-unique shared_array"); + + // alias w/ implied cast to const. + shared_array::type> ret(this->_data, this->_data.get(), this->_size, this->_type); + + // c++20 provides a move()-able alternative to the aliasing constructor. + // until this stops being the future, we consume the src ref. and + // inc. + dec. the ref counter... + this->clear(); + return ret; + } + + // static_cast() to non-void, preserving const-ness + template{} && (std::is_const{} == std::is_const{}), int>::type =0> + shared_array + castTo() const { + auto alen = this->_size/sizeof(TO); + return shared_array(this->_data, static_cast(this->_data.get()), alen); + } + + // static_cast() to void, preserving const-ness + template{} && (std::is_const{} == std::is_const{}), int>::type =0> + shared_array + castTo() const { + // in reality this is either void -> void, or const void -> const void + // aka. simple copy + return *this; + } }; // non-const -> const @@ -386,33 +456,16 @@ static inline shared_array::type> freeze(SRC&& src) { - typedef typename SRC::value_type FROM; - typedef typename std::add_const::type TO; - - if(!src.unique()) - throw std::logic_error("Can't freeze non-unique shared_array"); - - // cast data pointer to const - TO* data = src.data(); - - shared_array ret(src.dataPtr(), data, src.size()); - - // c++20 provides a move()-able alternative to the aliasing constructor. - // until this stops being the future, we consume the src ref. and - // inc. + dec. the ref counter... - auto temp(std::move(src)); - return ret; + return src.freeze(); } // change type, while keeping same const -template{} == std::is_const{}, int >::type=0> +template static inline shared_array shared_array_static_cast(const shared_array& src) { - size_t newsize = src.size()*detail::sizeofx::op()/detail::sizeofx::op(); - return shared_array(src.dataPtr(), static_cast(src.data()), newsize); + return src.template castTo(); } template{}, int>::type =0> diff --git a/test/testshared.cpp b/test/testshared.cpp index b5ab1cc..d9ac648 100644 --- a/test/testshared.cpp +++ b/test/testshared.cpp @@ -81,12 +81,25 @@ void testVoid() shared_array X(2); - shared_array Y(shared_array_static_cast(X)); + shared_array Y(X.template castTo()); testOk1(!X.unique()); testOk1(!Y.unique()); testEq(X.size(), 2u); testEq(Y.size(), 8u); testEq(Y.original_type(), ArrayType::UInt32); // never const uint32_t + + testThrows([&Y]() { + auto Z = Y.freeze(); + }); + + X.clear(); + testOk1(Y.unique()); + + auto Z = Y.freeze(); + testOk1(Y.unique()); + testOk1(Z.unique()); + testEq(Y.size(), 0u); + testEq(Z.size(), 8u); } void testFreeze() @@ -94,7 +107,7 @@ void testFreeze() testDiag("%s", __func__); shared_array X(2, 5); - shared_array Y(freeze(std::move(X))); + shared_array Y(X.freeze()); testOk1(X.unique()); testOk1(Y.unique()); testEq(X.size(), 0u); @@ -108,7 +121,7 @@ void testFreezeError() shared_array X(2, 5), Z(X); testOk1(!X.unique()); testThrows([&X]() { - shared_array Y(freeze(std::move(X))); + shared_array Y(X.freeze()); })<<"Attempt to freeze() non-unique"; } @@ -126,7 +139,7 @@ void testComplex() MAIN(testshared) { - testPlan(81); + testPlan(93); testEmpty(); testEmpty(); testEmpty();