diff --git a/src/misc/Makefile b/src/misc/Makefile index 1f2986c..68cc93b 100644 --- a/src/misc/Makefile +++ b/src/misc/Makefile @@ -37,3 +37,4 @@ LIBSRCS += parseToPOD.cpp LIBSRCS += pvUnitTest.cpp LIBSRCS += debugPtr.cpp LIBSRCS += reftrack.cpp +LIBSRCS += anyscalar.cpp diff --git a/src/misc/anyscalar.cpp b/src/misc/anyscalar.cpp new file mode 100644 index 0000000..7072922 --- /dev/null +++ b/src/misc/anyscalar.cpp @@ -0,0 +1,161 @@ + +#include + +#define epicsExportSharedSymbols +#include + +#include "pv/anyscalar.h" + +namespace epics {namespace pvData { + +AnyScalar::AnyScalar(ScalarType type, const void *buf) +{ + if(type==pvString) { + new (_wrap.blob) std::string(*static_cast(buf)); + + } else { + memcpy(_wrap.blob, buf, ScalarTypeFunc::elementSize(type)); + } + + _stype = type; +} + +AnyScalar::AnyScalar(const AnyScalar& o) + :_stype(o._stype) +{ + if(o._stype==pvString) { + new (_wrap.blob) std::string(o._as()); + } else if(o._stype!=(ScalarType)-1) { + memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob)); + } +} + +#if __cplusplus>=201103L +AnyScalar::AnyScalar(AnyScalar&& o) + :_stype(o._stype) +{ + typedef std::string string; + if(o._stype==pvString) { + new (_wrap.blob) std::string(); + _as() = std::move(o._as()); + o._as().~string(); + } else if(o._stype!=(ScalarType)-1) { + memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob)); + } + o._stype = (ScalarType)-1; +} +#endif + +void AnyScalar::clear() { + if(_stype==pvString) { + typedef std::string string; + _as().~string(); + } + // other types need no cleanup + _stype = (ScalarType)-1; +} + +void AnyScalar::swap(AnyScalar& o) { + typedef std::string string; + switch((int)_stype) { + case -1: + switch((int)o._stype) { + case -1: + // nil <-> nil + break; + case pvString: + // nil <-> string + new (_wrap.blob) std::string(); + _as().swap(o._as()); + o._as().~string(); + break; + default: + // nil <-> non-string + memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob)); + break; + } + break; + case pvString: + switch((int)o._stype) { + case -1: + // string <-> nil + new (o._wrap.blob) std::string(); + _as().swap(o._as()); + _as().~string(); + break; + case pvString: + // string <-> string + _as().swap(o._as()); + break; + default: { + // string <-> non-string + std::string temp; + temp.swap(_as()); + + _as().~string(); + + memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob)); + + new (o._wrap.blob) std::string(); + temp.swap(o._as()); + } + break; + } + break; + default: + switch((int)o._stype) { + case -1: + // non-string <-> nil + memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob)); + break; + case pvString: { + // non-string <-> string + std::string temp; + temp.swap(o._as()); + + o._as().~string(); + + memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob)); + + new (_wrap.blob) std::string(); + temp.swap(_as()); + } + break; + default: + // non-string <-> non-string + _largest_blob temp; + memcpy(&temp, _wrap.blob, sizeof(_largest_blob)); + memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob)); + memcpy(o._wrap.blob, &temp, sizeof(_largest_blob)); + // std::swap(o._wrap.blob, _wrap.blob); // gcc <=4.3 doesn't like this + break; + } + break; + } + std::swap(_stype, o._stype); +} +const void* AnyScalar::bufferUnsafe() const { + if(_stype==pvString) { + return as().c_str(); + } else { + return _wrap.blob; + } +} + +std::ostream& operator<<(std::ostream& strm, const AnyScalar& v) +{ + switch(v.type()) { +#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case pv ## PVACODE: strm<(); break; +#define CASE_REAL_INT64 +#define CASE_STRING +#include "pv/typemap.h" +#undef CASE +#undef CASE_REAL_INT64 +#undef CASE_STRING + default: + strm<<"(nil)"; break; + } + return strm; +} + +}} // namespace epics::pvData diff --git a/src/misc/pv/anyscalar.h b/src/misc/pv/anyscalar.h index 28d875e..47352d9 100644 --- a/src/misc/pv/anyscalar.h +++ b/src/misc/pv/anyscalar.h @@ -48,7 +48,7 @@ constexpr size_t cmax(size_t A, size_t B) { assert(A.ref()==5.0); // throws AnyScalar::bad_cast @endcode */ -class AnyScalar { +class epicsShareClass AnyScalar { public: struct bad_cast : public std::exception { #if __cplusplus>=201103L @@ -95,8 +95,11 @@ private: return *reinterpret_cast(_wrap.blob); } public: + //! Construct empty + //! @post empty()==true AnyScalar() : _stype((ScalarType)-1) {} + //! Construct from provided value. template explicit AnyScalar(T v) { @@ -112,59 +115,31 @@ public: _stype = (ScalarType)ScalarTypeID::value; } - AnyScalar(ScalarType type, const void *buf) - { - if(type==pvString) { - new (_wrap.blob) std::string(*static_cast(buf)); + //! Construct from un-typed pointer. + //! Caller is responsible to ensure that buf actually points to the provided type + AnyScalar(ScalarType type, const void *buf); - } else { - memcpy(_wrap.blob, buf, ScalarTypeFunc::elementSize(type)); - } - - _stype = type; - } - - AnyScalar(const AnyScalar& o) - :_stype(o._stype) - { - if(o._stype==pvString) { - new (_wrap.blob) std::string(o._as()); - } else if(o._stype!=(ScalarType)-1) { - memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob)); - } - } + AnyScalar(const AnyScalar& o); #if __cplusplus>=201103L - AnyScalar(AnyScalar&& o) - :_stype(o._stype) - { - typedef std::string string; - if(o._stype==pvString) { - new (_wrap.blob) std::string(); - _as() = std::move(o._as()); - o._as().~string(); - } else if(o._stype!=(ScalarType)-1) { - memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob)); - } - o._stype = (ScalarType)-1; - } + AnyScalar(AnyScalar&& o); #endif - ~AnyScalar() {clear();} + inline ~AnyScalar() {clear();} - AnyScalar& operator=(const AnyScalar& o) { + inline AnyScalar& operator=(const AnyScalar& o) { AnyScalar(o).swap(*this); return *this; } template - AnyScalar& operator=(T v) { + inline AnyScalar& operator=(T v) { AnyScalar(v).swap(*this); return *this; } #if __cplusplus>=201103L - AnyScalar& operator=(AnyScalar&& o) { + inline AnyScalar& operator=(AnyScalar&& o) { clear(); swap(o); return *this; @@ -174,101 +149,17 @@ public: //! Reset internal state. //! Added after 7.0.0 //! @post empty()==true - void clear() { - if(_stype==pvString) { - typedef std::string string; - _as().~string(); - } - // other types need no cleanup - _stype = (ScalarType)-1; - } + void clear(); - void swap(AnyScalar& o) { - typedef std::string string; - switch((int)_stype) { - case -1: - switch((int)o._stype) { - case -1: - // nil <-> nil - break; - case pvString: - // nil <-> string - new (_wrap.blob) std::string(); - _as().swap(o._as()); - o._as().~string(); - break; - default: - // nil <-> non-string - memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob)); - break; - } - break; - case pvString: - switch((int)o._stype) { - case -1: - // string <-> nil - new (o._wrap.blob) std::string(); - _as().swap(o._as()); - _as().~string(); - break; - case pvString: - // string <-> string - _as().swap(o._as()); - break; - default: { - // string <-> non-string - std::string temp; - temp.swap(_as()); - - _as().~string(); - - memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob)); - - new (o._wrap.blob) std::string(); - temp.swap(o._as()); - } - break; - } - break; - default: - switch((int)o._stype) { - case -1: - // non-string <-> nil - memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob)); - break; - case pvString: { - // non-string <-> string - std::string temp; - temp.swap(o._as()); - - o._as().~string(); - - memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob)); - - new (_wrap.blob) std::string(); - temp.swap(_as()); - } - break; - default: - // non-string <-> non-string - _largest_blob temp; - memcpy(&temp, _wrap.blob, sizeof(_largest_blob)); - memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob)); - memcpy(o._wrap.blob, &temp, sizeof(_largest_blob)); - // std::swap(o._wrap.blob, _wrap.blob); // gcc <=4.3 doesn't like this - break; - } - break; - } - std::swap(_stype, o._stype); - } + void swap(AnyScalar& o); + //! Type code of contained value. Or (ScalarType)-1 is empty. inline ScalarType type() const { return _stype; } - void* unsafe() { return _wrap.blob; } - const void* unsafe() const { return _wrap.blob; } + inline void* unsafe() { return _wrap.blob; } + inline const void* unsafe() const { return _wrap.blob; } inline bool empty() const { return _stype==(ScalarType)-1; } @@ -283,16 +174,18 @@ public: //! Provide read-only access to underlying buffer. //! For a string this is std::string::c_str(). - const void* bufferUnsafe() const { - if(_stype==pvString) { - return as().c_str(); - } else { - return _wrap.blob; - } - } + const void* bufferUnsafe() const; - /** Return reference to wrapped value */ + /** Return typed reference to wrapped value. Non-const reference allows value modification + * + @code + AnyScalar v(42); + v.ref() = 42; + assert(v.ref() = 43); + @endcode + */ template + inline // T -> strip_const -> map to storage type -> add reference typename detail::any_storage_type::type>::type& ref() { @@ -304,7 +197,15 @@ public: return reinterpret_cast(_wrap.blob); } + /** Return typed reference to wrapped value. Const reference does not allow modification. + * + @code + AnyScalar v(42); + assert(v.ref() = 42); + @endcode + */ template + inline // T -> strip_const -> map to storage type -> add const reference typename meta::decorate_const::type>::type>::type& ref() const { @@ -318,6 +219,7 @@ public: /** copy out wrapped value, with a value conversion. */ template + inline T as() const { typedef typename meta::strip_const::type T2; typedef typename detail::any_storage_type::type TT; @@ -334,22 +236,8 @@ private: friend std::ostream& operator<<(std::ostream& strm, const AnyScalar& v); }; -inline -std::ostream& operator<<(std::ostream& strm, const AnyScalar& v) -{ - switch(v.type()) { -#define CASE(BASETYPE, PVATYPE, DBFTYPE, PVACODE) case pv ## PVACODE: strm<(); break; -#define CASE_REAL_INT64 -#define CASE_STRING -#include "pv/typemap.h" -#undef CASE -#undef CASE_REAL_INT64 -#undef CASE_STRING - default: - strm<<"(nil)"; break; - } - return strm; -} +epicsShareExtern +std::ostream& operator<<(std::ostream& strm, const AnyScalar& v); }} // namespace epics::pvData