mirror of
https://github.com/Ardour/ardour.git
synced 2025-12-07 07:14:56 +01:00
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<bool> as this seems to be the convention used throughout libardour which will allow using string_to<bool> in those cases. Add accepted bool string values from PBD::string_is_affirmative to PBD::string_to<bool> 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.
This commit is contained in:
parent
78b82b7ff2
commit
c634daef6a
5 changed files with 1479 additions and 0 deletions
430
libs/pbd/pbd/string_convert.h
Normal file
430
libs/pbd/pbd/string_convert.h
Normal file
|
|
@ -0,0 +1,430 @@
|
|||
/*
|
||||
Copyright (C) 2015 Tim Mayberry
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#ifndef PBD_STRING_CONVERT_H
|
||||
#define PBD_STRING_CONVERT_H
|
||||
|
||||
#include <string>
|
||||
#include <stdint.h>
|
||||
|
||||
#include "pbd/libpbd_visibility.h"
|
||||
|
||||
/**
|
||||
* Locale independent and thread-safe string conversion utility functions.
|
||||
*
|
||||
* All conversions are done as if they were performed in the C locale without
|
||||
* actually changing the current locale.
|
||||
*/
|
||||
namespace PBD {
|
||||
|
||||
LIBPBD_API bool bool_to_string (bool val, std::string& str);
|
||||
|
||||
LIBPBD_API bool int16_to_string (int16_t val, std::string& str);
|
||||
|
||||
LIBPBD_API bool uint16_to_string (uint16_t val, std::string& str);
|
||||
|
||||
LIBPBD_API bool int32_to_string (int32_t val, std::string& str);
|
||||
|
||||
LIBPBD_API bool uint32_to_string (uint32_t val, std::string& str);
|
||||
|
||||
LIBPBD_API bool int64_to_string (int64_t val, std::string& str);
|
||||
|
||||
LIBPBD_API bool uint64_to_string (uint64_t val, std::string& str);
|
||||
|
||||
LIBPBD_API bool float_to_string (float val, std::string& str);
|
||||
|
||||
LIBPBD_API bool double_to_string (double val, std::string& str);
|
||||
|
||||
LIBPBD_API bool string_to_bool (const std::string& str, bool& val);
|
||||
|
||||
LIBPBD_API bool string_to_int16 (const std::string& str, int16_t& val);
|
||||
|
||||
LIBPBD_API bool string_to_uint16 (const std::string& str, uint16_t& val);
|
||||
|
||||
LIBPBD_API bool string_to_int32 (const std::string& str, int32_t& val);
|
||||
|
||||
LIBPBD_API bool string_to_uint32 (const std::string& str, uint32_t& val);
|
||||
|
||||
LIBPBD_API bool string_to_int64 (const std::string& str, int64_t& val);
|
||||
|
||||
LIBPBD_API bool string_to_uint64 (const std::string& str, uint64_t& val);
|
||||
|
||||
LIBPBD_API bool string_to_float (const std::string& str, float& val);
|
||||
|
||||
LIBPBD_API bool string_to_double (const std::string& str, double& val);
|
||||
|
||||
template <class T>
|
||||
inline bool to_string (T val, std::string& str)
|
||||
{
|
||||
// This will cause a compile time error if this function is ever
|
||||
// instantiated, which is useful to catch unintended conversions
|
||||
typename T::TO_STRING_TEMPLATE_NOT_DEFINED_FOR_THIS_TYPE invalid_type;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool to_string (bool val, std::string& str)
|
||||
{
|
||||
return bool_to_string (val, str);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool to_string (int8_t val, std::string& str)
|
||||
{
|
||||
return int16_to_string (val, str);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool to_string (uint8_t val, std::string& str)
|
||||
{
|
||||
return uint16_to_string (val, str);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool to_string (int16_t val, std::string& str)
|
||||
{
|
||||
return int16_to_string (val, str);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool to_string (uint16_t val, std::string& str)
|
||||
{
|
||||
return uint16_to_string (val, str);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool to_string (int32_t val, std::string& str)
|
||||
{
|
||||
return int32_to_string (val, str);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool to_string (uint32_t val, std::string& str)
|
||||
{
|
||||
return uint32_to_string (val, str);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool to_string (int64_t val, std::string& str)
|
||||
{
|
||||
return int64_to_string (val, str);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool to_string (uint64_t val, std::string& str)
|
||||
{
|
||||
return uint64_to_string (val, str);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool to_string (float val, std::string& str)
|
||||
{
|
||||
return float_to_string (val, str);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool to_string (double val, std::string& str)
|
||||
{
|
||||
return double_to_string (val, str);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool string_to (const std::string& str, T& val)
|
||||
{
|
||||
// This will cause a compile time error if this function is ever
|
||||
// instantiated, which is useful to catch unintended conversions
|
||||
typename T::TO_STRING_TEMPLATE_NOT_DEFINED_FOR_THIS_TYPE invalid_type;
|
||||
return false;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool string_to (const std::string& str, bool& val)
|
||||
{
|
||||
return string_to_bool (str, val);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool string_to (const std::string& str, int8_t& val)
|
||||
{
|
||||
int16_t tmp = val;
|
||||
bool success = string_to_int16 (str, tmp);
|
||||
if (!success) return false;
|
||||
val = tmp;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool string_to (const std::string& str, uint8_t& val)
|
||||
{
|
||||
uint16_t tmp = val;
|
||||
bool success = string_to_uint16 (str, tmp);
|
||||
if (!success) return false;
|
||||
val = tmp;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool string_to (const std::string& str, int16_t& val)
|
||||
{
|
||||
return string_to_int16 (str, val);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool string_to (const std::string& str, uint16_t& val)
|
||||
{
|
||||
return string_to_uint16 (str, val);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool string_to (const std::string& str, int32_t& val)
|
||||
{
|
||||
return string_to_int32 (str, val);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool string_to (const std::string& str, uint32_t& val)
|
||||
{
|
||||
return string_to_uint32 (str, val);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool string_to (const std::string& str, int64_t& val)
|
||||
{
|
||||
return string_to_int64 (str, val);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool string_to (const std::string& str, uint64_t& val)
|
||||
{
|
||||
return string_to_uint64 (str, val);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool string_to (const std::string& str, float& val)
|
||||
{
|
||||
return string_to_float (str, val);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline bool string_to (const std::string& str, double& val)
|
||||
{
|
||||
return string_to_double (str, val);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////
|
||||
// Variation that disregards conversion errors
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
template <class T>
|
||||
inline std::string to_string (T val)
|
||||
{
|
||||
// This will cause a compile time error if this function is ever
|
||||
// instantiated, which is useful to catch unintended conversions
|
||||
typename T::TO_STRING_TEMPLATE_NOT_DEFINED_FOR_THIS_TYPE invalid_type;
|
||||
return std::string();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::string to_string (bool val)
|
||||
{
|
||||
std::string tmp;
|
||||
bool_to_string (val, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::string to_string (int8_t val)
|
||||
{
|
||||
std::string tmp;
|
||||
int16_to_string (val, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::string to_string (uint8_t val)
|
||||
{
|
||||
std::string tmp;
|
||||
uint16_to_string (val, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::string to_string (int16_t val)
|
||||
{
|
||||
std::string tmp;
|
||||
int16_to_string (val, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::string to_string (uint16_t val)
|
||||
{
|
||||
std::string tmp;
|
||||
uint16_to_string (val, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::string to_string (int32_t val)
|
||||
{
|
||||
std::string tmp;
|
||||
int32_to_string (val, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::string to_string (uint32_t val)
|
||||
{
|
||||
std::string tmp;
|
||||
uint32_to_string (val, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::string to_string (int64_t val)
|
||||
{
|
||||
std::string tmp;
|
||||
int64_to_string (val, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::string to_string (uint64_t val)
|
||||
{
|
||||
std::string tmp;
|
||||
uint64_to_string (val, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::string to_string (float val)
|
||||
{
|
||||
std::string tmp;
|
||||
float_to_string (val, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline std::string to_string (double val)
|
||||
{
|
||||
std::string tmp;
|
||||
double_to_string (val, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline T string_to (const std::string& str)
|
||||
{
|
||||
// This will cause a compile time error if this function is ever
|
||||
// instantiated, which is useful to catch unintended conversions
|
||||
typename T::STRING_TO_TEMPLATE_NOT_DEFINED_FOR_THIS_TYPE invalid_type;
|
||||
return T();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline bool string_to (const std::string& str)
|
||||
{
|
||||
bool tmp;
|
||||
string_to_bool (str, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline int8_t string_to (const std::string& str)
|
||||
{
|
||||
int16_t tmp;
|
||||
string_to_int16 (str, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint8_t string_to (const std::string& str)
|
||||
{
|
||||
uint16_t tmp;
|
||||
string_to_uint16 (str, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline int16_t string_to (const std::string& str)
|
||||
{
|
||||
int16_t tmp;
|
||||
string_to_int16 (str, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint16_t string_to (const std::string& str)
|
||||
{
|
||||
uint16_t tmp;
|
||||
string_to_uint16 (str, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline int32_t string_to (const std::string& str)
|
||||
{
|
||||
int32_t tmp;
|
||||
string_to_int32 (str, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint32_t string_to (const std::string& str)
|
||||
{
|
||||
uint32_t tmp;
|
||||
string_to_uint32 (str, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline int64_t string_to (const std::string& str)
|
||||
{
|
||||
int64_t tmp;
|
||||
string_to_int64 (str, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline uint64_t string_to (const std::string& str)
|
||||
{
|
||||
uint64_t tmp;
|
||||
string_to_uint64 (str, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline float string_to (const std::string& str)
|
||||
{
|
||||
float tmp;
|
||||
string_to_float (str, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
template <>
|
||||
inline double string_to (const std::string& str)
|
||||
{
|
||||
double tmp;
|
||||
string_to_double (str, tmp);
|
||||
return tmp;
|
||||
}
|
||||
|
||||
} // namespace PBD
|
||||
|
||||
#endif // PBD_STRING_CONVERT_H
|
||||
366
libs/pbd/string_convert.cc
Normal file
366
libs/pbd/string_convert.cc
Normal file
|
|
@ -0,0 +1,366 @@
|
|||
/*
|
||||
Copyright (C) 2015 Tim Mayberry
|
||||
|
||||
This program is free software; you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||||
|
||||
*/
|
||||
|
||||
#include "pbd/string_convert.h"
|
||||
|
||||
#ifndef __STDC_FORMAT_MACROS
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#endif
|
||||
#include <inttypes.h>
|
||||
#include <cerrno>
|
||||
#include <cstdio>
|
||||
#include <limits>
|
||||
|
||||
#include <glib.h>
|
||||
#include <glib/gprintf.h>
|
||||
|
||||
#include "pbd/compose.h"
|
||||
#include "pbd/debug.h"
|
||||
#include "pbd/i18n.h"
|
||||
|
||||
#define DEBUG_SCONVERT(msg) DEBUG_TRACE (PBD::DEBUG::StringConvert, string_compose ("%1: %2\n", __LINE__, msg));
|
||||
|
||||
#define CONVERT_BUF_SIZE 32
|
||||
|
||||
namespace PBD {
|
||||
|
||||
bool string_to_bool (const std::string& str, bool& val)
|
||||
{
|
||||
if (str.empty ()) {
|
||||
return false;
|
||||
|
||||
} else if (str == X_("1")) {
|
||||
val = true;
|
||||
return true;
|
||||
|
||||
} else if (str == X_("0")) {
|
||||
val = false;
|
||||
return true;
|
||||
|
||||
} else if (str == X_("y")) {
|
||||
val = true;
|
||||
return true;
|
||||
|
||||
} else if (str == X_("n")) {
|
||||
val = false;
|
||||
return true;
|
||||
|
||||
} else if (g_ascii_strncasecmp (str.c_str(), X_("yes"), str.length()) == 0) {
|
||||
val = true;
|
||||
return true;
|
||||
|
||||
} else if (g_ascii_strncasecmp (str.c_str(), X_("no"), str.length()) == 0) {
|
||||
val = false;
|
||||
return true;
|
||||
|
||||
} else if (g_ascii_strncasecmp (str.c_str(), X_("true"), str.length()) == 0) {
|
||||
val = true;
|
||||
return true;
|
||||
|
||||
} else if (g_ascii_strncasecmp (str.c_str(), X_("false"), str.length()) == 0) {
|
||||
val = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
DEBUG_SCONVERT (
|
||||
string_compose ("string_to_bool conversion failed for %1", str));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool string_to_int16 (const std::string& str, int16_t& val)
|
||||
{
|
||||
if (sscanf (str.c_str (), "%" SCNi16, &val) != 1) {
|
||||
DEBUG_SCONVERT (
|
||||
string_compose ("string_to_int16 conversion failed for %1", str));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string_to_uint16 (const std::string& str, uint16_t& val)
|
||||
{
|
||||
if (sscanf (str.c_str (), "%" SCNu16, &val) != 1) {
|
||||
DEBUG_SCONVERT (
|
||||
string_compose ("string_to_uint16 conversion failed for %1", str));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string_to_int32 (const std::string& str, int32_t& val)
|
||||
{
|
||||
if (sscanf (str.c_str (), "%" SCNi32, &val) != 1) {
|
||||
DEBUG_SCONVERT (
|
||||
string_compose ("string_to_int32 conversion failed for %1", str));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string_to_uint32 (const std::string& str, uint32_t& val)
|
||||
{
|
||||
if (sscanf (str.c_str (), "%" SCNu32, &val) != 1) {
|
||||
DEBUG_SCONVERT (
|
||||
string_compose ("string_to_uint32 conversion failed for %1", str));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string_to_int64 (const std::string& str, int64_t& val)
|
||||
{
|
||||
if (sscanf (str.c_str (), "%" SCNi64, &val) != 1) {
|
||||
DEBUG_SCONVERT (
|
||||
string_compose ("string_to_int64 conversion failed for %1", str));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string_to_uint64 (const std::string& str, uint64_t& val)
|
||||
{
|
||||
if (sscanf (str.c_str (), "%" SCNu64, &val) != 1) {
|
||||
DEBUG_SCONVERT (
|
||||
string_compose ("string_to_uint64 conversion failed for %1", str));
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class FloatType>
|
||||
static bool
|
||||
_string_to_infinity (const std::string& str, FloatType& val)
|
||||
{
|
||||
if (!g_ascii_strncasecmp (str.c_str (), X_ ("inf"), str.length ()) ||
|
||||
!g_ascii_strncasecmp (str.c_str (), X_ ("+inf"), str.length ()) ||
|
||||
!g_ascii_strncasecmp (str.c_str (), X_ ("INFINITY"), str.length ()) ||
|
||||
!g_ascii_strncasecmp (str.c_str (), X_ ("+INFINITY"), str.length ())) {
|
||||
val = std::numeric_limits<FloatType>::infinity ();
|
||||
return true;
|
||||
} else if (!g_ascii_strncasecmp (str.c_str (), X_ ("-inf"), str.length ()) ||
|
||||
!g_ascii_strncasecmp (str.c_str (), X_ ("-INFINITY"), str.length ())) {
|
||||
val = -std::numeric_limits<FloatType>::infinity ();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
_string_to_double (const std::string& str, double& val)
|
||||
{
|
||||
val = g_ascii_strtod (str.c_str (), NULL);
|
||||
|
||||
// It is possible that the conversion was successful and another thread
|
||||
// has set errno meanwhile but as most conversions are currently not
|
||||
// checked for error conditions this is better than nothing.
|
||||
if (errno == ERANGE) {
|
||||
DEBUG_SCONVERT (string_compose ("string_to_double possible conversion failure for %1", str));
|
||||
// There should not be any conversion failures as we control the string
|
||||
// contents so returning false here should not have any impact...
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool string_to_float (const std::string& str, float& val)
|
||||
{
|
||||
double tmp;
|
||||
if (_string_to_double (str, tmp)) {
|
||||
val = (float)tmp;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_string_to_infinity (str, val)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool string_to_double (const std::string& str, double& val)
|
||||
{
|
||||
if (_string_to_double (str, val)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_string_to_infinity (str, val)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool bool_to_string (bool val, std::string& str)
|
||||
{
|
||||
if (val) {
|
||||
str = X_("yes");
|
||||
} else {
|
||||
str = X_("no");
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool int16_to_string (int16_t val, std::string& str)
|
||||
{
|
||||
char buffer[CONVERT_BUF_SIZE];
|
||||
|
||||
int retval = g_snprintf (buffer, sizeof(buffer), "%" PRIi16, val);
|
||||
|
||||
if (retval <= 0 || retval >= (int)sizeof(buffer)) {
|
||||
DEBUG_SCONVERT (
|
||||
string_compose ("int16_to_string conversion failure for %1", val));
|
||||
return false;
|
||||
}
|
||||
str = buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uint16_to_string (uint16_t val, std::string& str)
|
||||
{
|
||||
char buffer[CONVERT_BUF_SIZE];
|
||||
|
||||
int retval = g_snprintf (buffer, sizeof(buffer), "%" PRIu16, val);
|
||||
|
||||
if (retval <= 0 || retval >= (int)sizeof(buffer)) {
|
||||
DEBUG_SCONVERT (
|
||||
string_compose ("uint16_to_string conversion failure for %1", val));
|
||||
return false;
|
||||
}
|
||||
str = buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool int32_to_string (int32_t val, std::string& str)
|
||||
{
|
||||
char buffer[CONVERT_BUF_SIZE];
|
||||
|
||||
int retval = g_snprintf (buffer, sizeof(buffer), "%" PRIi32, val);
|
||||
|
||||
if (retval <= 0 || retval >= (int)sizeof(buffer)) {
|
||||
DEBUG_SCONVERT (
|
||||
string_compose ("int32_to_string conversion failure for %1", val));
|
||||
return false;
|
||||
}
|
||||
str = buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uint32_to_string (uint32_t val, std::string& str)
|
||||
{
|
||||
char buffer[CONVERT_BUF_SIZE];
|
||||
|
||||
int retval = g_snprintf (buffer, sizeof(buffer), "%" PRIu32, val);
|
||||
|
||||
if (retval <= 0 || retval >= (int)sizeof(buffer)) {
|
||||
DEBUG_SCONVERT (
|
||||
string_compose ("uint32_to_string conversion failure for %1", val));
|
||||
return false;
|
||||
}
|
||||
str = buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool int64_to_string (int64_t val, std::string& str)
|
||||
{
|
||||
char buffer[CONVERT_BUF_SIZE];
|
||||
|
||||
int retval = g_snprintf (buffer, sizeof(buffer), "%" PRIi64, val);
|
||||
|
||||
if (retval <= 0 || retval >= (int)sizeof(buffer)) {
|
||||
DEBUG_SCONVERT (
|
||||
string_compose ("int64_to_string conversion failure for %1", val));
|
||||
return false;
|
||||
}
|
||||
str = buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool uint64_to_string (uint64_t val, std::string& str)
|
||||
{
|
||||
char buffer[CONVERT_BUF_SIZE];
|
||||
|
||||
int retval = g_snprintf (buffer, sizeof(buffer), "%" PRIu64, val);
|
||||
|
||||
if (retval <= 0 || retval >= (int)sizeof(buffer)) {
|
||||
DEBUG_SCONVERT (
|
||||
string_compose ("uint64_to_string conversion failure for %1", val));
|
||||
return false;
|
||||
}
|
||||
str = buffer;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class FloatType>
|
||||
static bool
|
||||
_infinity_to_string (FloatType val, std::string& str)
|
||||
{
|
||||
if (val == std::numeric_limits<FloatType>::infinity ()) {
|
||||
str = "inf";
|
||||
return true;
|
||||
} else if (val == -std::numeric_limits<FloatType>::infinity ()) {
|
||||
str = "-inf";
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
_double_to_string (double val, std::string& str)
|
||||
{
|
||||
char buffer[G_ASCII_DTOSTR_BUF_SIZE];
|
||||
|
||||
char* d_cstr = g_ascii_dtostr (buffer, sizeof(buffer), val);
|
||||
|
||||
if (d_cstr == NULL) {
|
||||
return false;
|
||||
}
|
||||
str = d_cstr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool float_to_string (float val, std::string& str)
|
||||
{
|
||||
if (_infinity_to_string (val, str)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_double_to_string (val, str)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
DEBUG_SCONVERT (string_compose ("float_to_string conversion failure for %1", val));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool double_to_string (double val, std::string& str)
|
||||
{
|
||||
if (_infinity_to_string (val, str)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (_double_to_string (val, str)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
DEBUG_SCONVERT (string_compose ("double_to_string conversion failure for %1", val));
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace PBD
|
||||
651
libs/pbd/test/string_convert_test.cc
Normal file
651
libs/pbd/test/string_convert_test.cc
Normal file
|
|
@ -0,0 +1,651 @@
|
|||
#include "string_convert_test.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <limits>
|
||||
#include <cassert>
|
||||
|
||||
#include <pthread.h>
|
||||
|
||||
#include <glib.h>
|
||||
|
||||
#include "pbd/string_convert.h"
|
||||
|
||||
using namespace PBD;
|
||||
using namespace std;
|
||||
|
||||
CPPUNIT_TEST_SUITE_REGISTRATION (StringConvertTest);
|
||||
|
||||
static std::vector<std::string> get_test_locales ()
|
||||
{
|
||||
std::vector<std::string> 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<std::string> locales = get_test_locales();
|
||||
|
||||
for (std::vector<std::string>::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<int16_t>::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<int16_t>::max (), val);
|
||||
|
||||
CPPUNIT_ASSERT (int16_to_string (numeric_limits<int16_t>::min (), str));
|
||||
CPPUNIT_ASSERT_EQUAL (MIN_INT16_STR, str);
|
||||
|
||||
CPPUNIT_ASSERT (string_to_int16 (str, val));
|
||||
CPPUNIT_ASSERT_EQUAL (numeric_limits<int16_t>::min (), val);
|
||||
|
||||
// test the string_to/to_string templates
|
||||
int16_t max = numeric_limits<int16_t>::max ();
|
||||
CPPUNIT_ASSERT_EQUAL (max, string_to<int16_t>(to_string (max)));
|
||||
|
||||
int16_t min = numeric_limits<int16_t>::min ();
|
||||
CPPUNIT_ASSERT_EQUAL (min, string_to<int16_t>(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<uint16_t>::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<uint16_t>::max (), val);
|
||||
|
||||
CPPUNIT_ASSERT (uint16_to_string (numeric_limits<uint16_t>::min (), str));
|
||||
CPPUNIT_ASSERT_EQUAL (MIN_UINT16_STR, str);
|
||||
|
||||
CPPUNIT_ASSERT (string_to_uint16 (str, val));
|
||||
CPPUNIT_ASSERT_EQUAL (numeric_limits<uint16_t>::min (), val);
|
||||
|
||||
// test the string_to/to_string templates
|
||||
uint16_t max = numeric_limits<uint16_t>::max ();
|
||||
CPPUNIT_ASSERT_EQUAL (max, string_to<uint16_t>(to_string (max)));
|
||||
|
||||
uint16_t min = numeric_limits<uint16_t>::min ();
|
||||
CPPUNIT_ASSERT_EQUAL (min, string_to<uint16_t>(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<int32_t>::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<int32_t>::max (), val);
|
||||
|
||||
CPPUNIT_ASSERT (int32_to_string (numeric_limits<int32_t>::min (), str));
|
||||
CPPUNIT_ASSERT_EQUAL (MIN_INT32_STR, str);
|
||||
|
||||
CPPUNIT_ASSERT (string_to_int32 (str, val));
|
||||
CPPUNIT_ASSERT_EQUAL (numeric_limits<int32_t>::min (), val);
|
||||
|
||||
// test the string_to/to_string templates
|
||||
int32_t max = numeric_limits<int32_t>::max ();
|
||||
CPPUNIT_ASSERT_EQUAL (max, string_to<int32_t>(to_string (max)));
|
||||
|
||||
int32_t min = numeric_limits<int32_t>::min ();
|
||||
CPPUNIT_ASSERT_EQUAL (min, string_to<int32_t>(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<uint32_t>::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<uint32_t>::max (), val);
|
||||
|
||||
CPPUNIT_ASSERT (uint32_to_string (numeric_limits<uint32_t>::min (), str));
|
||||
CPPUNIT_ASSERT_EQUAL (MIN_UINT32_STR, str);
|
||||
|
||||
CPPUNIT_ASSERT (string_to_uint32 (str, val));
|
||||
CPPUNIT_ASSERT_EQUAL (numeric_limits<uint32_t>::min (), val);
|
||||
|
||||
// test the string_to/to_string templates
|
||||
uint32_t max = numeric_limits<uint32_t>::max ();
|
||||
CPPUNIT_ASSERT_EQUAL (max, string_to<uint32_t>(to_string (max)));
|
||||
|
||||
uint32_t min = numeric_limits<uint32_t>::min ();
|
||||
CPPUNIT_ASSERT_EQUAL (min, string_to<uint32_t>(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<int64_t>::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<int64_t>::max (), val);
|
||||
|
||||
CPPUNIT_ASSERT (int64_to_string (numeric_limits<int64_t>::min (), str));
|
||||
CPPUNIT_ASSERT_EQUAL (MIN_INT64_STR, str);
|
||||
|
||||
CPPUNIT_ASSERT (string_to_int64 (str, val));
|
||||
CPPUNIT_ASSERT_EQUAL (numeric_limits<int64_t>::min (), val);
|
||||
|
||||
// test the string_to/to_string templates
|
||||
int64_t max = numeric_limits<int64_t>::max ();
|
||||
CPPUNIT_ASSERT_EQUAL (max, string_to<int64_t>(to_string (max)));
|
||||
|
||||
int64_t min = numeric_limits<int64_t>::min ();
|
||||
CPPUNIT_ASSERT_EQUAL (min, string_to<int64_t>(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<uint64_t>::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<uint64_t>::max (), val);
|
||||
|
||||
CPPUNIT_ASSERT (uint64_to_string (numeric_limits<uint64_t>::min (), str));
|
||||
CPPUNIT_ASSERT_EQUAL (MIN_UINT64_STR, str);
|
||||
|
||||
CPPUNIT_ASSERT (string_to_uint64 (str, val));
|
||||
CPPUNIT_ASSERT_EQUAL (numeric_limits<uint64_t>::min (), val);
|
||||
|
||||
// test the string_to/to_string templates
|
||||
uint64_t max = numeric_limits<uint64_t>::max ();
|
||||
CPPUNIT_ASSERT_EQUAL (max, string_to<uint64_t>(to_string (max)));
|
||||
|
||||
uint64_t min = numeric_limits<uint64_t>::min ();
|
||||
CPPUNIT_ASSERT_EQUAL (min, string_to<uint64_t>(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<std::string>
|
||||
_pos_infinity_strings ()
|
||||
{
|
||||
std::vector<std::string> 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<std::string>
|
||||
_neg_infinity_strings ()
|
||||
{
|
||||
std::vector<std::string> 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 <class FloatType>
|
||||
void
|
||||
_test_infinity_conversion ()
|
||||
{
|
||||
const FloatType pos_infinity = numeric_limits<FloatType>::infinity ();
|
||||
const FloatType neg_infinity = -numeric_limits<FloatType>::infinity ();
|
||||
|
||||
// Check float -> string
|
||||
string str;
|
||||
CPPUNIT_ASSERT (to_string<FloatType> (pos_infinity, str));
|
||||
CPPUNIT_ASSERT_EQUAL (POS_INF_STR, str);
|
||||
|
||||
CPPUNIT_ASSERT (to_string<FloatType> (neg_infinity, str));
|
||||
CPPUNIT_ASSERT_EQUAL (NEG_INF_STR, str);
|
||||
|
||||
// Check string -> float for all supported string representations of "INFINITY"
|
||||
std::vector<std::string> pos_inf_strings = _pos_infinity_strings ();
|
||||
|
||||
for (std::vector<std::string>::const_iterator i = pos_inf_strings.begin ();
|
||||
i != pos_inf_strings.end (); ++i) {
|
||||
FloatType pos_infinity_arg;
|
||||
CPPUNIT_ASSERT (string_to<FloatType> (*i, pos_infinity_arg));
|
||||
CPPUNIT_ASSERT_EQUAL (pos_infinity_arg, pos_infinity);
|
||||
}
|
||||
|
||||
// Check string -> float for all supported string representations of "-INFINITY"
|
||||
std::vector<std::string> neg_inf_strings = _neg_infinity_strings ();
|
||||
|
||||
for (std::vector<std::string>::const_iterator i = neg_inf_strings.begin ();
|
||||
i != neg_inf_strings.end (); ++i) {
|
||||
FloatType neg_infinity_arg;
|
||||
CPPUNIT_ASSERT (string_to<FloatType> (*i, neg_infinity_arg));
|
||||
CPPUNIT_ASSERT_EQUAL (neg_infinity_arg, neg_infinity);
|
||||
}
|
||||
|
||||
// Check round-trip equality
|
||||
CPPUNIT_ASSERT_EQUAL (pos_infinity, string_to<FloatType> (to_string<FloatType> (pos_infinity)));
|
||||
CPPUNIT_ASSERT_EQUAL (neg_infinity, string_to<FloatType> (to_string<FloatType> (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<float>::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<float>::max (), val, numeric_limits<float>::epsilon ());
|
||||
|
||||
CPPUNIT_ASSERT (float_to_string (numeric_limits<float>::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<float>::min (), val, numeric_limits<float>::epsilon ());
|
||||
|
||||
// test the string_to/to_string templates
|
||||
float max = numeric_limits<float>::max ();
|
||||
CPPUNIT_ASSERT_EQUAL (max, string_to<float>(to_string<float> (max)));
|
||||
|
||||
float min = numeric_limits<float>::min ();
|
||||
CPPUNIT_ASSERT_EQUAL (min, string_to<float>(to_string<float> (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<float>::max (), val, numeric_limits<float>::epsilon ());
|
||||
|
||||
CPPUNIT_ASSERT (string_to_float (MIN_FLOAT_STR, val));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL (
|
||||
numeric_limits<float>::min (), val, numeric_limits<float>::epsilon ());
|
||||
#else
|
||||
CPPUNIT_ASSERT (string_to_float (MAX_FLOAT_WIN, val));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL (
|
||||
numeric_limits<float>::max (), val, numeric_limits<float>::epsilon ());
|
||||
|
||||
CPPUNIT_ASSERT (string_to_float (MIN_FLOAT_WIN, val));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL (
|
||||
numeric_limits<float>::min (), val, numeric_limits<float>::epsilon ());
|
||||
#endif
|
||||
|
||||
_test_infinity_conversion<float>();
|
||||
}
|
||||
|
||||
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<double>::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<double>::max (), val, numeric_limits<double>::epsilon ());
|
||||
|
||||
CPPUNIT_ASSERT (double_to_string (numeric_limits<double>::min (), str));
|
||||
CPPUNIT_ASSERT_EQUAL (MIN_DOUBLE_STR, str);
|
||||
|
||||
CPPUNIT_ASSERT (string_to_double (str, val));
|
||||
CPPUNIT_ASSERT_DOUBLES_EQUAL (
|
||||
numeric_limits<double>::min (), val, numeric_limits<double>::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<double>::max ();
|
||||
CPPUNIT_ASSERT_EQUAL (max, string_to<double>(to_string<double> (max)));
|
||||
|
||||
double min = numeric_limits<double>::min ();
|
||||
CPPUNIT_ASSERT_EQUAL (min, string_to<double>(to_string<double> (min)));
|
||||
|
||||
_test_infinity_conversion<double>();
|
||||
}
|
||||
|
||||
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<bool>(to_string (true)));
|
||||
|
||||
CPPUNIT_ASSERT_EQUAL (false, string_to<bool>(to_string (false)));
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
bool
|
||||
check_int_convert ()
|
||||
{
|
||||
int32_t num = g_random_int ();
|
||||
return (num == string_to<int32_t>(to_string (num)));
|
||||
}
|
||||
|
||||
bool
|
||||
check_float_convert ()
|
||||
{
|
||||
float num = (float)g_random_double ();
|
||||
return (num == string_to<float>(to_string<float> (num)));
|
||||
}
|
||||
|
||||
bool
|
||||
check_double_convert ()
|
||||
{
|
||||
double num = g_random_double ();
|
||||
return (num == string_to<double>(to_string<double> (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);
|
||||
}
|
||||
30
libs/pbd/test/string_convert_test.h
Normal file
30
libs/pbd/test/string_convert_test.h
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
#include <cppunit/TestFixture.h>
|
||||
#include <cppunit/extensions/HelperMacros.h>
|
||||
|
||||
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 ();
|
||||
};
|
||||
|
|
@ -73,6 +73,7 @@ libpbd_sources = [
|
|||
'stacktrace.cc',
|
||||
'stateful_diff_command.cc',
|
||||
'stateful.cc',
|
||||
'string_convert.cc',
|
||||
'strreplace.cc',
|
||||
'strsplit.cc',
|
||||
'system_exec.cc',
|
||||
|
|
@ -191,6 +192,7 @@ def build(bld):
|
|||
test/mutex_test.cc
|
||||
test/scalar_properties.cc
|
||||
test/signals_test.cc
|
||||
test/string_convert_test.cc
|
||||
test/convert_test.cc
|
||||
test/filesystem_test.cc
|
||||
test/natsort_test.cc
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue