]> git.tdb.fi Git - libs/core.git/blobdiff - source/strings/lexicalcast.cpp
Fix several corner case bugs in lexical_cast
[libs/core.git] / source / strings / lexicalcast.cpp
index 5903bb1197d6364382714dd4f1772b477844b919..ab83b110b432ba408f8bdfa3bd2fb53d3e848c8f 100644 (file)
@@ -1,12 +1,7 @@
-/* $Id$
-
-This file is part of libmspstrings
-Copyright © 2006-2008 Mikko Rasa
-Distributed under the LGPL
-*/
-
 #include <cmath>
 #include <limits>
+#include <msp/core/inttypes.h>
+#include "format.h"
 #include "lexicalcast.h"
 
 using namespace std;
@@ -19,20 +14,6 @@ template<typename T>
 struct IsSigned
 { enum { result = !(static_cast<T>(-1)>0) }; };
 
-template<typename T, bool f = (sizeof(T)>sizeof(unsigned long))>
-struct Temporary
-{ typedef unsigned long Type; };
-
-template<typename T>
-struct Temporary<T, true>
-{
-#ifdef WIN32
-       typedef __int64 Type;
-#else
-       typedef unsigned long long Type;
-#endif
-};
-
 /* Helper to avoid warnings about an unsigned type never being < 0 */
 template<typename T, bool f = IsSigned<T>::result>
 struct IsNegative
