/** * 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 */ #ifdef _WIN32 #define NOMINMAX #endif #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "pv/typeCast.h" using epics::pvData::String; namespace { template struct testequal { static bool op(T A, T B) {return A==B; } }; template<> struct testequal { static bool op(double A, double B) {return fabs(A-B)<1e-300; } }; template<> struct testequal { static bool op(float A, float B) {return fabs(A-B)<1e-30; } }; template struct testcase { static void op(TO expect, FROM inp) { std::ostringstream msg; TO actual; try { actual = ::epics::pvData::castUnsafe(inp); //actual = ::epics::pvData::detail::cast_helper::op(inp); } catch(std::runtime_error& e) { msg<<"Failed to cast " < " <::op(actual, expect)) { msg<<"Failed cast gives unexpected value " < " < " < struct testfail { static void op(FROM inp) { std::ostringstream msg; TO actual; try { actual = ::epics::pvData::castUnsafe(inp); msg<<"Failed to generate expected error " < (" < (" <::op(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) testfail::op(VFRO) } // end namespace MAIN(testTypeCast) { testPlan(122); try { 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(x## N); \ std::transform(&x ## N, &x ## N+1, &x ## M, ::epics::pvData::castUnsafe) //#define CHECK(M, N) x## M = ::epics::pvData::detail::cast_helper::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 testDiag("Integer signed <=> unsigned"); TEST2(uint8_t, std::numeric_limits::max(), int8_t, -1); TEST2(uint16_t, std::numeric_limits::max(), int8_t, -1); TEST2(uint32_t, std::numeric_limits::max(), int8_t, -1); TEST2(uint64_t, std::numeric_limits::max(), int8_t, -1); testDiag("Integer unsigned promote (and demote when in range)"); 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); testDiag("Double to int w/ round towards zero (aka truncation)"); 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); testDiag("Float to int w/ round towards zero (aka truncation)"); TEST(int32_t, 2, float, 2.1f); TEST(int32_t, 2, float, 2.5f); TEST(int32_t, 2, float, 2.7f); TEST(int32_t, -2, float, -2.1f); TEST(int32_t, -2, float, -2.5f); TEST(int32_t, -2, float, -2.7f); testDiag("String Printing/parsing"); 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, "127", int32_t, std::numeric_limits::max()); TEST2(String, "-128", int32_t, std::numeric_limits::min()); TEST2(String, "255", int32_t, std::numeric_limits::max()); TEST2(String, "32767", int32_t, std::numeric_limits::max()); TEST2(String, "-32768", int32_t, std::numeric_limits::min()); TEST2(String, "65535", int32_t, std::numeric_limits::max()); TEST2(String, "2147483647", int32_t, std::numeric_limits::max()); TEST2(String, "-2147483648", int32_t, std::numeric_limits::min()); TEST2(String, "4294967295", uint32_t, std::numeric_limits::max()); TEST2(String, "9223372036854775807", int64_t, std::numeric_limits::max()); TEST2(String, "-9223372036854775808", int64_t, std::numeric_limits::min()); TEST2(String, "18446744073709551615", uint64_t, std::numeric_limits::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"); // any non-zero value is true TEST(String, "true", epics::pvData::boolean, 100); TEST2(String, "true", epics::pvData::boolean, 1); TEST2(String, "false", epics::pvData::boolean, 0); // Case insensitive TEST(epics::pvData::boolean, 1, String, "True"); TEST(epics::pvData::boolean, 0, String, "False"); TEST(epics::pvData::boolean, 1, String, "TRUE"); TEST(epics::pvData::boolean, 0, String, "FALSE"); testDiag("String Parsing"); 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"); TEST(int64_t, 15, String, "0xf"); TEST(int64_t, 15, String, "0xF"); TEST(int64_t, -15, String, "-0xF"); TEST(int64_t, 16, String, "0x10"); TEST(int64_t, -16, String, "-0x10"); TEST(int64_t, 7, String, "07"); TEST(int64_t, 8, String, "010"); TEST(int64_t, -7, String, "-07"); TEST(int64_t, -8, String, "-010"); testDiag("String parsing errors"); FAIL(int32_t, String, "hello!"); FAIL(int32_t, String, "42 is the answer"); FAIL(int64_t, String, "hello!"); FAIL(int64_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"); FAIL(epics::pvData::boolean, String, "hello"); FAIL(epics::pvData::boolean, String, "1"); FAIL(epics::pvData::boolean, String, "0"); FAIL(epics::pvData::boolean, String, "T"); FAIL(epics::pvData::boolean, String, "F"); testDiag("Floating point overflows"); 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(epicsNAN); testOk(isnan( xfloat ), "Test cast double NAN to float NAN yields: %f", xfloat); { std::string result[3]; const int32_t in[3] = { 1234, 506001, 42424242 }; testDiag("Test vcast int32 -> String"); epics::pvData::castUnsafeV(3, epics::pvData::pvString, (void*)result, epics::pvData::pvInt, (void*)in); testDiag("Yields %s %s %s", result[0].c_str(), result[1].c_str(), result[2].c_str()); testOk1(result[0]=="1234"); testOk1(result[1]=="506001"); testOk1(result[2]=="42424242"); } } catch(std::exception& e) { testAbort("Uncaught exception: %s", e.what()); } return testDone(); }