type casting with castUnsafe<TO>(FROM val)
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
373
pvDataApp/misc/parseToPOD.cpp
Normal file
373
pvDataApp/misc/parseToPOD.cpp
Normal 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
177
pvDataApp/misc/typeCast.h
Normal 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
|
||||
@@ -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
|
||||
|
||||
376
testApp/misc/testTypeCast.cpp
Normal file
376
testApp/misc/testTypeCast.cpp
Normal 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;
|
||||
}
|
||||
Reference in New Issue
Block a user