type casting with castUnsafe<TO>(FROM val)

This commit is contained in:
Michael Davidsaver
2013-04-15 14:39:42 -04:00
parent c435f71592
commit d7eada7216
5 changed files with 933 additions and 0 deletions

View File

@@ -24,6 +24,7 @@ INC += destroyable.h
INC += status.h
INC += sharedPtr.h
INC += localStaticLock.h
INC += typeCast.h
LIBSRCS += byteBuffer.cpp
LIBSRCS += bitSet.cpp
@@ -37,6 +38,7 @@ LIBSRCS += timer.cpp
LIBSRCS += status.cpp
LIBSRCS += messageQueue.cpp
LIBSRCS += localStaticLock.cpp
LIBSRCS += parseToPOD.cpp
SRC_DIRS += $(PVDATA)/pv

View File

@@ -0,0 +1,373 @@
#include <ctype.h>
#include <stdio.h>
#include <errno.h>
#include <float.h>
#include <epicsVersion.h>
#include <epicsMath.h>
#include <epicsStdlib.h>
#include <epicsString.h>
#include <epicsConvert.h>
#include "typeCast.h"
#ifndef EPICS_VERSION_INT
#define VERSION_INT(V,R,M,P) ( ((V)<<24) | ((R)<<16) | ((M)<<8) | (P))
#define EPICS_VERSION_INT VERSION_INT(EPICS_VERSION, EPICS_REVISION, EPICS_MODIFICATION, EPICS_PATCH_LEVEL)
#endif
#if EPICS_VERSION_INT < VERSION_INT(3,15,0,1)
/* integer conversion primatives added to epicsStdlib.c in 3.15.0.1 */
#define S_stdlib_noConversion 1 /* No digits to convert */
#define S_stdlib_extraneous 2 /* Extraneous characters */
#define S_stdlib_underflow 3 /* Too small to represent */
#define S_stdlib_overflow 4 /* Too large to represent */
#define S_stdlib_badBase 5 /* Number base not supported */
static int
epicsParseLong(const char *str, long *to, int base, char **units)
{
int c;
char *endp;
long value;
while ((c = *str) && isspace(c))
++str;
errno = 0;
value = strtol(str, &endp, base);
if (endp == str)
return S_stdlib_noConversion;
if (errno == EINVAL) /* Not universally supported */
return S_stdlib_badBase;
if (errno == ERANGE)
return S_stdlib_overflow;
while ((c = *endp) && isspace(c))
++endp;
if (c && !units)
return S_stdlib_extraneous;
*to = value;
if (units)
*units = endp;
return 0;
}
static int
epicsParseULong(const char *str, unsigned long *to, int base, char **units)
{
int c;
char *endp;
unsigned long value;
while ((c = *str) && isspace(c))
++str;
errno = 0;
value = strtoul(str, &endp, base);
if (endp == str)
return S_stdlib_noConversion;
if (errno == EINVAL) /* Not universally supported */
return S_stdlib_badBase;
if (errno == ERANGE)
return S_stdlib_overflow;
while ((c = *endp) && isspace(c))
++endp;
if (c && !units)
return S_stdlib_extraneous;
*to = value;
if (units)
*units = endp;
return 0;
}
static int
epicsParseDouble(const char *str, double *to, char **units)
{
int c;
char *endp;
double value;
while ((c = *str) && isspace(c))
++str;
errno = 0;
value = epicsStrtod(str, &endp);
if (endp == str)
return S_stdlib_noConversion;
if (errno == ERANGE)
return (value == 0) ? S_stdlib_underflow : S_stdlib_overflow;
while ((c = *endp) && isspace(c))
++endp;
if (c && !units)
return S_stdlib_extraneous;
*to = value;
if (units)
*units = endp;
return 0;
}
/* These call the primitives */
static int
epicsParseInt8(const char *str, epicsInt8 *to, int base, char **units)
{
long value;
int status = epicsParseLong(str, &value, base, units);
if (status)
return status;
if (value < -0x80 || value > 0x7f)
return S_stdlib_overflow;
*to = value;
return 0;
}
static int
epicsParseUInt8(const char *str, epicsUInt8 *to, int base, char **units)
{
unsigned long value;
int status = epicsParseULong(str, &value, base, units);
if (status)
return status;
if (value > 0xff && value <= ~0xffUL)
return S_stdlib_overflow;
*to = value;
return 0;
}
static int
epicsParseInt16(const char *str, epicsInt16 *to, int base, char **units)
{
long value;
int status = epicsParseLong(str, &value, base, units);
if (status)
return status;
if (value < -0x8000 || value > 0x7fff)
return S_stdlib_overflow;
*to = value;
return 0;
}
static int
epicsParseUInt16(const char *str, epicsUInt16 *to, int base, char **units)
{
unsigned long value;
int status = epicsParseULong(str, &value, base, units);
if (status)
return status;
if (value > 0xffff && value <= ~0xffffUL)
return S_stdlib_overflow;
*to = value;
return 0;
}
static int
epicsParseInt32(const char *str, epicsInt32 *to, int base, char **units)
{
long value;
int status = epicsParseLong(str, &value, base, units);
if (status)
return status;
#if (LONG_MAX > 0x7fffffff)
if (value < -0x80000000L || value > 0x7fffffffL)
return S_stdlib_overflow;
#endif
*to = value;
return 0;
}
static int
epicsParseUInt32(const char *str, epicsUInt32 *to, int base, char **units)
{
unsigned long value;
int status = epicsParseULong(str, &value, base, units);
if (status)
return status;
#if (ULONG_MAX > 0xffffffff)
if (value > 0xffffffffUL && value <= ~0xffffffffUL)
return S_stdlib_overflow;
#endif
*to = value;
return 0;
}
static int
epicsParseFloat(const char *str, float *to, char **units)
{
double value, abs;
int status = epicsParseDouble(str, &value, units);
if (status)
return status;
abs = fabs(value);
if (value > 0 && abs <= FLT_MIN)
return S_stdlib_underflow;
if (finite(value) && abs >= FLT_MAX)
return S_stdlib_overflow;
*to = value;
return 0;
}
#endif
/* do we need long long? */
#if INT_MAX == LONG_MAX
static int
epicsParseLongLong(const char *str, long long *to, int base, char **units)
{
int c;
char *endp;
long long value;
while ((c = *str) && isspace(c))
++str;
errno = 0;
value = strtoll(str, &endp, base);
if (endp == str)
return S_stdlib_noConversion;
if (errno == EINVAL) /* Not universally supported */
return S_stdlib_badBase;
if (errno == ERANGE)
return S_stdlib_overflow;
while ((c = *endp) && isspace(c))
++endp;
if (c && !units)
return S_stdlib_extraneous;
*to = value;
if (units)
*units = endp;
return 0;
}
static int
epicsParseULongLong(const char *str, unsigned long long *to, int base, char **units)
{
int c;
char *endp;
unsigned long long value;
while ((c = *str) && isspace(c))
++str;
errno = 0;
value = strtoull(str, &endp, base);
if (endp == str)
return S_stdlib_noConversion;
if (errno == EINVAL) /* Not universally supported */
return S_stdlib_badBase;
if (errno == ERANGE)
return S_stdlib_overflow;
while ((c = *endp) && isspace(c))
++endp;
if (c && !units)
return S_stdlib_extraneous;
*to = value;
if (units)
*units = endp;
return 0;
}
#endif
static
void handleParseError(int err)
{
switch(err) {
case 0: break;
case S_stdlib_noConversion: throw std::runtime_error("parseToPOD: No digits to convert");
case S_stdlib_extraneous: throw std::runtime_error("parseToPOD: Extraneous characters");
case S_stdlib_underflow: throw std::runtime_error("parseToPOD: Too small to represent");
case S_stdlib_overflow: throw std::runtime_error("parseToPOD: Too large to represent");
case S_stdlib_badBase: throw std::runtime_error("parseToPOD: Number base not supported");
default:
throw std::runtime_error("parseToPOD: unknown error");
}
}
namespace epics { namespace pvData { namespace detail {
void parseToPOD(const std::string& in, int8 *out) {
epicsInt8 temp;
int err = epicsParseInt8(in.c_str(), &temp, 0, NULL);
if(err) handleParseError(err);
else *out = temp;
}
#define INTFN(T, S) \
void parseToPOD(const std::string& in, T *out) { \
int err = epicsParse ## S(in.c_str(), out, 0, NULL); \
if(err) handleParseError(err); \
}
INTFN(char, Int8);
INTFN(uint8_t, UInt8);
INTFN(int16_t, Int16);
INTFN(uint16_t, UInt16);
INTFN(int32_t, Int32);
INTFN(uint32_t, UInt32);
void parseToPOD(const std::string& in, int64_t *out) {
#if INT_MAX == LONG_MAX
int err = epicsParseLongLong(in.c_str(), out, 0, NULL);
#else
int err = epicsParseLong(in.c_str(), out, 0, NULL);
#endif
if(err) handleParseError(err);
}
void parseToPOD(const std::string& in, uint64_t *out) {
#if INT_MAX == LONG_MAX
int err = epicsParseULongLong(in.c_str(), out, 0, NULL);
#else
int err = epicsParseULong(in.c_str(), out, 0, NULL);
#endif
if(err) handleParseError(err);
}
void parseToPOD(const std::string& in, float *out) {
int err = epicsParseFloat(in.c_str(), out, NULL);
if(err) handleParseError(err);
}
void parseToPOD(const std::string& in, double *out) {
int err = epicsParseDouble(in.c_str(), out, NULL);
if(err) handleParseError(err);
}
}}}

177
pvDataApp/misc/typeCast.h Normal file
View File

@@ -0,0 +1,177 @@
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/* Author: Michael Davidsaver */
#ifndef PVTYPECAST_H
#define PVTYPECAST_H
#include <stdexcept>
#include <sstream>
#include <epicsConvert.h>
#include <pv/pvType.h>
// gently nudge the compiler to inline our wrappers
#if defined(__GNUC__)
# define FORCE_INLINE __attribute__((always_inline)) inline
#elif defined(_MSC_VER)
# define FORCE_INLINE __forceinline
#else
# define FORCE_INLINE inline
#endif
namespace epics { namespace pvData {
typedef std::string String;
namespace detail {
// parseToPOD wraps the epicsParse*() functions in one name
// and throws exceptions
void parseToPOD(const std::string&, char *out);
void parseToPOD(const std::string&, int8_t *out);
void parseToPOD(const std::string&, uint8_t *out);
void parseToPOD(const std::string&, int16_t *out);
void parseToPOD(const std::string&, uint16_t *out);
void parseToPOD(const std::string&, int32_t *out);
void parseToPOD(const std::string&, uint32_t *out);
void parseToPOD(const std::string&, int64_t *out);
void parseToPOD(const std::string&, uint64_t *out);
void parseToPOD(const std::string&, float *out);
void parseToPOD(const std::string&, double *out);
/* want to pass POD types by value,
* and String by const reference
*/
template<typename ARG>
struct cast_arg { typedef ARG arg; };
template<>
struct cast_arg<String> { typedef const String& arg; };
// test to allow specialization only when A!=B
template<typename A, typename B, typename R = void>
struct not_same_type {typedef R type;};
template<typename A>
struct not_same_type<A,A> {};
// trick std::ostream into treating char's as numbers
// by promoting char to int
template<typename T>
struct print_cast { typedef T type; };
template<>
struct print_cast<char> { typedef int type; };
template<>
struct print_cast<signed char> { typedef signed int type; };
template<>
struct print_cast<unsigned char> { typedef unsigned int type; };
// default to C++ type casting
template<typename TO, typename FROM, class Enable = void>
struct cast_helper {
static FORCE_INLINE TO op(FROM from) {
return static_cast<TO>(from);
}
};
// special handling when down-casting double to float
template<>
struct cast_helper<float, double> {
static FORCE_INLINE float op(double from) {
return epicsConvertDoubleToFloat(from);
}
};
// print POD to string
// when String!=FROM
template<typename FROM>
struct cast_helper<String, FROM, typename not_same_type<String,FROM>::type> {
static String op(FROM from) {
typedef typename print_cast<FROM>::type ptype;
std::ostringstream strm;
strm << (ptype)from;
if(strm.fail())
throw std::runtime_error("Cast to string failed");
return strm.str();
}
};
// parse POD from string
// TO!=String
template<typename TO>
struct cast_helper<TO, String, typename not_same_type<TO,String>::type> {
static FORCE_INLINE TO op(const String& from) {
TO ret;
parseToPOD(from, &ret);
return ret;
}
};
} // end detail
/** @brief Casting/converting between supported scalar types.
*
* Supported types: uint8_t, int8_t, uint16_t, int16_t,
* uint32_t, int32_t, uint64_t, int64_t,
* float, double, String
*
* As defined in pvType.h
*
@throws std::runtime_error when the cast is not possible.
@throws std::bad_alloc when the cast is not possible.
*
@section convertg Conversion Guarantees
*
* Conversions which always produce a correct result.
*
* - signed integer -> larger signed integer
* - unsigned integer -> larger unsigned integer
* - integer -> float or double (where sizeof(integer)<sizeof(floating))
* - float -> double
*
* Conversions where out of range inputs always produce
* a defined result, but may not be reversable.
*
* - double -> float. When abs(value) is outside the range
* [FLT_MIN, FLT_MAX] the value is clipped to FLT_MIN or FLT_MAX
* with the sign preserved.
*
* Conversions where invalid or out of range inputs result
* in an exception.
*
* - non-String -> String
* - String -> non-String
* - String -> String (throws only std::bad_alloc)
*
* Conversions where out of range inputs produce undefined
* results.
*
* - signed integer -> smaller signed integer
* - unsigned integer -> smaller unsigned integer
* - signed integer <-> unsigned integer
* - integer -> float or double (where sizeof(integer)>=sizeof(floating))
* - float or double -> integer. The floating point value
* is rounded towards zero. However, the result for values
* too large to be represented by the integer type
* is not defined.
*
@section stringf String formats
*
* - Numbers beginning with 1-9 are parsed as base-10.
* - Numbers beginning with '0x' are parsed as base-16
* - Numbers beginning with '0' are parsed as base-8.
* - Hex numbers are case insensitive.
* - Exponential numbers may use either 'e' or 'E'.
*/
template<typename TO, typename FROM>
static FORCE_INLINE TO castUnsafe(const FROM& from)
{
return detail::cast_helper<TO,FROM>::op(from);
}
}} // end namespace
#undef FORCE_INLINE
#endif // PVTYPECAST_H

View File

@@ -42,6 +42,11 @@ PROD_HOST += testMessageQueue
testMessageQueue_SRCS += testMessageQueue.cpp
testMessageQueue_LIBS += pvData Com
PROD_HOST += testTypeCast
testTypeCast_SRCS += testTypeCast.cpp
testTypeCast_LIBS += pvData Com
include $(TOP)/configure/RULES
#----------------------------------------
# ADD RULES AFTER THIS LINE

View File

@@ -0,0 +1,376 @@
/**
* Copyright - See the COPYRIGHT that is included with this distribution.
* EPICS pvData is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
*/
/* Author: Michael Davidsaver */
#include <fstream>
#include <iostream>
#include <limits>
#include <typeinfo>
#include <stddef.h>
#include <stdlib.h>
#include <stddef.h>
#include <string.h>
#include <assert.h>
#include <stdio.h>
#include <float.h>
#include <epicsMath.h>
#include <epicsMath.h>
#include "pv/typeCast.h"
#include <stdint.h>
using epics::pvData::String;
namespace {
template<typename T>
struct testequal {
static bool op(T A, T B) {return A==B; }
};
template<>
struct testequal<double> {
static bool op(double A, double B) {return fabs(A-B)<1e-300; }
};
template<>
struct testequal<float> {
static bool op(float A, float B) {return fabs(A-B)<1e-30; }
};
template<typename TO, typename FROM>
struct testcase {
static bool op(std::ostream& msg, TO expect, FROM inp)
{
TO actual;
try {
actual = ::epics::pvData::castUnsafe<TO,FROM>(inp);
//actual = ::epics::pvData::detail::cast_helper<TO,FROM>::op(inp);
} catch(std::runtime_error& e) {
msg<<"Failed to cast "
<<inp<<" ("<<typeid(FROM).name()<<") -> "
<<expect<<" ("<<typeid(TO).name()<<")\n Error: "
<<typeid(e).name()<<"("<<e.what()<<")\n";
return false;
}
if(!testequal<TO>::op(actual, expect)) {
msg<<"Failed cast gives unexpected value "
<<inp<<" ("<<typeid(FROM).name()<<") -> "
<<expect<<" ("<<typeid(TO).name()<<") yields: "
<<actual<<"\n";
return false;
}
msg<<"Pass cast "
<<inp<<" ("<<typeid(FROM).name()<<") -> "
<<expect<<" ("<<typeid(TO).name()<<") yields: "
<<actual<<"\n";
return true;
}
};
template<typename TO, typename FROM>
struct testfail {
static bool op(std::ostream& msg, FROM inp)
{
TO actual;
try {
actual = ::epics::pvData::castUnsafe<TO,FROM>(inp);
msg<<"Failed to generate expected error "
<<inp<<" ("<<typeid(FROM).name()<<") -> ("
<<typeid(TO).name()<<") yields: "
<<actual<<"\n";
return false;
} catch(std::runtime_error& e) {
msg<<"Got expected error "
<<inp<<" ("<<typeid(FROM).name()<<") -> ("
<<typeid(TO).name()<<") fails with: "
<<e.what()<<"\n";
return true;
}
}
};
// Test cast
#define TEST(TTO, VTO, TFRO, VFRO) fail |= !testcase<TTO, TFRO>::op(*out, VTO, VFRO)
// Test cast and reverse
#define TEST2(TTO, VTO, TFRO, VFRO) TEST(TTO, VTO, TFRO, VFRO); TEST(TFRO, VFRO, TTO, VTO)
#define FAIL(TTO, TFRO, VFRO) fail |= !testfail<TTO,TFRO>::op(*out, VFRO)
} // end namespace
int main(int argc,char *argv[])
{
std::ostream *out = &std::cerr;
std::ofstream outf;
if(argc>1){
outf.open(argv[1]);
if(!outf.good()) {
std::cerr<<"Failed to open "<<argv[1]<<" for output!\n";
} else {
out = &outf;
}
}
bool fail=false;
int8_t xint8=0;
uint8_t xuint8=0;
int16_t xint16=0;
uint16_t xuint16=0;
int32_t xint32=0;
uint32_t xuint32=0;
int64_t xint64=0;
uint64_t xuint64=0;
float xfloat=0.0;
double xdouble=0.0;
epics::pvData::String xString("0");
typedef float float_t;
typedef double double_t;
typedef epics::pvData::String String_t;
// force all possibilities to be compiled
#define CHECK(M, N) x## M = ::epics::pvData::castUnsafe<M ##_t>(x## N)
//#define CHECK(M, N) x## M = ::epics::pvData::detail::cast_helper<M ##_t,N ##_t>::op(x## N)
CHECK(int8, int8);
CHECK(int8, uint8);
CHECK(int8, int16);
CHECK(int8, uint16);
CHECK(int8, int32);
CHECK(int8, uint32);
CHECK(int8, int64);
CHECK(int8, uint64);
CHECK(int8, float);
CHECK(int8, double);
CHECK(int8, String);
CHECK(uint8, int8);
CHECK(uint8, uint8);
CHECK(uint8, int16);
CHECK(uint8, uint16);
CHECK(uint8, int32);
CHECK(uint8, uint32);
CHECK(uint8, int64);
CHECK(uint8, uint64);
CHECK(uint8, float);
CHECK(uint8, double);
CHECK(uint8, String);
CHECK(int16, int8);
CHECK(int16, uint8);
CHECK(int16, int16);
CHECK(int16, uint16);
CHECK(int16, int32);
CHECK(int16, uint32);
CHECK(int16, int64);
CHECK(int16, uint64);
CHECK(int16, float);
CHECK(int16, double);
CHECK(int16, String);
CHECK(uint16, int8);
CHECK(uint16, uint8);
CHECK(uint16, int16);
CHECK(uint16, uint16);
CHECK(uint16, int32);
CHECK(uint16, uint32);
CHECK(uint16, int64);
CHECK(uint16, uint64);
CHECK(uint16, float);
CHECK(uint16, double);
CHECK(uint16, String);
CHECK(int32, int8);
CHECK(int32, uint8);
CHECK(int32, int16);
CHECK(int32, uint16);
CHECK(int32, int32);
CHECK(int32, uint32);
CHECK(int32, int64);
CHECK(int32, uint64);
CHECK(int32, float);
CHECK(int32, double);
CHECK(int32, String);
CHECK(uint32, int8);
CHECK(uint32, uint8);
CHECK(uint32, int16);
CHECK(uint32, uint16);
CHECK(uint32, int32);
CHECK(uint32, uint32);
CHECK(uint32, int64);
CHECK(uint32, uint64);
CHECK(uint32, float);
CHECK(uint32, double);
CHECK(uint32, String);
CHECK(int64, int8);
CHECK(int64, uint8);
CHECK(int64, int16);
CHECK(int64, uint16);
CHECK(int64, int32);
CHECK(int64, uint32);
CHECK(int64, int64);
CHECK(int64, uint64);
CHECK(int64, float);
CHECK(int64, double);
//CHECK(int64, String);
CHECK(uint64, int8);
CHECK(uint64, uint8);
CHECK(uint64, int16);
CHECK(uint64, uint16);
CHECK(uint64, int32);
CHECK(uint64, uint32);
CHECK(uint64, int64);
CHECK(uint64, uint64);
CHECK(uint64, float);
CHECK(uint64, double);
//CHECK(uint64, String);
CHECK(float, int8);
CHECK(float, uint8);
CHECK(float, int16);
CHECK(float, uint16);
CHECK(float, int32);
CHECK(float, uint32);
CHECK(float, int64);
CHECK(float, uint64);
CHECK(float, float);
CHECK(float, double);
CHECK(float, String);
CHECK(double, int8);
CHECK(double, uint8);
CHECK(double, int16);
CHECK(double, uint16);
CHECK(double, int32);
CHECK(double, uint32);
CHECK(double, int64);
CHECK(double, uint64);
CHECK(double, float);
CHECK(double, double);
CHECK(double, String);
CHECK(String, int8);
CHECK(String, uint8);
CHECK(String, int16);
CHECK(String, uint16);
CHECK(String, int32);
CHECK(String, uint32);
CHECK(String, int64);
CHECK(String, uint64);
CHECK(String, float);
CHECK(String, double);
CHECK(String, String);
#undef CHECK
*out << "Integer signed <=> unsigned\n";
TEST2(uint8_t, std::numeric_limits<uint8_t>::max(), int8_t, -1);
TEST2(uint16_t, std::numeric_limits<uint16_t>::max(), int8_t, -1);
TEST2(uint32_t, std::numeric_limits<uint32_t>::max(), int8_t, -1);
TEST2(uint64_t, std::numeric_limits<uint64_t>::max(), int8_t, -1);
*out << "Integer unsigned promote (and demote when in range)\n";
TEST2(uint16_t, 0xff, uint8_t, 0xff);
TEST2(uint32_t, 0xffff, uint16_t, 0xffff);
TEST2(uint64_t, 0xffffffffu, uint32_t, 0xffffffffu);
TEST2(int16_t, 0x7f, int8_t, 0x7f);
TEST2(int32_t, 0x7fff, int16_t, 0x7fff);
TEST2(int64_t, 0x7fffffff, int32_t, 0x7fffffff);
*out << "Double to int w/ round towards zero (aka truncation)\n";
TEST(int32_t, 2, double, 2.1);
TEST(int32_t, 2, double, 2.5);
TEST(int32_t, 2, double, 2.7);
TEST(int32_t, -2, double, -2.1);
TEST(int32_t, -2, double, -2.5);
TEST(int32_t, -2, double, -2.7);
*out << "Float to int w/ round towards zero (aka truncation)\n";
TEST(int32_t, 2, float, 2.1);
TEST(int32_t, 2, float, 2.5);
TEST(int32_t, 2, float, 2.7);
TEST(int32_t, -2, float, -2.1);
TEST(int32_t, -2, float, -2.5);
TEST(int32_t, -2, float, -2.7);
*out << "String Printing/parsing\n";
TEST2(String, "1", int32_t, 1);
TEST2(String, "-1", int32_t, -1);
TEST2(String, "1", int8_t, 1);
TEST2(String, "-1", int8_t, -1);
TEST2(String, "1", uint8_t, 1);
TEST2(String, "-1", char, -1);
TEST2(String, "127", int32_t, std::numeric_limits<int8_t>::max());
TEST2(String, "-128", int32_t, std::numeric_limits<int8_t>::min());
TEST2(String, "255", int32_t, std::numeric_limits<uint8_t>::max());
TEST2(String, "32767", int32_t, std::numeric_limits<int16_t>::max());
TEST2(String, "-32768", int32_t, std::numeric_limits<int16_t>::min());
TEST2(String, "65535", int32_t, std::numeric_limits<uint16_t>::max());
TEST2(String, "2147483647", int32_t, std::numeric_limits<int32_t>::max());
TEST2(String, "-2147483648", int32_t, std::numeric_limits<int32_t>::min());
TEST2(String, "4294967295", uint32_t, std::numeric_limits<uint32_t>::max());
TEST2(String, "9223372036854775807", int64_t, std::numeric_limits<int64_t>::max());
TEST2(String, "-9223372036854775808", int64_t, std::numeric_limits<int64_t>::min());
TEST2(String, "18446744073709551615", uint64_t, std::numeric_limits<uint64_t>::max());
TEST2(String, "1.1", double, 1.1);
TEST2(String, "1.1e+100", double, 1.1e100);
TEST2(String, "1.1e-100", double, 1.1e-100);
TEST(double, 1.1e100, String, "1.1E+100");
*out << "String Parsing\n";
TEST(int32_t, 15, String, "0xf");
TEST(int32_t, 15, String, "0xF");
TEST(int32_t, -15, String, "-0xF");
TEST(int32_t, 16, String, "0x10");
TEST(int32_t, -16, String, "-0x10");
TEST(int32_t, 7, String, "07");
TEST(int32_t, 8, String, "010");
TEST(int32_t, -7, String, "-07");
TEST(int32_t, -8, String, "-010");
*out << "String parsing errors\n";
FAIL(int32_t, String, "hello!");
FAIL(int32_t, String, "42 is the answer");
FAIL(double, String, "hello!");
FAIL(double, String, "42 is the answer");
FAIL(int8_t, String, "1000");
FAIL(int8_t, String, "-1000");
FAIL(double, String, "1e+10000000");
*out << "Floating point overflows\n";
TEST(float, FLT_MAX, double, 1e300);
TEST(float, -FLT_MAX, double, -1e300);
TEST(float, FLT_MIN, double, 1e-300);
TEST(float, -FLT_MIN, double, -1e-300);
xfloat = ::epics::pvData::castUnsafe<float,double>(epicsNAN);
*out << "Cast double NAN to float NAN yields: "<<xfloat<<"\n";
fail |= !isnan( xfloat );
return fail ? 1 : 0;
}