summaryrefslogtreecommitdiff
path: root/libs/pbd/string_convert.cc
diff options
context:
space:
mode:
authorTim Mayberry <mojofunk@gmail.com>2015-08-03 12:32:31 +1000
committerTim Mayberry <mojofunk@gmail.com>2017-04-16 14:02:41 +1000
commitc634daef6a9de8df7245d4fb6fbbcdf0ad7ac7ff (patch)
treee21e96a8a76133570f756ac31c1fad705c92384a /libs/pbd/string_convert.cc
parent78b82b7ff2e28d53faae176776491404115ce02c (diff)
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.
Diffstat (limited to 'libs/pbd/string_convert.cc')
-rw-r--r--libs/pbd/string_convert.cc366
1 files changed, 366 insertions, 0 deletions
diff --git a/libs/pbd/string_convert.cc b/libs/pbd/string_convert.cc
new file mode 100644
index 0000000000..3a46b8a682
--- /dev/null
+++ b/libs/pbd/string_convert.cc
@@ -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