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:
Tim Mayberry
2015-08-03 12:32:31 +10:00
parent 78b82b7ff2
commit c634daef6a
5 changed files with 1479 additions and 0 deletions

View 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