diff options
author | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:12:17 +0200 |
---|---|---|
committer | Daniel Wilhelm <daniel@wili.li> | 2014-04-18 17:12:17 +0200 |
commit | b654dbfa5f3e4a4d02f72023f7c5895635aa6396 (patch) | |
tree | 8c1dfe7f638c0fc7afc1d08bc2fc0fd0f8646e5e /shared/string_tools.h | |
parent | 3.17 (diff) | |
download | FreeFileSync-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.h | 577 |
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); } } |