@@ -78,7 +59,7 @@ char *int_to_str(T v, const Fmt &f, char *end)
        const char *digits = (f.get_uppercase() ? udigits : ldigits);
        if(v)
        {
-               typename Temporary<T>::Type w = Absolute<T>::eval(v);
+               typename MatchingInt<T>::UnsignedType w = Absolute<T>::eval(v);
                while(w)
                {
                        *--ptr = digits[w%base];
@@ -130,7 +111,7 @@ template<typename T>
 T str_to_int(const std::string &s, const Fmt &f)
 {
        if(s.empty())
-               throw LexicalError("Empty input in integer conversion");
+               throw lexical_error("conversion of '' to integer");
 
        std::string::const_iterator i = s.begin();
 
@@ -139,7 +120,7 @@ T str_to_int(const std::string &s, const Fmt &f)
        if(*i=='-')
        {
                if(!IsSigned<T>::result)
-                       throw LexicalError("Negative sign in unsigned integer conversion");
+                       throw lexical_error(format("conversion of '%s' to unsigned integer", s));
                neg = true;
                ++i;
        }
@@ -148,7 +129,7 @@ T str_to_int(const std::string &s, const Fmt &f)
 
        // Must have some digits to convert
        if(i==s.end())
-               throw LexicalError("Missing digits in integer conversion");
+               throw lexical_error(format("conversion of '%s' to integer", s));
 
        T base = f.get_base();
        if(!base && i!=s.end())
@@ -185,10 +166,10 @@ T str_to_int(const std::string &s, const Fmt &f)
                else if(*i>='a' && *i<='f')
                        digit = *i-'a'+10;
                if(digit>=base)
-                       throw LexicalError("Invalid digit in integer conversion");
+                       throw lexical_error(format("conversion of '%s' to integer (base-%d)", s, base));
                T next = result*base+digit;
                if(next/base!=result)
-                       throw LexicalError("Overflow in integer conversion");
+                       throw lexical_error(format("conversion of '%s' to %d-bit integer", s, sizeof(T)*8));
                result = next;
        }
 
@@ -212,14 +193,14 @@ string bool_to_str(bool b, const Fmt &f)
 bool str_to_bool(const string &s)
 {
        if(s.empty())
-               throw LexicalError("Empty input in boolean conversion");
+               throw lexical_error("conversion of '' to boolean");
 
        if(s=="1" || s=="true" || s=="yes" || s=="on")
                return true;
        else if(s=="0" || s=="false" || s=="no" || s=="off")
                return false;
 
-       throw LexicalError("Invalid input in boolean conversion");
+       throw lexical_error(format("conversion of '%s' to boolean", s));
 }
 
 
@@ -229,7 +210,7 @@ template<typename T>
 string flt_to_str(T v, const Fmt &f)
 {
        if(f.get_type()==Fmt::CHAR)
-               throw LexicalError("Character format in floating-point conversion");
+               throw format_mismatch("floating-point conversion with character format");
 
        Fmt::FloatMode mode = f.get_floatmode();
        long double w = abs(v);
@@ -256,7 +237,7 @@ string flt_to_str(T v, const Fmt &f)
        if(w>=10)
        {
                long double div = 1;
-               while(div*10<w)
+               while(div*10<=w)
                {
                        ++exp;
                        div *= 10;
@@ -276,6 +257,7 @@ string flt_to_str(T v, const Fmt &f)
 
        // Decide how to format the number
        unsigned digits;
+       unsigned leading_zeroes = 0;
        unsigned point = 1;
        bool showexp = false;
        if(mode==Fmt::FIXED)
@@ -298,15 +280,16 @@ string flt_to_str(T v, const Fmt &f)
                }
                else
                {
-                       point = max(exp, 0)+1;
                        if(exp<0)
-                               digits += -exp;
+                               leading_zeroes = -exp;
+                       else
+                               point = exp+1;
                }
        }
 
        // Apply rounding
        w += 5.0l/pow(10.0l, static_cast<long double>(digits));
-       if(w>10)
+       if(w>=10)
        {
                // Rounding bumped us to the next exponent, deal with it
                w /= 10;
@@ -317,13 +300,19 @@ string flt_to_str(T v, const Fmt &f)
                }
                if(!showexp)
                {
-                       ++digits;
-                       ++point;
+                       if(mode==Fmt::FIXED)
+                               ++digits;
+                       if(leading_zeroes)
+                               --leading_zeroes;
+                       else
+                               ++point;
                }
                else
                        ++exp;
        }
 
+       digits += leading_zeroes;
+
        // Create a buffer and start from the end
        unsigned size = max(f.get_width(), digits+8);
        char *buf = new char[size];
@@ -345,14 +334,17 @@ string flt_to_str(T v, const Fmt &f)
        {
                if(i==point)
                        *mptr++ = '.';
-               if(showexp || static_cast<int>(i)>=-exp)
+               if(!leading_zeroes)
                {
                        int digit = static_cast<int>(w);
                        *mptr++ = '0'+digit;
                        w = (w-digit)*10;
                }
                else
+               {
                        *mptr++ = '0';
+                       --leading_zeroes;
+               }
        }
 
        if(f.get_showpoint())
@@ -395,7 +387,7 @@ template<typename T>
 T str_to_flt(const string &s, const Fmt &)
 {
        if(s.empty())
-               throw LexicalError("Empty input in floating-point conversion");
+               throw lexical_error("conversion of '' to floating-point");
 
        std::string::const_iterator i = s.begin();
 
@@ -411,7 +403,7 @@ T str_to_flt(const string &s, const Fmt &)
 
        // Must have some digits to convert
        if(i==s.end())
-               throw LexicalError("Missing digits in floating-point conversion");
+               throw lexical_error(format("conversion of '%s' to floating-point", s));
 
        long double v = 0;
        int exp = 0;
@@ -423,7 +415,7 @@ T str_to_flt(const string &s, const Fmt &)
                if(*i=='.')
                {
                        if(point_seen)
-                               throw LexicalError("Extra point in floating-point conversion");
+                               throw lexical_error(format("conversion of '%s' to floating-point", s));
                        point_seen = true;
                }
                else if(*i>='0' && *i<='9')
@@ -442,7 +434,7 @@ T str_to_flt(const string &s, const Fmt &)
                        break;
                }
                else
-                       throw LexicalError("Invalid digit in floating-point conversion");
+                       throw lexical_error(format("conversion of '%s' to floating-point", s));
        }
 
        // Scale and negate the result as needed
@@ -469,7 +461,7 @@ T str_to_flt(const string &s, const Fmt &)
 string str_to_str(const string &s, const Fmt &f)
 {
        if(f.get_type()==Fmt::NUM)
-               throw LexicalError("Numeric format in string conversion");
+               throw format_mismatch("string conversion with numeric format");
        return s;
 }
 
@@ -553,7 +545,7 @@ void operator<<(LexicalConverter &c, const char *s)
 { c.result(str_to_str(s, c.get_fmt())); }
 
 void operator<<(LexicalConverter &c, const void *p)
-{ c.result(int_to_str(reinterpret_cast<unsigned long>(p), c.get_fmt())); }
+{ c.result(int_to_str(reinterpret_cast<IntPtr>(p), c.get_fmt())); }
 
 
 /*** operator>> ***/
@@ -566,9 +558,9 @@ void operator>>(const LexicalConverter &c, char &v)
        {
                const std::string &s = c.get();
                if(s.empty())
-                       throw LexicalError("Empty input in character conversion");
+                       throw lexical_error("conversion of '' to character");
                if(s.size()>1)
-                       throw LexicalError("Extra input in character conversion");
+                       throw lexical_error(format("conversion of '%s' to character", s));
                v = s[0];
        }
 }