summaryrefslogtreecommitdiff
path: root/shared/string_tools.h
diff options
context:
space:
mode:
authorDaniel Wilhelm <daniel@wili.li>2014-04-18 17:12:17 +0200
committerDaniel Wilhelm <daniel@wili.li>2014-04-18 17:12:17 +0200
commitb654dbfa5f3e4a4d02f72023f7c5895635aa6396 (patch)
tree8c1dfe7f638c0fc7afc1d08bc2fc0fd0f8646e5e /shared/string_tools.h
parent3.17 (diff)
downloadFreeFileSync-b654dbfa5f3e4a4d02f72023f7c5895635aa6396.tar.gz
FreeFileSync-b654dbfa5f3e4a4d02f72023f7c5895635aa6396.tar.bz2
FreeFileSync-b654dbfa5f3e4a4d02f72023f7c5895635aa6396.zip
3.18
Diffstat (limited to 'shared/string_tools.h')
-rw-r--r--shared/string_tools.h577
1 files changed, 411 insertions, 166 deletions
diff --git a/shared/string_tools.h b/shared/string_tools.h
index 53365f71..d2e2c597 100644
--- a/shared/string_tools.h
+++ b/shared/string_tools.h
@@ -1,54 +1,80 @@
// **************************************************************************
-// * This file is part of the FreeFileSync project. It is distributed under *
-// * GNU General Public License: http://www.gnu.org/licenses/gpl.html *
-// * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) *
+// * This file is part of the zenXML project. It is distributed under the *
+// * Boost Software License, Version 1.0. See accompanying file *
+// * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt. *
+// * Copyright (C) 2011 ZenJu (zhnmju123 AT gmx.de) *
// **************************************************************************
-//
+
#ifndef STRING_TOOLS_HEADER_213458973046
#define STRING_TOOLS_HEADER_213458973046
#include <cstddef> //size_t
#include <cctype> //isspace
#include <cwctype> //iswspace
+#include <cwchar> //swprintf
+#include <cstdio> //sprintf
#include <algorithm>
#include <cassert>
#include <sstream>
#include <functional>
#include <vector>
+#include "loki/TypeManip.h"
+#include "loki/EmptyType.h"
#include "loki/TypeTraits.h"
+#include "assert_static.h"
+#ifdef _MSC_VER
+template <> struct Loki::IsCustomUnsignedInt<unsigned __int64> { enum { value = 1 }; };
+template <> struct Loki::IsCustomSignedInt <signed __int64> { enum { value = 1 }; };
+#endif
//enhance arbitray string class with useful non-member functions:
namespace zen
{
-template <class C> size_t cStringLength(const C* input); //strlen()
-template <class C> bool cStringIsWhiteSpace(C ch);
-template <class C> bool cStringIsDigit(C ch);
-
-template <class S> bool startsWith(const S& str, typename S::value_type prefix);
-template <class S> bool startsWith(const S& str, const typename S::value_type* prefix);
-template <class S, class T> bool startsWith(const S& str, const T& prefix, typename T::value_type dummy = 0); //SFINAE: T must be a "string"
-template <class S> bool endsWith(const S& str, typename S::value_type postfix);
-template <class S> bool endsWith(const S& str, const typename S::value_type* postfix);
-template <class S, class T> bool endsWith(const S& str, const T& postfix, typename T::value_type dummy = 0); //SFINAE: T must be a "string"
-
-template <class S> S afterLast (const S& str, typename S::value_type ch); //returns the whole string if ch not found
-template <class S> S beforeLast (const S& str, typename S::value_type ch); //returns empty string if ch not found
-template <class S> S afterFirst (const S& str, typename S::value_type ch); //returns empty string if ch not found
-template <class S> S beforeFirst(const S& str, typename S::value_type ch); //returns the whole string if ch not found
+template <class C> size_t cStringLength(const C* str); //strlen()
+template <class C> bool cStringIsWhiteSpace(C ch);
+template <class C> bool cStringIsDigit(C ch);
+
+//uniform access to string-like types: classes and character arrays
+/*
+strBegin():
+ std::wstring str(L"dummy");
+ char array[] = "dummy";
+ const wchar_t* iter = strBegin(str); //returns str.c_str()
+ const char* iter2 = strBegin(array); //returns array
+
+strLength():
+ strLength(str); //equals str.size()
+ strLength(array); //equals cStringLength(array)
+
+StringTraits<>:
+ StringTraits<std::wstring>::CharType //equals wchar_t
+ StringTraits<wchar_t[5]> ::CharType //equals wchar_t
+ StringTraits<const wchar_t*>::isStringLike; //equals "true"
+ StringTraits<const int*> ::isStringLike; //equals "false"
+ StringTraits<std::wstring>::isStringClass //equals "true"
+ StringTraits<wchar_t[5]> ::isStringClass //equals "false"
+*/
+
+template <class S, class T> bool startsWith(const S& str, const T& prefix); //both S and T can be strings or char/wchar_t arrays or simple char/wchar_t
+template <class S, class T> bool endsWith (const S& str, const T& postfix); //
+
+template <class S, class T> S afterLast (const S& str, const T& ch); //returns the whole string if ch not found
+template <class S, class T> S beforeLast (const S& str, const T& ch); //returns empty string if ch not found
+template <class S, class T> S afterFirst (const S& str, const T& ch); //returns empty string if ch not found
+template <class S, class T> S beforeFirst(const S& str, const T& ch); //returns the whole string if ch not found
template <class S, class T> std::vector<S> split(const S& str, const T& delimiter);
template <class S> void truncate(S& str, size_t newLen);
-template <class S> void replace(S& str, const typename S::value_type* old, const typename S::value_type* replacement, bool replaceAll);
+template <class S, class T, class U> void replace(S& str, const T& old, const U& replacement, bool replaceAll = true);
template <class S> void trim(S& str, bool fromLeft = true, bool fromRight = true);
-//formatted number conversion the C++ way
+//high-performance conversion from numbers to strings
template <class S, class Num> S toString(const Num& number);
template <class Num, class S> Num toNumber(const S& str);
-
-
-
+//string to string conversion: converst string-like type into compatible target string class
+template <class T, class S> T cvrtString(const S& str);
@@ -87,36 +113,31 @@ template <class Num, class S> Num toNumber(const S& str);
//---------------------- implementation ----------------------
-template <class C>
-inline
-size_t cStringLength(const C* input) //strlen()
+template <class C> inline
+size_t cStringLength(const C* str) //strlen()
{
- const C* iter = input;
- while (*iter != 0)
- ++iter;
- return iter - input;
+ assert_static((Loki::IsSameType<C, char>::value || Loki::IsSameType<C, wchar_t>::value));
+ size_t len = 0;
+ while (*str++ != 0)
+ ++len;
+ return len;
}
-template <>
-inline
+template <> inline
bool cStringIsWhiteSpace(char ch)
{
- const unsigned char usc = ch; //caveat 1: std::isspace() takes an int, but expects an unsigned char
- return usc < 128 && //caveat 2: some parts of UTF-8 chars are erroneously seen as whitespace, e.g. the a0 from "\xec\x8b\xa0" (MSVC)
- std::isspace(usc) != 0;
-}
-
-template <>
-inline
-bool cStringIsWhiteSpace(wchar_t ch)
-{
- return std::iswspace(ch) != 0; //some compilers (e.g. VC++ 6.0) return true for a call to isspace('\xEA'); but no issue with newer versions of MSVC
+ //caveat 1: std::isspace() takes an int, but expects an unsigned char
+ //caveat 2: some parts of UTF-8 chars are erroneously seen as whitespace, e.g. the a0 from "\xec\x8b\xa0" (MSVC)
+ return static_cast<unsigned char>(ch) < 128 &&
+ std::isspace(static_cast<unsigned char>(ch)) != 0;
}
+template <> inline bool cStringIsWhiteSpace(unsigned char ch) { return cStringIsWhiteSpace<char>(ch); }
+template <> inline bool cStringIsWhiteSpace(signed char ch) { return cStringIsWhiteSpace<char>(ch); }
+template <> inline bool cStringIsWhiteSpace(wchar_t ch) { return std::iswspace(ch) != 0; }
-template <>
-inline
+template <> inline
bool cStringIsDigit(char ch)
{
return std::isdigit(static_cast<unsigned char>(ch)) != 0; //caveat: takes an int, but expects an unsigned char
@@ -130,85 +151,179 @@ bool cStringIsDigit(wchar_t ch)
return std::iswdigit(ch) != 0;
}
-
-template <class S>
-inline
-bool startsWith(const S& str, typename S::value_type prefix)
+namespace implementation
{
- return str.length() != 0 && str.operator[](0) == prefix;
-}
+template <class T>
+struct UnArray { typedef T NonArrayType; };
+template <class T, int N>
+struct UnArray<T[N]> { typedef T NonArrayType; };
-template <class S>
-inline
-bool startsWith(const S& str, const typename S::value_type* prefix)
+template <class T>
+struct UnPointer { typedef T NonPtrType; };
+
+template <class T>
+struct UnPointer<T*> { typedef T NonPtrType; };
+
+template <class T>
+struct UnReference { typedef T NonRefType; };
+
+template <class T>
+struct UnReference<T&> { typedef T NonRefType; };
+
+
+template<typename T>
+class HasValueType
{
- const size_t pfLength = cStringLength(prefix);
- if (str.length() < pfLength)
- return false;
+ typedef char Yes[1];
+ typedef char No [2];
- return std::equal(str.c_str(), str.c_str() + pfLength,
- prefix);
-}
+ template <typename U> class HelperTp {};
+ //detect presence of a member type called value_type
+ template <class U> static Yes& hasMemberValueType(HelperTp<typename U::value_type>*);
+ template <class U> static No& hasMemberValueType(...);
-template <class S, class T>
-inline
-bool startsWith(const S& str, const T& prefix, typename T::value_type)
+public:
+ enum { Result = sizeof(hasMemberValueType<T>(NULL)) == sizeof(Yes)
+ };
+};
+
+
+template<typename T, bool isClassType>
+class HasStringMembers
{
- if (str.length() < prefix.length())
- return false;
+public:
+ enum { Result = false };
+};
- return std::equal(str.c_str(), str.c_str() + prefix.length(),
- prefix.c_str());
-}
+template<typename T>
+class HasStringMembers<T, true>
+{
+ typedef char Yes[1];
+ typedef char No [2];
+
+ //detect presence of member functions (without specific restriction on return type, within T or one of it's base classes)
+ template <typename U, U t> class HelperFn {};
+
+ struct Fallback
+ {
+ int c_str;
+ int length;
+ };
+
+ template <class U>
+ struct Helper2 : public U, public Fallback {}; //U must be a class-type!
+
+ //we don't know the exact declaration of the member attribute (may be in base class), but we know what NOT to expect:
+ template <class U> static No& hasMemberCstr(HelperFn<int Fallback::*, &Helper2<U>::c_str>*);
+ template <class U> static Yes& hasMemberCstr(...);
+
+ template <class U> static No& hasMemberLength(HelperFn<int Fallback::*, &Helper2<U>::length>*);
+ template <class U> static Yes& hasMemberLength(...);
+public:
+ enum { Result = sizeof(hasMemberCstr <T>(NULL)) == sizeof(Yes) &&
+ sizeof(hasMemberLength<T>(NULL)) == sizeof(Yes)
+ };
+};
+
+template <class S, bool isStringClass> struct StringTraits2 { typedef Loki::EmptyType Result; }; //"StringTraits2": fix some VS bug with namespace and partial template specialization
+template <class S> struct StringTraits2<S, true> { typedef typename S::value_type Result; };
+template <> struct StringTraits2<char, false> { typedef char Result; };
+template <> struct StringTraits2<wchar_t, false> { typedef wchar_t Result; };
+}
template <class S>
-inline
-bool endsWith(const S& str, typename S::value_type postfix)
+struct StringTraits
{
- const size_t len = str.length();
- return len != 0 && str.operator[](len - 1) == postfix;
-}
+private:
+ typedef typename implementation::UnReference<S>::NonRefType NonRefType;
+ typedef typename Loki::TypeTraits<NonRefType>::NonConstType UndecoratedType;
+
+ typedef typename implementation::UnArray<UndecoratedType>::NonArrayType NonArrayType;
+ typedef typename implementation::UnPointer<NonArrayType>::NonPtrType NonPtrType;
+ typedef typename Loki::TypeTraits<NonPtrType>::NonConstType NonConstValType; //handle "const char* const"
+public:
+ enum
+ {
+ isStringClass = implementation::HasStringMembers<UndecoratedType, implementation::HasValueType<UndecoratedType>::Result>::Result
+ };
+ typedef typename implementation::StringTraits2<NonConstValType, isStringClass>::Result CharType;
-template <class S>
-inline
-bool endsWith(const S& str, const typename S::value_type* postfix)
+ enum
+ {
+ isStringLike = Loki::IsSameType<CharType, char>::value || Loki::IsSameType<CharType, wchar_t>::value
+ };
+};
+
+
+template <class S> inline
+const typename StringTraits<S>::CharType* strBegin(const S& str, typename S::value_type dummy = 0) { return str.c_str(); } //SFINAE: T must be a "string"
+
+template <class Char>
+inline const typename StringTraits<Char>::CharType* strBegin(const Char* str) { return str; }
+inline const char* strBegin(const char& ch) { return &ch; }
+inline const wchar_t* strBegin(const wchar_t& ch) { return &ch; }
+
+
+template <class S> inline
+size_t strLength(const S& str, typename S::value_type dummy = 0) { return str.length(); } //SFINAE: T must be a "string"
+
+template <class Char>
+inline size_t strLength(const Char* str) { return cStringLength(str); }
+inline size_t strLength(char) { return 1; }
+inline size_t strLength(wchar_t) { return 1; }
+
+
+template <class S, class T> inline
+bool startsWith(const S& str, const T& prefix)
{
- const size_t pfLength = cStringLength(postfix);
- if (str.length() < pfLength)
+ assert_static(StringTraits<S>::isStringLike);
+ assert_static(StringTraits<T>::isStringLike);
+
+ const size_t pfLength = strLength(prefix);
+ if (strLength(str) < pfLength)
return false;
- const typename S::value_type* cmpBegin = str.c_str() + str.length() - pfLength;
- return std::equal(cmpBegin, cmpBegin + pfLength,
- postfix);
+ return std::equal(strBegin(str), strBegin(str) + pfLength,
+ strBegin(prefix));
}
-template <class S, class T>
-inline
-bool endsWith(const S& str, const T& postfix, typename T::value_type)
+template <class S, class T> inline
+bool endsWith(const S& str, const T& postfix)
{
- if (str.length() < postfix.length())
+ assert_static(StringTraits<S>::isStringLike);
+ assert_static(StringTraits<T>::isStringLike);
+
+ size_t strLen = strLength(str);
+ size_t pfLen = strLength(postfix);
+ if (strLen < pfLen)
return false;
- const typename S::value_type* cmpBegin = str.c_str() + str.length() - postfix.length();
- return std::equal(cmpBegin, cmpBegin + postfix.length(),
- postfix.c_str());
+ typedef typename StringTraits<S>::CharType CharType;
+
+ const CharType* cmpBegin = strBegin(str) + strLen - pfLen;
+ return std::equal(cmpBegin, cmpBegin + pfLen,
+ strBegin(postfix));
}
// get all characters after the last occurence of ch
// (returns the whole string if ch not found)
-template <class S>
-inline
-S afterLast(const S& str, typename S::value_type ch)
+template <class S, class T> inline
+S afterLast(const S& str, const T& ch)
{
+ assert_static(StringTraits<T>::isStringLike);
+
const size_t pos = str.rfind(ch);
if (pos != S::npos)
- return S(str.c_str() + pos + 1, str.length() - pos - 1);
+ {
+ size_t chLen = strLength(ch);
+ return S(str.c_str() + pos + chLen, str.length() - pos - chLen);
+ }
else
return str;
}
@@ -216,10 +331,11 @@ S afterLast(const S& str, typename S::value_type ch)
// get all characters before the last occurence of ch
// (returns empty string if ch not found)
-template <class S>
-inline
-S beforeLast(const S& str, typename S::value_type ch)
+template <class S, class T> inline
+S beforeLast(const S& str, const T& ch)
{
+ assert_static(StringTraits<T>::isStringLike);
+
const size_t pos = str.rfind(ch);
if (pos != S::npos)
return S(str.c_str(), pos); //data is non-empty string in this context: else ch would not have been found!
@@ -229,13 +345,17 @@ S beforeLast(const S& str, typename S::value_type ch)
//returns empty string if ch not found
-template <class S>
-inline
-S afterFirst(const S& str, typename S::value_type ch)
+template <class S, class T> inline
+S afterFirst(const S& str, const T& ch)
{
+ assert_static(StringTraits<T>::isStringLike);
+
const size_t pos = str.find(ch);
if (pos != S::npos)
- return S(str.c_str() + pos + 1, str.length() - pos - 1);
+ {
+ size_t chLen = strLength(ch);
+ return S(str.c_str() + pos + chLen, str.length() - pos - chLen);
+ }
else
return S();
@@ -243,11 +363,12 @@ S afterFirst(const S& str, typename S::value_type ch)
//returns the whole string if ch not found
-template <class S>
-inline
-S beforeFirst(const S& str, typename S::value_type ch)
+template <class S, class T> inline
+S beforeFirst(const S& str, const T& ch)
{
- const size_t pos = str.find(ch, 0);
+ assert_static(StringTraits<T>::isStringLike);
+
+ const size_t pos = str.find(ch);
if (pos != S::npos)
return S(str.c_str(), pos); //data is non-empty string in this context: else ch would not have been found!
else
@@ -255,18 +376,19 @@ S beforeFirst(const S& str, typename S::value_type ch)
}
-template <class S, class T>
-inline
+template <class S, class T> inline
std::vector<S> split(const S& str, const T& delimiter)
{
- const S delim = S() + delimiter; //handle char, char* and string
+ assert_static(StringTraits<T>::isStringLike);
+
std::vector<S> output;
size_t bockStart = 0;
- if (!delim.empty())
+ size_t delimLen = strLength(delimiter);
+ if (delimLen != 0)
{
- for (size_t blockEnd = str.find(delim, bockStart);
+ for (size_t blockEnd = str.find(delimiter, bockStart);
blockEnd != S::npos;
- bockStart = blockEnd + delim.size(), blockEnd = str.find(delim, bockStart))
+ bockStart = blockEnd + delimLen, blockEnd = str.find(delimiter, bockStart))
{
output.push_back(S(str.c_str() + bockStart, blockEnd - bockStart));
}
@@ -276,8 +398,7 @@ std::vector<S> split(const S& str, const T& delimiter)
}
-template <class S>
-inline
+template <class S> inline
void truncate(S& str, size_t newLen)
{
if (newLen < str.length())
@@ -285,18 +406,19 @@ void truncate(S& str, size_t newLen)
}
-template <class S>
-inline
-void replace(S& str, const typename S::value_type* old, const typename S::value_type* replacement, bool replaceAll)
+template <class S, class T, class U> inline
+void replace(S& str, const T& old, const U& replacement, bool replaceAll)
{
- const size_t oldLen = cStringLength(old);
- const size_t replacementLen = cStringLength(replacement);
+ assert_static(StringTraits<T>::isStringLike);
+ assert_static(StringTraits<U>::isStringLike);
size_t pos = 0;
+ size_t oldLen = strLength(old);
+ size_t repLen = strLength(replacement);
while ((pos = str.find(old, pos)) != S::npos)
{
- str.replace(pos, oldLen, replacement, replacementLen);
- pos += replacementLen; //move past the string that was replaced
+ str.replace(pos, oldLen, replacement);
+ pos += repLen; //move past the string that was replaced
if (!replaceAll)
break;
@@ -304,8 +426,7 @@ void replace(S& str, const typename S::value_type* old, const typename S::value_
}
-template <class S>
-inline
+template <class S> inline
void trim(S& str, bool fromLeft, bool fromRight)
{
assert(fromLeft || fromRight);
@@ -334,93 +455,205 @@ void trim(S& str, bool fromLeft, bool fromRight)
}
-namespace
+namespace implementation
{
template <class S, class T>
struct CnvtStringToString
{
- T convert(const S& src) const { return T(src.c_str(), src.size()); }
+ T convert(const S& src) const { return T(strBegin(src), strLength(src)); }
};
template <class S>
-struct CnvtStringToString<S, S> //optimization: for "basic_string -> basic_string" we don't need a deep copy
+struct CnvtStringToString<S, S> //perf: we don't need a deep copy if string types match
{
- S convert(const S& src) const { return src; }
+ const S& convert(const S& src) const { return src; }
};
+}
+template <class T, class S> inline
+T cvrtString(const S& str) { return implementation::CnvtStringToString<S, T>().convert(str); }
-template <class S, class Num>
+
+namespace implementation
+{
+enum NumberType
+{
+ NUM_TYPE_SIGNED_INT,
+ NUM_TYPE_UNSIGNED_INT,
+ NUM_TYPE_FLOATING_POINT,
+ NUM_TYPE_OTHER,
+};
+
+
+template <class S, class Num, NumberType>
struct CvrtNumberToString
{
- S convert(const Num& number) const //convert string to number using streams: convenient, but SLOW
+ S convert(const Num& number) const //default number to string conversion using streams: convenient, but SLOW, SLOW, SLOW!!!! (~ factor of 20)
{
- typedef typename S::value_type CharType;
+ typedef typename StringTraits<S>::CharType CharType;
std::basic_ostringstream<CharType> ss;
ss << number;
- return CnvtStringToString<std::basic_string<CharType>, S>().convert(ss.str());
+ return cvrtString<S>(ss.str());
}
};
-template <class S, class Num, bool isIntegral>
-struct CvrtStringToNumber
+template <class S, class Num>
+struct CvrtNumberToString<S, Num, NUM_TYPE_FLOATING_POINT>
{
- Num convert(const S& str) const //convert number to string using streams: convenient, but SLOW
+ S convert(const Num& number) const { return convertFloat(number, typename StringTraits<S>::CharType()); }
+
+private:
+ S convertFloat(const Num& number, char) const
+ {
+ char buffer[50];
+ int charsWritten = std::sprintf(buffer, "%f", static_cast<double>(number));
+ return charsWritten > 0 ? S(buffer, charsWritten) : S();
+ }
+ S convertFloat(const Num& number, wchar_t) const
{
- typedef typename S::value_type CharType;
+ wchar_t buffer[50];
+#ifdef __MINGW32__
+ int charsWritten = ::swprintf(buffer, L"%f", static_cast<double>(number)); //MinGW does not comply to the C standard!
+#else
+ int charsWritten = std::swprintf(buffer, 50, L"%f", static_cast<double>(number));
+#endif
+ return charsWritten > 0 ? S(buffer, charsWritten) : S();
+ }
+};
+
+/*
+perf: integer to string: (executed 10 mio. times)
+ std::stringstream - 14796 ms
+ std::sprintf - 3086 ms
+ hand coded - 778 ms
+*/
+
+template <class S, class Num> inline
+S formatInteger(Num n, bool hasMinus)
+{
+ assert(n >= 0);
+ S output;
+ do
+ {
+ output += '0' + n % 10;
+ n /= 10;
+ }
+ while (n != 0);
+ if (hasMinus)
+ output += '-';
+
+ std::reverse(output.begin(), output.end());
+ return output;
+}
+template <class S, class Num>
+struct CvrtNumberToString<S, Num, NUM_TYPE_SIGNED_INT>
+{
+ S convert(const Num& number) const { return formatInteger<S>(number < 0 ? -number : number, number < 0); }
+};
+
+template <class S, class Num>
+struct CvrtNumberToString<S, Num, NUM_TYPE_UNSIGNED_INT>
+{
+ S convert(const Num& number) const { return formatInteger<S>(number, false); }
+};
+
+//--------------------------------------------------------------------------------
+
+template <class S, class Num, NumberType>
+struct CvrtStringToNumber
+{
+ Num convert(const S& str) const //default string to number conversion using streams: convenient, but SLOW
+ {
+ typedef typename StringTraits<S>::CharType CharType;
Num number = 0;
- std::basic_istringstream<CharType>(CnvtStringToString<S, std::basic_string<CharType> >().convert(str)) >> number;
+ std::basic_istringstream<CharType>(cvrtString<std::basic_string<CharType> >(str)) >> number;
return number;
}
};
template <class S, class Num>
-struct CvrtStringToNumber<S, Num, true>
+struct CvrtStringToNumber<S, Num, NUM_TYPE_FLOATING_POINT>
{
- Num convert(const S& str) const //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic
- {
- typedef typename S::value_type CharType;
+ Num convert(const S& str) const { return convertFloat(strBegin(str)); }
+
+private:
+ Num convertFloat(const char* str) const { return std::strtod(str, NULL); }
+ Num convertFloat(const wchar_t* str) const { return std::wcstod(str, NULL); }
+};
- const CharType* first = str.c_str();
- const CharType* last = first + str.size();
+template <class Num, class S>
+Num extractInteger(const S& str, bool& hasMinusSign) //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic
+{
+ typedef typename StringTraits<S>::CharType CharType;
- while (first != last && cStringIsWhiteSpace(*first)) //skip leading whitespace
- ++first;
+ const CharType* first = strBegin(str);
+ const CharType* last = first + strLength(str);
- bool hasMinusSign = false; //handle minus sign
- if (first != last)
+ while (first != last && cStringIsWhiteSpace(*first)) //skip leading whitespace
+ ++first;
+
+ hasMinusSign = false; //handle minus sign
+ if (first != last)
+ {
+ if (*first == '-')
{
- if (*first == '-')
- {
- hasMinusSign = true;
- ++first;
- }
- else if (*first == '+')
- ++first;
+ hasMinusSign = true;
+ ++first;
}
+ else if (*first == '+')
+ ++first;
+ }
- Num number = 0;
- for (const CharType* iter = first; iter != last; ++iter)
+ Num number = 0;
+ for (const CharType* iter = first; iter != last; ++iter)
+ {
+ const CharType c = *iter;
+ if ('0' <= c && c <= '9')
+ {
+ number *= 10;
+ number += c - '0';
+ }
+ else
{
- const CharType c = *iter;
- if ('0' <= c && c <= '9')
- {
- number *= 10;
- number += c - '0';
- }
- else
- {
- assert(std::find_if(iter, last, std::not1(std::ptr_fun(&cStringIsWhiteSpace<CharType>))) == last); //rest of string should contain whitespace only
- break;
- }
+ assert(std::find_if(iter, last, std::not1(std::ptr_fun(&cStringIsWhiteSpace<CharType>))) == last); //rest of string should contain whitespace only
+ break;
}
+ }
+ return number;
+}
+
+template <class S, class Num>
+struct CvrtStringToNumber<S, Num, NUM_TYPE_SIGNED_INT>
+{
+ Num convert(const S& str) const
+ {
+ bool hasMinusSign = false; //handle minus sign
+ const Num number = extractInteger<Num>(str, hasMinusSign);
return hasMinusSign ? -number : number;
}
};
+
+
+template <class S, class Num>
+struct CvrtStringToNumber<S, Num, NUM_TYPE_UNSIGNED_INT>
+{
+ Num convert(const S& str) const //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic
+ {
+ bool hasMinusSign = false; //handle minus sign
+ const Num number = extractInteger<Num>(str, hasMinusSign);
+ if (hasMinusSign)
+ {
+ assert(false);
+ return 0U;
+ }
+ return number;
+ }
+};
}
@@ -428,15 +661,27 @@ template <class S, class Num>
inline
S toString(const Num& number) //convert number to string the C++ way
{
- return CvrtNumberToString<S, Num>().convert(number);
+ using namespace implementation;
+ return CvrtNumberToString<S, Num,
+ Loki::TypeTraits<Num>::isSignedInt ? NUM_TYPE_SIGNED_INT :
+ Loki::TypeTraits<Num>::isUnsignedInt ? NUM_TYPE_UNSIGNED_INT :
+ Loki::TypeTraits<Num>::isFloat ? NUM_TYPE_FLOATING_POINT :
+ NUM_TYPE_OTHER
+ >().convert(number);
}
template <class Num, class S>
inline
-Num toNumber(const S& str) //convert number to string the C++ way
+Num toNumber(const S& str) //convert string to number the C++ way
{
- return CvrtStringToNumber<S, Num, Loki::TypeTraits<Num>::isIntegral>().convert(str);
+ using namespace implementation;
+ return CvrtStringToNumber<S, Num,
+ Loki::TypeTraits<Num>::isSignedInt ? NUM_TYPE_SIGNED_INT :
+ Loki::TypeTraits<Num>::isUnsignedInt ? NUM_TYPE_UNSIGNED_INT :
+ Loki::TypeTraits<Num>::isFloat ? NUM_TYPE_FLOATING_POINT :
+ NUM_TYPE_OTHER
+ >().convert(str);
}
}
bgstack15