use AnyScalar from pvDataCPP
This commit is contained in:
@ -1,308 +0,0 @@
|
||||
#ifndef ANYSCALAR_H
|
||||
#define ANYSCALAR_H
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
# include <type_traits>
|
||||
#endif
|
||||
|
||||
#include <ostream>
|
||||
#include <exception>
|
||||
#include <map>
|
||||
|
||||
#include <epicsAssert.h>
|
||||
|
||||
#include <pv/templateMeta.h>
|
||||
#include <pv/typeCast.h>
|
||||
#include <pv/pvIntrospect.h> /* for ScalarType enum */
|
||||
|
||||
namespace detail {
|
||||
|
||||
// special mangling for AnyScalar ctor to map from argument type to storage type.
|
||||
// allow construction from constants.
|
||||
template <typename T>
|
||||
struct any_storage_type { typedef T type; };
|
||||
template<> struct any_storage_type<int> { typedef epics::pvData::int32 type; };
|
||||
template<> struct any_storage_type<unsigned> { typedef epics::pvData::uint32 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() const noexcept
|
||||
#else
|
||||
bad_cast() throw() {}
|
||||
virtual ~bad_cast() throw() {}
|
||||
virtual const char* what() const throw()
|
||||
#endif
|
||||
{ return "bad_cast() type mis-match"; }
|
||||
};
|
||||
|
||||
private:
|
||||
typedef epics::pvData::ScalarType ScalarType;
|
||||
ScalarType _stype;
|
||||
|
||||
// always reserve enough storage for std::string (assumed worst case)
|
||||
#if __cplusplus>=201103L
|
||||
struct wrap_t {
|
||||
typename std::aligned_storage<sizeof(std::string), alignof(std::string)>::type blob[1];
|
||||
} _wrap;
|
||||
#else
|
||||
struct wrap_t {
|
||||
union blob_t {
|
||||
char data[sizeof(std::string)];
|
||||
double align_f; // assume std::string alignment <= 8
|
||||
} blob[1];
|
||||
} _wrap;
|
||||
#endif
|
||||
|
||||
// assumed largest non-string type
|
||||
typedef double _largest_blob;
|
||||
|
||||
template<typename T>
|
||||
inline T& _as() {
|
||||
return *reinterpret_cast<T*>(_wrap.blob);
|
||||
}
|
||||
template<typename T>
|
||||
inline const T& _as() const {
|
||||
return *reinterpret_cast<const T*>(_wrap.blob);
|
||||
}
|
||||
public:
|
||||
AnyScalar() : _stype((ScalarType)-1) {}
|
||||
|
||||
template<typename T>
|
||||
explicit AnyScalar(T v)
|
||||
{
|
||||
typedef typename epics::pvData::meta::strip_const<T>::type T2;
|
||||
typedef typename detail::any_storage_type<T2>::type TT;
|
||||
|
||||
STATIC_ASSERT(sizeof(TT)<=sizeof(_wrap.blob));
|
||||
|
||||
new (_wrap.blob) TT(v);
|
||||
|
||||
// this line fails to compile when type T can't be mapped to one of
|
||||
// the PVD scalar types.
|
||||
_stype = (ScalarType)epics::pvData::ScalarTypeID<TT>::value;
|
||||
}
|
||||
|
||||
AnyScalar(const AnyScalar& o)
|
||||
:_stype(o._stype)
|
||||
{
|
||||
if(o._stype==epics::pvData::pvString) {
|
||||
new (_wrap.blob) std::string(o._as<std::string>());
|
||||
} else if(o._stype!=(ScalarType)-1) {
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
}
|
||||
}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
AnyScalar(AnyScalar&& o)
|
||||
:_stype(o._stype)
|
||||
{
|
||||
typedef std::string string;
|
||||
if(o._stype==epics::pvData::pvString) {
|
||||
new (_wrap.blob) std::string();
|
||||
_as<std::string>() = std::move(o._as<std::string>());
|
||||
o._as<string>().~string();
|
||||
} else if(o._stype!=(ScalarType)-1) {
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
}
|
||||
o._stype = (ScalarType)-1;
|
||||
}
|
||||
#endif
|
||||
|
||||
~AnyScalar() {
|
||||
if(_stype==epics::pvData::pvString) {
|
||||
typedef std::string string;
|
||||
_as<string>().~string();
|
||||
}
|
||||
// other types need no cleanup
|
||||
}
|
||||
|
||||
AnyScalar& operator=(const AnyScalar& o) {
|
||||
AnyScalar(o).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
AnyScalar& operator=(T v) {
|
||||
AnyScalar(v).swap(*this);
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
AnyScalar& operator=(AnyScalar&& o) {
|
||||
if(_stype==epics::pvData::pvString) {
|
||||
typedef std::string string;
|
||||
_as<string>().~string();
|
||||
}
|
||||
_stype = (ScalarType)-1;
|
||||
swap(o);
|
||||
return *this;
|
||||
}
|
||||
#endif
|
||||
|
||||
void swap(AnyScalar& o) {
|
||||
typedef std::string string;
|
||||
switch((int)_stype) {
|
||||
case -1:
|
||||
switch((int)o._stype) {
|
||||
case -1:
|
||||
// nil <-> nil
|
||||
break;
|
||||
case epics::pvData::pvString:
|
||||
// nil <-> string
|
||||
new (_wrap.blob) std::string();
|
||||
_as<std::string>().swap(o._as<std::string>());
|
||||
o._as<std::string>().~string();
|
||||
break;
|
||||
default:
|
||||
// nil <-> non-string
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case epics::pvData::pvString:
|
||||
switch((int)o._stype) {
|
||||
case -1:
|
||||
// string <-> nil
|
||||
new (o._wrap.blob) std::string();
|
||||
_as<std::string>().swap(o._as<std::string>());
|
||||
_as<std::string>().~string();
|
||||
break;
|
||||
case epics::pvData::pvString:
|
||||
// string <-> string
|
||||
_as<std::string>().swap(o._as<std::string>());
|
||||
break;
|
||||
default: {
|
||||
// string <-> non-string
|
||||
std::string temp;
|
||||
temp.swap(_as<std::string>());
|
||||
|
||||
_as<std::string>().~string();
|
||||
|
||||
memcpy(_wrap.blob, o._wrap.blob, sizeof(_largest_blob));
|
||||
|
||||
new (o._wrap.blob) std::string();
|
||||
temp.swap(o._as<std::string>());
|
||||
}
|
||||
break;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
switch((int)o._stype) {
|
||||
case -1:
|
||||
// non-string <-> nil
|
||||
memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob));
|
||||
break;
|
||||
case epics::pvData::pvString: {
|
||||
// non-string <-> string
|
||||
std::string temp;
|
||||
temp.swap(o._as<std::string>());
|
||||
|
||||
o._as<std::string>().~string();
|
||||
|
||||
memcpy(o._wrap.blob, _wrap.blob, sizeof(_largest_blob));
|
||||
|
||||
new (_wrap.blob) std::string();
|
||||
temp.swap(_as<std::string>());
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
inline epics::pvData::ScalarType type() const {
|
||||
return _stype;
|
||||
}
|
||||
|
||||
inline bool empty() const { return _stype==(ScalarType)-1; }
|
||||
|
||||
#if __cplusplus>=201103L
|
||||
explicit operator bool() const { return !empty(); }
|
||||
#else
|
||||
private:
|
||||
typedef void (AnyScalar::*bool_type)(AnyScalar&);
|
||||
public:
|
||||
operator bool_type() const { return !empty() ? &AnyScalar::swap : 0; }
|
||||
#endif
|
||||
|
||||
/** Return reference to wrapped value */
|
||||
template<typename T>
|
||||
// T -> strip_const -> map to storage type -> add reference
|
||||
typename detail::any_storage_type<typename epics::pvData::meta::strip_const<T>::type>::type&
|
||||
ref() {
|
||||
typedef typename epics::pvData::meta::strip_const<T>::type T2;
|
||||
typedef typename detail::any_storage_type<T2>::type TT;
|
||||
|
||||
if(_stype!=(ScalarType)epics::pvData::ScalarTypeID<TT>::value)
|
||||
throw bad_cast();
|
||||
return _as<TT>();
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
// T -> strip_const -> map to storage type -> add const reference
|
||||
typename epics::pvData::meta::decorate_const<typename detail::any_storage_type<typename epics::pvData::meta::strip_const<T>::type>::type>::type&
|
||||
ref() const {
|
||||
typedef typename epics::pvData::meta::strip_const<T>::type T2;
|
||||
typedef typename detail::any_storage_type<T2>::type TT;
|
||||
|
||||
if(_stype!=(ScalarType)epics::pvData::ScalarTypeID<TT>::value)
|
||||
throw bad_cast();
|
||||
return _as<TT>();
|
||||
}
|
||||
|
||||
/** copy out wrapped value, with a value conversion. */
|
||||
template<typename T>
|
||||
T as() const {
|
||||
typedef typename epics::pvData::meta::strip_const<T>::type T2;
|
||||
typedef typename detail::any_storage_type<T2>::type TT;
|
||||
|
||||
if(_stype==(ScalarType)-1)
|
||||
throw bad_cast();
|
||||
TT ret;
|
||||
epics::pvData::castUnsafeV(1, (ScalarType)epics::pvData::ScalarTypeID<T2>::value, &ret,
|
||||
_stype, _wrap.blob);
|
||||
return ret;
|
||||
}
|
||||
|
||||
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 epics::pvData::pv ## PVACODE: strm<<v._as<PVATYPE>(); break;
|
||||
#define CASE_REAL_INT64
|
||||
#define CASE_STRING
|
||||
#include "pvatypemap.h"
|
||||
#undef CASE
|
||||
#undef CASE_REAL_INT64
|
||||
#undef CASE_STRING
|
||||
default:
|
||||
strm<<"(nil)"; break;
|
||||
}
|
||||
return strm;
|
||||
}
|
||||
|
||||
#endif // ANYSCALAR_H
|
@ -15,7 +15,7 @@ namespace {
|
||||
|
||||
namespace pvd = epics::pvData;
|
||||
|
||||
typedef std::map<std::string, AnyScalar> options_t;
|
||||
typedef std::map<std::string, pvd::AnyScalar> options_t;
|
||||
typedef std::map<std::string, options_t> config_t;
|
||||
|
||||
struct context {
|
||||
@ -37,7 +37,7 @@ struct context {
|
||||
throw std::runtime_error("Can't assign value in this context");
|
||||
}
|
||||
|
||||
void assign(const AnyScalar& value) {
|
||||
void assign(const pvd::AnyScalar& value) {
|
||||
can_assign();
|
||||
GroupConfig::Group& grp = conf.groups[group];
|
||||
|
||||
@ -89,7 +89,7 @@ struct context {
|
||||
int conf_null(void * ctx)
|
||||
{
|
||||
TRY {
|
||||
self->assign(AnyScalar());
|
||||
self->assign(pvd::AnyScalar());
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
@ -98,7 +98,7 @@ int conf_null(void * ctx)
|
||||
int conf_boolean(void * ctx, int boolVal)
|
||||
{
|
||||
TRY {
|
||||
self->assign(AnyScalar(pvd::boolean(boolVal)));
|
||||
self->assign(pvd::AnyScalar(pvd::boolean(boolVal)));
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
@ -106,7 +106,7 @@ int conf_boolean(void * ctx, int boolVal)
|
||||
int conf_integer(void * ctx, long integerVal)
|
||||
{
|
||||
TRY {
|
||||
self->assign(AnyScalar(pvd::int64(integerVal)));
|
||||
self->assign(pvd::AnyScalar(pvd::int64(integerVal)));
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
@ -114,7 +114,7 @@ int conf_integer(void * ctx, long integerVal)
|
||||
int conf_double(void * ctx, double doubleVal)
|
||||
{
|
||||
TRY {
|
||||
self->assign(AnyScalar(doubleVal));
|
||||
self->assign(pvd::AnyScalar(doubleVal));
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
@ -124,7 +124,7 @@ int conf_string(void * ctx, const unsigned char * stringVal,
|
||||
{
|
||||
TRY {
|
||||
std::string val((const char*)stringVal, stringLen);
|
||||
self->assign(AnyScalar(val));
|
||||
self->assign(pvd::AnyScalar(val));
|
||||
return 1;
|
||||
}CATCH()
|
||||
}
|
||||
|
@ -13,8 +13,7 @@
|
||||
#include <pv/status.h>
|
||||
#include <pv/bitSet.h>
|
||||
#include <pv/pvData.h>
|
||||
|
||||
#include <anyscalar.h>
|
||||
#include <pv/anyscalar.h>
|
||||
|
||||
#include <shareLib.h>
|
||||
|
||||
|
@ -29,11 +29,6 @@ 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
|
||||
|
@ -1,227 +0,0 @@
|
||||
|
||||
#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_ctor()
|
||||
{
|
||||
testDiag("test_ctor()");
|
||||
AnyScalar A(10),
|
||||
B(10.0),
|
||||
C("foo"),
|
||||
D(std::string("bar"));
|
||||
|
||||
testEqual(A.type(), pvd::pvInt);
|
||||
testEqual(B.type(), pvd::pvDouble);
|
||||
testEqual(C.type(), pvd::pvString);
|
||||
testEqual(D.type(), pvd::pvString);
|
||||
|
||||
testEqual(A.ref<pvd::int32>(), 10);
|
||||
testEqual(B.ref<double>(), 10);
|
||||
testEqual(C.ref<std::string>(), "foo");
|
||||
testEqual(D.ref<std::string>(), "bar");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
void test_swap()
|
||||
{
|
||||
testDiag("test_swap()");
|
||||
|
||||
// AnyScalar::swap() has 3 cases each for LHS and RHS
|
||||
// nil, string, and non-string
|
||||
// So we have 9 cases to test
|
||||
|
||||
{
|
||||
AnyScalar A, B;
|
||||
A.swap(B);
|
||||
testOk1(A.empty());
|
||||
testOk1(B.empty());
|
||||
}
|
||||
{
|
||||
AnyScalar A, B("hello");
|
||||
A.swap(B);
|
||||
testEqual(A.ref<std::string>(), "hello");
|
||||
testOk1(B.empty());
|
||||
}
|
||||
{
|
||||
AnyScalar A, B(40);
|
||||
A.swap(B);
|
||||
testEqual(A.ref<pvd::int32>(), 40);
|
||||
testOk1(B.empty());
|
||||
}
|
||||
|
||||
{
|
||||
AnyScalar A("world"), B;
|
||||
A.swap(B);
|
||||
testOk1(A.empty());
|
||||
testEqual(B.ref<std::string>(), "world");
|
||||
}
|
||||
{
|
||||
AnyScalar A("world"), B("hello");
|
||||
A.swap(B);
|
||||
testEqual(A.ref<std::string>(), "hello");
|
||||
testEqual(B.ref<std::string>(), "world");
|
||||
}
|
||||
{
|
||||
AnyScalar A("world"), B(40);
|
||||
A.swap(B);
|
||||
testEqual(A.ref<pvd::int32>(), 40);
|
||||
testEqual(B.ref<std::string>(), "world");
|
||||
}
|
||||
|
||||
{
|
||||
AnyScalar A(39), B;
|
||||
A.swap(B);
|
||||
testOk1(A.empty());
|
||||
testEqual(B.ref<pvd::int32>(), 39);
|
||||
}
|
||||
{
|
||||
AnyScalar A(39), B("hello");
|
||||
A.swap(B);
|
||||
testEqual(A.ref<std::string>(), "hello");
|
||||
testEqual(B.ref<pvd::int32>(), 39);
|
||||
}
|
||||
{
|
||||
AnyScalar A(39), B(40);
|
||||
A.swap(B);
|
||||
testEqual(A.ref<pvd::int32>(), 40);
|
||||
testEqual(B.ref<pvd::int32>(), 39);
|
||||
}
|
||||
}
|
||||
|
||||
void test_move()
|
||||
{
|
||||
testDiag("test_move()");
|
||||
#if __cplusplus>=201103L
|
||||
{
|
||||
AnyScalar x, y(std::move(x));
|
||||
testOk1(x.empty());
|
||||
testOk1(y.empty());
|
||||
}
|
||||
{
|
||||
AnyScalar x(5), y(std::move(x));
|
||||
testOk1(x.empty());
|
||||
testEqual(y.ref<pvd::int32>(), 5);
|
||||
}
|
||||
{
|
||||
AnyScalar x("hello"), y(std::move(x));
|
||||
testOk1(x.empty());
|
||||
testEqual(y.ref<std::string>(), "hello");
|
||||
}
|
||||
|
||||
{
|
||||
AnyScalar x, y;
|
||||
y = std::move(x);
|
||||
testOk1(x.empty());
|
||||
testOk1(y.empty());
|
||||
}
|
||||
{
|
||||
AnyScalar x, y(5);
|
||||
y = std::move(x);
|
||||
testOk1(x.empty());
|
||||
testOk1(y.empty());
|
||||
}
|
||||
{
|
||||
AnyScalar x, y("test");
|
||||
y = std::move(x);
|
||||
testOk1(x.empty());
|
||||
testOk1(y.empty());
|
||||
}
|
||||
#else
|
||||
testSkip(12, "No c++11");
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
MAIN(testanyscalar)
|
||||
{
|
||||
testPlan(66);
|
||||
try {
|
||||
test_empty();
|
||||
test_ctor();
|
||||
test_basic();
|
||||
test_swap();
|
||||
test_move();
|
||||
}catch(std::exception& e){
|
||||
testAbort("Unexpected exception: %s", e.what());
|
||||
}
|
||||
return testDone();
|
||||
}
|
@ -63,7 +63,7 @@ void test_fail()
|
||||
|
||||
} // namespace
|
||||
|
||||
MAIN(testanyscalar)
|
||||
MAIN(testgroupconfig)
|
||||
{
|
||||
testPlan(10);
|
||||
try {
|
||||
|
Reference in New Issue
Block a user