Add AnyScalar
This commit is contained in:
163
common/anyscalar.h
Normal file
163
common/anyscalar.h
Normal file
@ -0,0 +1,163 @@
|
||||
#ifndef ANYSCALAR_H
|
||||
#define ANYSCALAR_H
|
||||
|
||||
#include <ostream>
|
||||
#include <exception>
|
||||
#include <map>
|
||||
|
||||
#include <pv/templateMeta.h>
|
||||
#include <pv/typeCast.h>
|
||||
#include <pv/pvIntrospect.h> /* for ScalarType enum */
|
||||
|
||||
namespace detail {
|
||||
template <typename T>
|
||||
struct any_storage_type { typedef T type; };
|
||||
template<> struct any_storage_type<char*> { typedef std::string type; };
|
||||
template<> struct any_storage_type<const char*> { typedef std::string type; };
|
||||
}// namespace detail
|
||||
|
||||
/** A type-safe variant union capable of holding
|
||||
* any of the PVD scalar types
|
||||
*/
|
||||
class AnyScalar {
|
||||
public:
|
||||
struct bad_cast : public std::exception {
|
||||
#if __cplusplus>=201103L
|
||||
bad_cast() noexcept {}
|
||||
virtual ~bad_cast() noexcept {}
|
||||
virtual const char* what() noexcept
|
||||
#else
|
||||
bad_cast() throw() {}
|
||||
virtual ~bad_cast() throw() {}
|
||||
virtual const char* what() throw()
|
||||
#endif
|
||||
{ return "bad_cast() type mis-match"; }
|
||||
};
|
||||
|
||||
private:
|
||||
struct HolderBase {
|
||||
virtual ~HolderBase() {}
|
||||
virtual HolderBase* clone() =0;
|
||||
virtual epics::pvData::ScalarType type() =0;
|
||||
virtual void* ptr() =0;
|
||||
virtual void show(std::ostream&) =0;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
struct Holder : public HolderBase {
|
||||
T held;
|
||||
|
||||
Holder(typename epics::pvData::meta::arg_type<T>::type v,
|
||||
epics::pvData::ScalarType t = (epics::pvData::ScalarType)epics::pvData::ScalarTypeID<T>::value)
|
||||
:held(v)
|
||||
{}
|
||||
virtual ~Holder() {}
|
||||
|
||||
virtual HolderBase* clone() {
|
||||
return new Holder(held);
|
||||
}
|
||||
|
||||
virtual epics::pvData::ScalarType type() {
|
||||
return (epics::pvData::ScalarType)epics::pvData::ScalarTypeID<T>::value;
|
||||
}
|
||||
|
||||
virtual void* ptr() {
|
||||
return static_cast<void*>(&held);
|
||||
}
|
||||
|
||||
virtual void show(std::ostream& strm) {
|
||||
strm<<held;
|
||||
}
|
||||
};
|
||||
|
||||
HolderBase *holder;
|
||||
public:
|
||||
AnyScalar() :holder(0) {}
|
||||
|
||||
template<typename T>
|
||||
explicit AnyScalar(T v)
|
||||
:holder(new Holder<typename epics::pvData::meta::strip_const<typename detail::any_storage_type<T>::type>::type>(v))
|
||||
{}
|
||||
|
||||
AnyScalar(const AnyScalar& o)
|
||||
:holder(o.holder ? o.holder->clone() : 0)
|
||||
{}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
AnyScalar(AnyScalar&& o)
|
||||
:holder(o.holder)
|
||||
{ o.holder = 0; }
|
||||
#endif
|
||||
|
||||
~AnyScalar() { delete holder; }
|
||||
|
||||
AnyScalar& operator=(const AnyScalar& o) {
|
||||
AnyScalar(o).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
inline void swap(AnyScalar& o) {
|
||||
std::swap(holder, o.holder);
|
||||
}
|
||||
|
||||
inline epics::pvData::ScalarType type() const {
|
||||
return holder ? holder->type() : ((epics::pvData::ScalarType)-1);
|
||||
}
|
||||
|
||||
inline bool empty() const { return !holder; }
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
explicit operator bool() const { return holder; }
|
||||
#else
|
||||
private:
|
||||
typedef void (AnyScalar::bool_type)(AnyScalar&);
|
||||
public:
|
||||
operator bool_type() const { holder ? &AnyScalar::swap : 0 }
|
||||
#endif
|
||||
|
||||
/** Return reference to wrapped value */
|
||||
template<typename T>
|
||||
T&
|
||||
ref() {
|
||||
typedef typename epics::pvData::meta::strip_const<T>::type T2;
|
||||
if(!holder || type()!=(epics::pvData::ScalarType)epics::pvData::ScalarTypeID<T2>::value)
|
||||
throw bad_cast();
|
||||
return *static_cast<T*>(holder->ptr());
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
typename epics::pvData::meta::decorate_const<T&>::type
|
||||
ref() const {
|
||||
typedef typename epics::pvData::meta::strip_const<T>::type T2;
|
||||
if(!holder || type()!=(epics::pvData::ScalarType)epics::pvData::ScalarTypeID<T2>::value)
|
||||
throw bad_cast();
|
||||
return *static_cast<T*>(holder->ptr());
|
||||
}
|
||||
|
||||
/** copy out wrapped value, with a value conversion. */
|
||||
template<typename T>
|
||||
T as() const {
|
||||
typedef typename epics::pvData::meta::strip_const<T>::type T2;
|
||||
if(!holder)
|
||||
throw bad_cast();
|
||||
T2 ret;
|
||||
epics::pvData::castUnsafeV(1, (epics::pvData::ScalarType)epics::pvData::ScalarTypeID<T2>::value, &ret,
|
||||
holder->type(), holder->ptr());
|
||||
return ret;
|
||||
}
|
||||
|
||||
private:
|
||||
friend std::ostream& operator<<(std::ostream& strm, const AnyScalar& v);
|
||||
};
|
||||
|
||||
inline
|
||||
std::ostream& operator<<(std::ostream& strm, const AnyScalar& v)
|
||||
{
|
||||
if(v.holder)
|
||||
v.holder->show(strm);
|
||||
else
|
||||
strm<<"(nil)";
|
||||
return strm;
|
||||
}
|
||||
|
||||
#endif // ANYSCALAR_H
|
@ -29,6 +29,11 @@ teststream_SRCS = teststream.cpp
|
||||
teststream_LIBS = Com
|
||||
TESTS += teststream
|
||||
|
||||
TESTPROD_HOST += testanyscalar
|
||||
testanyscalar_SRCS = testanyscalar.cpp
|
||||
testanyscalar_LIBS = pvData Com
|
||||
TESTS += testanyscalar
|
||||
|
||||
TESTPROD_HOST += testtest
|
||||
testtest_SRCS = testtest.cpp
|
||||
testtest_LIBS = testutils p2pcore pvAccess pvData Com
|
||||
|
96
testApp/testanyscalar.cpp
Normal file
96
testApp/testanyscalar.cpp
Normal file
@ -0,0 +1,96 @@
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <pv/pvIntrospect.h>
|
||||
|
||||
#include <pv/pvUnitTest.h>
|
||||
#include <testMain.h>
|
||||
|
||||
#include "anyscalar.h"
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
|
||||
namespace {
|
||||
|
||||
void test_empty()
|
||||
{
|
||||
testDiag("test_empty()");
|
||||
AnyScalar O;
|
||||
testOk1(O.empty());
|
||||
testOk1(!O);
|
||||
|
||||
testThrows(AnyScalar::bad_cast, O.ref<double>());
|
||||
testThrows(AnyScalar::bad_cast, O.as<double>());
|
||||
}
|
||||
|
||||
void test_basic()
|
||||
{
|
||||
testDiag("test_basic()");
|
||||
AnyScalar I(42);
|
||||
|
||||
testOk1(!I.empty());
|
||||
testOk1(!!I);
|
||||
|
||||
testEqual(I.type(), pvd::pvInt);
|
||||
testEqual(I.ref<pvd::int32>(), 42);
|
||||
testEqual(I.as<pvd::int32>(), 42);
|
||||
testEqual(I.as<double>(), 42.0);
|
||||
testEqual(I.as<std::string>(), "42");
|
||||
|
||||
testThrows(AnyScalar::bad_cast, I.ref<double>());
|
||||
|
||||
{
|
||||
std::ostringstream strm;
|
||||
strm<<I;
|
||||
testEqual(strm.str(), "42");
|
||||
}
|
||||
|
||||
I.ref<pvd::int32>() = 43;
|
||||
|
||||
testEqual(I.ref<pvd::int32>(), 43);
|
||||
testEqual(I.as<pvd::int32>(), 43);
|
||||
testEqual(I.as<double>(), 43.0);
|
||||
|
||||
I = AnyScalar("hello");
|
||||
|
||||
testEqual(I.type(), pvd::pvString);
|
||||
testEqual(I.ref<std::string>(), "hello");
|
||||
testEqual(I.as<std::string>(), "hello");
|
||||
|
||||
testThrows(AnyScalar::bad_cast, I.ref<pvd::int32>());
|
||||
|
||||
{
|
||||
AnyScalar O(I);
|
||||
testOk1(!I.empty());
|
||||
testOk1(!O.empty());
|
||||
|
||||
testEqual(I.ref<std::string>(), "hello");
|
||||
testEqual(O.ref<std::string>(), "hello");
|
||||
}
|
||||
|
||||
{
|
||||
AnyScalar O;
|
||||
I.swap(O);
|
||||
testOk1(I.empty());
|
||||
testOk1(!O.empty());
|
||||
|
||||
testThrows(AnyScalar::bad_cast, I.ref<std::string>());
|
||||
testEqual(O.ref<std::string>(), "hello");
|
||||
|
||||
I.swap(O);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
MAIN(testanyscalar)
|
||||
{
|
||||
testPlan(28);
|
||||
try {
|
||||
test_empty();
|
||||
test_basic();
|
||||
}catch(std::exception& e){
|
||||
testAbort("Unexpected exception: %s", e.what());
|
||||
}
|
||||
return testDone();
|
||||
}
|
Reference in New Issue
Block a user