From c634daef6a9de8df7245d4fb6fbbcdf0ad7ac7ff Mon Sep 17 00:00:00 2001 From: Tim Mayberry Date: Mon, 3 Aug 2015 12:32:31 +1000 Subject: Add locale independent and thread safe string conversion API with tests All conversions are performed as if in the "C" locale but without actually changing locale. This is a wrapper around printf/sscanf for int types which aren't affected by locale and uses glib functions g_ascii_strtod and g_ascii_dtostr for float/double types. My first attempt at this used std::stringstream and ios::imbue(std::locale::classic()) as it should be thread safe, but testing shows it is not for gcc/mingw-w64 on Windows, and possibly also some versions of macOS/OS X. Use "yes" and "no" when converting a boolean in PBD::string_to as this seems to be the convention used throughout libardour which will allow using string_to in those cases. Add accepted bool string values from PBD::string_is_affirmative to PBD::string_to Mark strings in pbd/string_convert.cc as not for translation Add u/int16_t string conversions to pbd/string_convert.h and tests Add DEBUG_TRACE output on conversion errors Add int8_t/uint8_t conversions(using int16/uint16 types) to string_convert.h Add support for converting an infinity expression to/from string Follows the C99/C11 standard for strtof/strtod where subject sequence is an optional plus or minus sign then INF or INFINITY, ignoring case. --- libs/pbd/test/string_convert_test.cc | 651 +++++++++++++++++++++++++++++++++++ libs/pbd/test/string_convert_test.h | 30 ++ 2 files changed, 681 insertions(+) create mode 100644 libs/pbd/test/string_convert_test.cc create mode 100644 libs/pbd/test/string_convert_test.h (limited to 'libs/pbd/test') diff --git a/libs/pbd/test/string_convert_test.cc b/libs/pbd/test/string_convert_test.cc new file mode 100644 index 0000000000..f383aed8a2 --- /dev/null +++ b/libs/pbd/test/string_convert_test.cc @@ -0,0 +1,651 @@ +#include "string_convert_test.h" + +#include +#include + +#include +#include + +#include + +#include + +#include "pbd/string_convert.h" + +using namespace PBD; +using namespace std; + +CPPUNIT_TEST_SUITE_REGISTRATION (StringConvertTest); + +static std::vector get_test_locales () +{ + std::vector locales; + +#ifdef PLATFORM_WINDOWS + locales.push_back("French_France.1252"); // must be first + locales.push_back("Dutch_Netherlands.1252"); + locales.push_back("Italian_Italy.1252"); + locales.push_back("Farsi_Iran.1256"); + locales.push_back("Chinese_China.936"); + locales.push_back("Czech_Czech Republic.1250"); +#else + locales.push_back("fr_FR"); // French France // must be first + locales.push_back("nl_NL"); // Dutch - Netherlands + locales.push_back("it_IT"); // Italian + locales.push_back("fa_IR"); // Farsi Iran + locales.push_back("zh_CN"); // Chinese + locales.push_back("cs_CZ"); // Czech +#endif + return locales; +} + + +namespace { + +class LocaleGuard { +public: + // RAII class that sets the global C locale and then resets it to its + // previous setting when going out of scope + LocaleGuard (const std::string& locale) + { + m_previous_locale = setlocale (LC_ALL, NULL); + + CPPUNIT_ASSERT (m_previous_locale != NULL); + + const char* new_locale = setlocale (LC_ALL, locale.c_str ()); + + CPPUNIT_ASSERT (new_locale != NULL); + + CPPUNIT_ASSERT (locale == new_locale); + } + + ~LocaleGuard () + { + CPPUNIT_ASSERT (setlocale (LC_ALL, m_previous_locale) != NULL); + } + +private: + const char* m_previous_locale; +}; + +} // anon namespace + +static const std::string MAX_INT16_STR ("32767"); +static const std::string MIN_INT16_STR ("-32768"); + +typedef void (*TestFunctionType)(void); + +void +test_function_for_locales (TestFunctionType test_func) +{ + const std::vector locales = get_test_locales(); + + for (std::vector::const_iterator ci = locales.begin (); + ci != locales.end (); + ++ci) { + LocaleGuard guard (*ci); + test_func (); + } +} + +void +_test_int16_conversion () +{ + string str; + CPPUNIT_ASSERT (int16_to_string (numeric_limits::max (), str)); + CPPUNIT_ASSERT_EQUAL (MAX_INT16_STR, str); + + int16_t val = 0; + CPPUNIT_ASSERT (string_to_int16 (str, val)); + CPPUNIT_ASSERT_EQUAL (numeric_limits::max (), val); + + CPPUNIT_ASSERT (int16_to_string (numeric_limits::min (), str)); + CPPUNIT_ASSERT_EQUAL (MIN_INT16_STR, str); + + CPPUNIT_ASSERT (string_to_int16 (str, val)); + CPPUNIT_ASSERT_EQUAL (numeric_limits::min (), val); + + // test the string_to/to_string templates + int16_t max = numeric_limits::max (); + CPPUNIT_ASSERT_EQUAL (max, string_to(to_string (max))); + + int16_t min = numeric_limits::min (); + CPPUNIT_ASSERT_EQUAL (min, string_to(to_string (min))); +} + +void +StringConvertTest::test_int16_conversion () +{ + test_function_for_locales(&_test_int16_conversion); +} + +static const std::string MAX_UINT16_STR("65535"); +static const std::string MIN_UINT16_STR("0"); + +void +_test_uint16_conversion () +{ + string str; + CPPUNIT_ASSERT (uint16_to_string (numeric_limits::max (), str)); + CPPUNIT_ASSERT_EQUAL (MAX_UINT16_STR, str); + + uint16_t val = 0; + CPPUNIT_ASSERT (string_to_uint16 (str, val)); + CPPUNIT_ASSERT_EQUAL (numeric_limits::max (), val); + + CPPUNIT_ASSERT (uint16_to_string (numeric_limits::min (), str)); + CPPUNIT_ASSERT_EQUAL (MIN_UINT16_STR, str); + + CPPUNIT_ASSERT (string_to_uint16 (str, val)); + CPPUNIT_ASSERT_EQUAL (numeric_limits::min (), val); + + // test the string_to/to_string templates + uint16_t max = numeric_limits::max (); + CPPUNIT_ASSERT_EQUAL (max, string_to(to_string (max))); + + uint16_t min = numeric_limits::min (); + CPPUNIT_ASSERT_EQUAL (min, string_to(to_string (min))); +} + +void +StringConvertTest::test_uint16_conversion () +{ + test_function_for_locales(&_test_uint16_conversion); +} + +static const std::string MAX_INT32_STR ("2147483647"); +static const std::string MIN_INT32_STR ("-2147483648"); + +void +_test_int32_conversion () +{ + string str; + CPPUNIT_ASSERT (int32_to_string (numeric_limits::max (), str)); + CPPUNIT_ASSERT_EQUAL (MAX_INT32_STR, str); + + int32_t val = 0; + CPPUNIT_ASSERT (string_to_int32 (str, val)); + CPPUNIT_ASSERT_EQUAL (numeric_limits::max (), val); + + CPPUNIT_ASSERT (int32_to_string (numeric_limits::min (), str)); + CPPUNIT_ASSERT_EQUAL (MIN_INT32_STR, str); + + CPPUNIT_ASSERT (string_to_int32 (str, val)); + CPPUNIT_ASSERT_EQUAL (numeric_limits::min (), val); + + // test the string_to/to_string templates + int32_t max = numeric_limits::max (); + CPPUNIT_ASSERT_EQUAL (max, string_to(to_string (max))); + + int32_t min = numeric_limits::min (); + CPPUNIT_ASSERT_EQUAL (min, string_to(to_string (min))); +} + +void +StringConvertTest::test_int32_conversion () +{ + test_function_for_locales(&_test_int32_conversion); +} + +static const std::string MAX_UINT32_STR("4294967295"); +static const std::string MIN_UINT32_STR("0"); + +void +_test_uint32_conversion () +{ + string str; + CPPUNIT_ASSERT (uint32_to_string (numeric_limits::max (), str)); + CPPUNIT_ASSERT_EQUAL (MAX_UINT32_STR, str); + + uint32_t val = 0; + CPPUNIT_ASSERT (string_to_uint32 (str, val)); + CPPUNIT_ASSERT_EQUAL (numeric_limits::max (), val); + + CPPUNIT_ASSERT (uint32_to_string (numeric_limits::min (), str)); + CPPUNIT_ASSERT_EQUAL (MIN_UINT32_STR, str); + + CPPUNIT_ASSERT (string_to_uint32 (str, val)); + CPPUNIT_ASSERT_EQUAL (numeric_limits::min (), val); + + // test the string_to/to_string templates + uint32_t max = numeric_limits::max (); + CPPUNIT_ASSERT_EQUAL (max, string_to(to_string (max))); + + uint32_t min = numeric_limits::min (); + CPPUNIT_ASSERT_EQUAL (min, string_to(to_string (min))); +} + +void +StringConvertTest::test_uint32_conversion () +{ + test_function_for_locales(&_test_uint32_conversion); +} + +static const std::string MAX_INT64_STR ("9223372036854775807"); +static const std::string MIN_INT64_STR ("-9223372036854775808"); + +void +_test_int64_conversion () +{ + string str; + CPPUNIT_ASSERT (int64_to_string (numeric_limits::max (), str)); + CPPUNIT_ASSERT_EQUAL (MAX_INT64_STR, str); + + int64_t val = 0; + CPPUNIT_ASSERT (string_to_int64 (str, val)); + CPPUNIT_ASSERT_EQUAL (numeric_limits::max (), val); + + CPPUNIT_ASSERT (int64_to_string (numeric_limits::min (), str)); + CPPUNIT_ASSERT_EQUAL (MIN_INT64_STR, str); + + CPPUNIT_ASSERT (string_to_int64 (str, val)); + CPPUNIT_ASSERT_EQUAL (numeric_limits::min (), val); + + // test the string_to/to_string templates + int64_t max = numeric_limits::max (); + CPPUNIT_ASSERT_EQUAL (max, string_to(to_string (max))); + + int64_t min = numeric_limits::min (); + CPPUNIT_ASSERT_EQUAL (min, string_to(to_string (min))); +} + +void +StringConvertTest::test_int64_conversion () +{ + test_function_for_locales(&_test_int64_conversion); +} + +static const std::string MAX_UINT64_STR ("18446744073709551615"); +static const std::string MIN_UINT64_STR ("0"); + +void +_test_uint64_conversion () +{ + string str; + CPPUNIT_ASSERT (uint64_to_string (numeric_limits::max (), str)); + CPPUNIT_ASSERT_EQUAL (MAX_UINT64_STR, str); + + uint64_t val = 0; + CPPUNIT_ASSERT (string_to_uint64 (str, val)); + CPPUNIT_ASSERT_EQUAL (numeric_limits::max (), val); + + CPPUNIT_ASSERT (uint64_to_string (numeric_limits::min (), str)); + CPPUNIT_ASSERT_EQUAL (MIN_UINT64_STR, str); + + CPPUNIT_ASSERT (string_to_uint64 (str, val)); + CPPUNIT_ASSERT_EQUAL (numeric_limits::min (), val); + + // test the string_to/to_string templates + uint64_t max = numeric_limits::max (); + CPPUNIT_ASSERT_EQUAL (max, string_to(to_string (max))); + + uint64_t min = numeric_limits::min (); + CPPUNIT_ASSERT_EQUAL (min, string_to(to_string (min))); +} + +void +StringConvertTest::test_uint64_conversion () +{ + test_function_for_locales(&_test_uint64_conversion); +} + +static const std::string POS_INFINITY_STR ("infinity"); +static const std::string NEG_INFINITY_STR ("-infinity"); +static const std::string POS_INFINITY_CAPS_STR ("INFINITY"); +static const std::string NEG_INFINITY_CAPS_STR ("-INFINITY"); +static const std::string POS_INF_STR ("inf"); +static const std::string NEG_INF_STR ("-inf"); +static const std::string POS_INF_CAPS_STR ("INF"); +static const std::string NEG_INF_CAPS_STR ("-INF"); + +static +std::vector +_pos_infinity_strings () +{ + std::vector vec; + vec.push_back (POS_INFINITY_STR); + vec.push_back (POS_INFINITY_CAPS_STR); + vec.push_back (POS_INF_STR); + vec.push_back (POS_INF_CAPS_STR); + return vec; +} + +static +std::vector +_neg_infinity_strings () +{ + std::vector vec; + vec.push_back (NEG_INFINITY_STR); + vec.push_back (NEG_INFINITY_CAPS_STR); + vec.push_back (NEG_INF_STR); + vec.push_back (NEG_INF_CAPS_STR); + return vec; +} + +template +void +_test_infinity_conversion () +{ + const FloatType pos_infinity = numeric_limits::infinity (); + const FloatType neg_infinity = -numeric_limits::infinity (); + + // Check float -> string + string str; + CPPUNIT_ASSERT (to_string (pos_infinity, str)); + CPPUNIT_ASSERT_EQUAL (POS_INF_STR, str); + + CPPUNIT_ASSERT (to_string (neg_infinity, str)); + CPPUNIT_ASSERT_EQUAL (NEG_INF_STR, str); + + // Check string -> float for all supported string representations of "INFINITY" + std::vector pos_inf_strings = _pos_infinity_strings (); + + for (std::vector::const_iterator i = pos_inf_strings.begin (); + i != pos_inf_strings.end (); ++i) { + FloatType pos_infinity_arg; + CPPUNIT_ASSERT (string_to (*i, pos_infinity_arg)); + CPPUNIT_ASSERT_EQUAL (pos_infinity_arg, pos_infinity); + } + + // Check string -> float for all supported string representations of "-INFINITY" + std::vector neg_inf_strings = _neg_infinity_strings (); + + for (std::vector::const_iterator i = neg_inf_strings.begin (); + i != neg_inf_strings.end (); ++i) { + FloatType neg_infinity_arg; + CPPUNIT_ASSERT (string_to (*i, neg_infinity_arg)); + CPPUNIT_ASSERT_EQUAL (neg_infinity_arg, neg_infinity); + } + + // Check round-trip equality + CPPUNIT_ASSERT_EQUAL (pos_infinity, string_to (to_string (pos_infinity))); + CPPUNIT_ASSERT_EQUAL (neg_infinity, string_to (to_string (neg_infinity))); +} + +static const std::string MAX_FLOAT_WIN ("3.4028234663852886e+038"); +static const std::string MIN_FLOAT_WIN ("1.1754943508222875e-038"); +static const std::string MAX_FLOAT_STR ("3.4028234663852886e+38"); +static const std::string MIN_FLOAT_STR ("1.1754943508222875e-38"); + +void +_test_float_conversion () +{ + // check float to string and back again for min and max float values + string str; + CPPUNIT_ASSERT (float_to_string (numeric_limits::max (), str)); +#ifdef PLATFORM_WINDOWS + CPPUNIT_ASSERT_EQUAL (MAX_FLOAT_WIN, str); +#else + CPPUNIT_ASSERT_EQUAL (MAX_FLOAT_STR, str); +#endif + + float val = 0.0f; + CPPUNIT_ASSERT (string_to_float (str, val)); + CPPUNIT_ASSERT_DOUBLES_EQUAL ( + numeric_limits::max (), val, numeric_limits::epsilon ()); + + CPPUNIT_ASSERT (float_to_string (numeric_limits::min (), str)); +#ifdef PLATFORM_WINDOWS + CPPUNIT_ASSERT_EQUAL (MIN_FLOAT_WIN, str); +#else + CPPUNIT_ASSERT_EQUAL (MIN_FLOAT_STR, str); +#endif + + CPPUNIT_ASSERT (string_to_float (str, val)); + CPPUNIT_ASSERT_DOUBLES_EQUAL ( + numeric_limits::min (), val, numeric_limits::epsilon ()); + + // test the string_to/to_string templates + float max = numeric_limits::max (); + CPPUNIT_ASSERT_EQUAL (max, string_to(to_string (max))); + + float min = numeric_limits::min (); + CPPUNIT_ASSERT_EQUAL (min, string_to(to_string (min))); + +// check that parsing the windows float string representation with the +// difference in the exponent part parses correctly on other platforms +// and vice versa +#ifdef PLATFORM_WINDOWS + CPPUNIT_ASSERT (string_to_float (MAX_FLOAT_STR, val)); + CPPUNIT_ASSERT_DOUBLES_EQUAL ( + numeric_limits::max (), val, numeric_limits::epsilon ()); + + CPPUNIT_ASSERT (string_to_float (MIN_FLOAT_STR, val)); + CPPUNIT_ASSERT_DOUBLES_EQUAL ( + numeric_limits::min (), val, numeric_limits::epsilon ()); +#else + CPPUNIT_ASSERT (string_to_float (MAX_FLOAT_WIN, val)); + CPPUNIT_ASSERT_DOUBLES_EQUAL ( + numeric_limits::max (), val, numeric_limits::epsilon ()); + + CPPUNIT_ASSERT (string_to_float (MIN_FLOAT_WIN, val)); + CPPUNIT_ASSERT_DOUBLES_EQUAL ( + numeric_limits::min (), val, numeric_limits::epsilon ()); +#endif + + _test_infinity_conversion(); +} + +void +StringConvertTest::test_float_conversion () +{ + test_function_for_locales(&_test_float_conversion); +} + +static const std::string MAX_DOUBLE_STR ("1.7976931348623157e+308"); +static const std::string MIN_DOUBLE_STR ("2.2250738585072014e-308"); + +void +_test_double_conversion () +{ + string str; + CPPUNIT_ASSERT (double_to_string (numeric_limits::max (), str)); + CPPUNIT_ASSERT_EQUAL (MAX_DOUBLE_STR, str); + + double val = 0.0; + CPPUNIT_ASSERT (string_to_double (str, val)); + CPPUNIT_ASSERT_DOUBLES_EQUAL ( + numeric_limits::max (), val, numeric_limits::epsilon ()); + + CPPUNIT_ASSERT (double_to_string (numeric_limits::min (), str)); + CPPUNIT_ASSERT_EQUAL (MIN_DOUBLE_STR, str); + + CPPUNIT_ASSERT (string_to_double (str, val)); + CPPUNIT_ASSERT_DOUBLES_EQUAL ( + numeric_limits::min (), val, numeric_limits::epsilon ()); + + // test that overflow fails + CPPUNIT_ASSERT (!string_to_double ("1.8e+308", val)); + // test that underflow fails + CPPUNIT_ASSERT (!string_to_double ("2.4e-310", val)); + + // test the string_to/to_string templates + double max = numeric_limits::max (); + CPPUNIT_ASSERT_EQUAL (max, string_to(to_string (max))); + + double min = numeric_limits::min (); + CPPUNIT_ASSERT_EQUAL (min, string_to(to_string (min))); + + _test_infinity_conversion(); +} + +void +StringConvertTest::test_double_conversion () +{ + test_function_for_locales(&_test_double_conversion); +} + +// we have to use these as CPPUNIT_ASSERT_EQUAL won't accept char arrays +static const std::string BOOL_TRUE_STR ("yes"); +static const std::string BOOL_FALSE_STR ("no"); + +void +StringConvertTest::test_bool_conversion () +{ + string str; + + // check the normal case for true/false + CPPUNIT_ASSERT (bool_to_string (true, str)); + CPPUNIT_ASSERT_EQUAL (BOOL_TRUE_STR, str); + + bool val = false; + CPPUNIT_ASSERT (string_to_bool (str, val)); + CPPUNIT_ASSERT_EQUAL (val, true); + + CPPUNIT_ASSERT (bool_to_string (false, str)); + CPPUNIT_ASSERT_EQUAL (BOOL_FALSE_STR, str); + + val = true; + CPPUNIT_ASSERT (string_to_bool (str, val)); + CPPUNIT_ASSERT_EQUAL (val, false); + + // now check the other accepted values for true and false + // when converting from a string to bool + + val = false; + CPPUNIT_ASSERT (string_to_bool ("1", val)); + CPPUNIT_ASSERT_EQUAL (val, true); + + val = true; + CPPUNIT_ASSERT (string_to_bool ("0", val)); + CPPUNIT_ASSERT_EQUAL (val, false); + + val = false; + CPPUNIT_ASSERT (string_to_bool ("Y", val)); + CPPUNIT_ASSERT_EQUAL (val, true); + + val = true; + CPPUNIT_ASSERT (string_to_bool ("N", val)); + CPPUNIT_ASSERT_EQUAL (val, false); + + val = false; + CPPUNIT_ASSERT (string_to_bool ("y", val)); + CPPUNIT_ASSERT_EQUAL (val, true); + + val = true; + CPPUNIT_ASSERT (string_to_bool ("n", val)); + CPPUNIT_ASSERT_EQUAL (val, false); + + // test some junk + CPPUNIT_ASSERT (!string_to_bool ("01234someYNtrueyesno junk0123", val)); + + // test the string_to/to_string templates + CPPUNIT_ASSERT_EQUAL (true, string_to(to_string (true))); + + CPPUNIT_ASSERT_EQUAL (false, string_to(to_string (false))); +} + +namespace { + +bool +check_int_convert () +{ + int32_t num = g_random_int (); + return (num == string_to(to_string (num))); +} + +bool +check_float_convert () +{ + float num = (float)g_random_double (); + return (num == string_to(to_string (num))); +} + +bool +check_double_convert () +{ + double num = g_random_double (); + return (num == string_to(to_string (num))); +} + +static const int s_iter_count = 500000; + +void* +check_int_convert_thread(void*) +{ + for (int n = 0; n < s_iter_count; n++) { + assert (check_int_convert ()); + } + return NULL; +} + +void* +check_float_convert_thread(void*) +{ + for (int n = 0; n < s_iter_count; n++) { + assert (check_float_convert ()); + } + return NULL; +} + +void* +check_double_convert_thread(void*) +{ + for (int n = 0; n < s_iter_count; n++) { + assert (check_double_convert ()); + } + return NULL; +} + +static const double s_test_double = 31459.265359; + +bool +check_fr_printf () +{ + char buf[32]; + snprintf (buf, sizeof(buf), "%.12g", s_test_double); + bool found = (strchr (buf, ',') != NULL); + return found; +} + +void* +check_fr_printf_thread (void*) +{ + for (int n = 0; n < s_iter_count; n++) { + assert (check_fr_printf ()); + } + + return NULL; +} + +} // anon namespace + +// Perform the test in the French locale as the format for decimals is +// different and a comma is used as a decimal point. Test that this has no +// impact on the string conversions which are expected to be the same as in the +// C locale. +void +StringConvertTest::test_convert_thread_safety () +{ + LocaleGuard guard (get_test_locales().front()); + + CPPUNIT_ASSERT (check_int_convert ()); + CPPUNIT_ASSERT (check_float_convert ()); + CPPUNIT_ASSERT (check_double_convert ()); + CPPUNIT_ASSERT (check_fr_printf ()); + + pthread_t convert_int_thread; + pthread_t convert_float_thread; + pthread_t convert_double_thread; + pthread_t fr_printf_thread; + + CPPUNIT_ASSERT ( + pthread_create ( + &convert_int_thread, NULL, check_int_convert_thread, NULL) == 0); + CPPUNIT_ASSERT ( + pthread_create ( + &convert_float_thread, NULL, check_float_convert_thread, NULL) == 0); + CPPUNIT_ASSERT ( + pthread_create ( + &convert_double_thread, NULL, check_double_convert_thread, NULL) == 0); + CPPUNIT_ASSERT ( + pthread_create (&fr_printf_thread, NULL, check_fr_printf_thread, NULL) == + 0); + + void* return_value; + + CPPUNIT_ASSERT (pthread_join (convert_int_thread, &return_value) == 0); + CPPUNIT_ASSERT (pthread_join (convert_float_thread, &return_value) == 0); + CPPUNIT_ASSERT (pthread_join (convert_double_thread, &return_value) == 0); + CPPUNIT_ASSERT (pthread_join (fr_printf_thread, &return_value) == 0); +} diff --git a/libs/pbd/test/string_convert_test.h b/libs/pbd/test/string_convert_test.h new file mode 100644 index 0000000000..82ccb26af3 --- /dev/null +++ b/libs/pbd/test/string_convert_test.h @@ -0,0 +1,30 @@ +#include +#include + +class StringConvertTest : public CppUnit::TestFixture +{ + CPPUNIT_TEST_SUITE (StringConvertTest); + CPPUNIT_TEST (test_int16_conversion); + CPPUNIT_TEST (test_uint16_conversion); + CPPUNIT_TEST (test_int32_conversion); + CPPUNIT_TEST (test_uint32_conversion); + CPPUNIT_TEST (test_int64_conversion); + CPPUNIT_TEST (test_uint64_conversion); + CPPUNIT_TEST (test_float_conversion); + CPPUNIT_TEST (test_double_conversion); + CPPUNIT_TEST (test_bool_conversion); + CPPUNIT_TEST (test_convert_thread_safety); + CPPUNIT_TEST_SUITE_END (); + +public: + void test_int16_conversion (); + void test_uint16_conversion (); + void test_int32_conversion (); + void test_uint32_conversion (); + void test_int64_conversion (); + void test_uint64_conversion (); + void test_float_conversion (); + void test_double_conversion (); + void test_bool_conversion (); + void test_convert_thread_safety (); +}; -- cgit v1.2.3