Rewrite lexical_cast to use internal conversion routines
authorMikko Rasa <tdb@tdb.fi>
Tue, 9 Dec 2008 09:12:30 +0000 (09:12 +0000)
committerMikko Rasa <tdb@tdb.fi>
Tue, 9 Dec 2008 09:12:30 +0000 (09:12 +0000)
Add getters to Fmt

source/fmt.cpp
source/fmt.h
source/lexicalcast.cpp [new file with mode: 0644]
source/lexicalcast.h

index fdd73f3c43b726175f927fe4d0ade866db577315..0c1531d8315dfe8c19560be5b28ed2b8374cf9e7 100644 (file)
@@ -1,9 +1,10 @@
 /* $Id$
 
 This file is part of libmspstrings
-Copyright © 2006-2007 Mikko Rasa
+Copyright © 2006-2008 Mikko Rasa
 Distributed under the LGPL
 */
+
 #include <msp/core/except.h>
 #include "fmt.h"
 
@@ -22,10 +23,11 @@ Fmt &Fmt::reset()
        fillc=' ';
        base=DEC;
        sbase=false;
-       fmode=EXP;
+       fmode=AUTOFLT;
        spoint=false;
        align=RIGHT;
        ucase=false;
+       type=STR;
 
        return *this;
 }
@@ -94,27 +96,36 @@ void Fmt::parse(const char *f)
                }
        }
 
-       if(*f=='x' || *f=='X')
+       type=NUM;
+       if(*f=='d' || *f=='u')
+               base=DEC;
+       else if(*f=='x' || *f=='X')
                base=HEX;
        else if(*f=='o')
                base=OCT;
+       else if(*f=='b')
+               base=BIN;
        else if(*f=='e' || *f=='E')
                fmode=SCI;
        else if(*f=='f' || *f=='F')
                fmode=FIXED;
        else if(*f=='g' || *f=='G')
-               fmode=EXP;
-       else if(*f=='p')
+               fmode=AUTOFLT;
+       else if(*f=='p' || *f=='P')
        {
                base=HEX;
                sbase=true;
        }
-       else if(*f=='d' || *f=='i' || *f=='u' || *f=='c' || *f=='s')
-               ;
+       else if(*f=='c')
+               type=CHAR;
+       else if(*f=='s')
+               type=STR;
+       else if(*f=='i')
+               base=AUTOBASE;
        else
                throw InvalidParameterValue("Invalid conversion specifier");
 
-       if(*f=='E' || *f=='F' || *f=='G' || *f=='X')
+       if(*f=='E' || *f=='F' || *f=='G' || *f=='X' || *f=='P')
                ucase=true;
 
        ++f;
index c4b46b5e3d86f599853c964ca02e5eb073e0e32d..7dc605fd05086434d05def3a6165146033877f2b 100644 (file)
@@ -1,7 +1,7 @@
 /* $Id$
 
 This file is part of libmspstrings
-Copyright © 2006-2007 Mikko Rasa
+Copyright © 2006-2008 Mikko Rasa
 Distributed under the LGPL
 */
 
@@ -23,43 +23,39 @@ chaining calls to the various setter functions, or with a mixture of both.
 
 Since type information for conversions is acquired through templates, the
 meaning of the conversion specifier character is reduced to only specifying
-what the conversion should look like.  In particular, the d, i, u, c and s
-conversions are no-ops.
+what the conversion should look like.  Of special note is the s conversion,
+which will result in a default conversion for any data type.  Size modifiers
+are not supported and there is no difference between signed and unsigned
+conversions.
+
+Some new conversions are supported:
+
+  b/B  Binary integer conversion
+  P    Uppercase pointer conversion (like %#X)
 */
 class Fmt
 {
 public:
-       Fmt()                        { reset(); }
-       Fmt(const char *f)           { reset(); parse(f); }
-       Fmt(const std::string &f)    { reset(); parse(f.c_str()); }
-       Fmt  &width(unsigned w)      { wd=w; return *this; }
-       Fmt  &precision(unsigned p)  { prec=p; return *this; }
-       Fmt  &showpos(bool s=true)   { spos=s; return *this; }
-       Fmt  &fill(wchar_t f)        { fillc=f; return *this; }
-       Fmt  &fixed()                { fmode=FIXED; return *this; }
-       Fmt  &scientific()           { fmode=SCI; return *this; }
-       Fmt  &showpoint(bool s=true) { spoint=s; return *this; }
-       Fmt  &showbase(bool s=true)  { sbase=s; return *this; }
-       Fmt  &left()                 { align=LEFT; return *this; }
-       Fmt  &right()                { align=RIGHT; return *this; }
-       Fmt  &dec()                  { base=DEC; return *this; }
-       Fmt  &hex()                  { base=HEX; return *this; }
-       Fmt  &oct()                  { base=OCT; return *this; }
-       Fmt  &uppercase(bool u=true) { ucase=u; return *this; }
-       Fmt  &reset();
-       void apply(std::ostream &) const;
-private:
+       enum Type
+       {
+               NUM,
+               CHAR,
+               STR
+       };
+
        enum Base
        {
-               DEC,
-               HEX,
-               OCT
+               AUTOBASE = 0,
+               DEC = 10,
+               HEX = 16,
+               OCT = 8,
+               BIN = 2
        };
 
        enum FloatMode
        {
                FIXED,
-               EXP,
+               AUTOFLT,
                SCI
        };
 
@@ -69,6 +65,7 @@ private:
                RIGHT
        };
 
+private:
        unsigned  wd;
        unsigned  prec;
        bool      spos;
@@ -79,7 +76,47 @@ private:
        bool      spoint;
        Align     align;
        bool      ucase;
+       Type      type;
+
+public:
+       Fmt()                     { reset(); }
+       Fmt(const char *f)        { reset(); parse(f); }
+       Fmt(const std::string &f) { reset(); parse(f.c_str()); }
+
+       Fmt &width(unsigned w)      { wd=w; return *this; }
+       Fmt &precision(unsigned p)  { prec=p; return *this; }
+       Fmt &showpos(bool s=true)   { spos=s; return *this; }
+       Fmt &fill(wchar_t f)        { fillc=f; return *this; }
+       Fmt &fixed()                { fmode=FIXED; return *this; }
+       Fmt &scientific()           { fmode=SCI; return *this; }
+       Fmt &showpoint(bool s=true) { spoint=s; return *this; }
+       Fmt &showbase(bool s=true)  { sbase=s; return *this; }
+       Fmt &left()                 { align=LEFT; return *this; }
+       Fmt &right()                { align=RIGHT; return *this; }
+       Fmt &dec()                  { base=DEC; return *this; }
+       Fmt &hex()                  { base=HEX; return *this; }
+       Fmt &oct()                  { base=OCT; return *this; }
+       Fmt &bin()                  { base=BIN; return *this; }
+       Fmt &uppercase(bool u=true) { ucase=u; return *this; }
+       Fmt &numeric()              { type=NUM; return *this; }
+       Fmt &character()            { type=CHAR; return *this; }
+       Fmt &string()               { type=STR; return *this; }
+       Fmt &reset();
 
+       unsigned  get_width() const     { return wd; }
+       unsigned  get_precision() const { return prec; }
+       bool      get_showpos() const   { return spos; }
+       wchar_t   get_fill() const      { return fillc; }
+       Base      get_base() const      { return base; }
+       bool      get_showbase() const  { return sbase; }
+       FloatMode get_floatmode() const { return fmode; }
+       bool      get_showpoint() const { return spoint; }
+       Align     get_align() const     { return align; }
+       bool      get_uppercase() const { return ucase; }
+       Type      get_type() const      { return type; }
+
+       void apply(std::ostream &) const;
+private:
        void parse(const char *);
 };
 
diff --git a/source/lexicalcast.cpp b/source/lexicalcast.cpp
new file mode 100644 (file)
index 0000000..687a37b
--- /dev/null
@@ -0,0 +1,619 @@
+/* $Id$
+
+This file is part of libmspstrings
+Copyright © 2006-2008 Mikko Rasa
+Distributed under the LGPL
+*/
+
+#include <cmath>
+#include <limits>
+#include "lexicalcast.h"
+
+using namespace std;
+
+namespace {
+
+using namespace Msp;
+
+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
+{ static bool eval(T v) { return v<0; } };
+
+template<typename T>
+struct IsNegative<T, false>
+{ static bool eval(T) { return false; } };
+
+/* Helper to avoid errors about ambiguous function calls since there are no
+overloads of abs for unsigned types */
+template<typename T, bool f=IsSigned<T>::result>
+struct Absolute
+{ static T eval(T v) { return v<0 ? -v : v; } };
+
+template<typename T>
+struct Absolute<T, false>
+{ static T eval(T v) { return v; } };
+
+
+/*** Integer conversions ***/
+
+const char udigits[]="0123456789ABCDEF";
+const char ldigits[]="0123456789abcdef";
+
+template<typename T>
+char *int_to_str(T v, const Fmt &f, char *end)
+{
+       if(f.get_type()==Fmt::CHAR)
+       {
+               *--end=v;
+               return end;
+       }
+
+       char *ptr=end;
+
+       // Find out the base to use
+       unsigned base=f.get_base();
+       if(!base)
+               base=10;
+
+       // Format the number, starting from the least significant digit
+       const char *digits=(f.get_uppercase() ? udigits : ldigits);
+       if(v)
+       {
+               typename Temporary<T>::Type w=Absolute<T>::eval(v);
+               while(w)
+               {
+                       *--ptr=digits[w%base];
+                       w/=base;
+               }
+       }
+       else
+               *--ptr=digits[0];
+
+       char sign=(IsNegative<T>::eval(v) ? '-' : f.get_showpos() ? '+' : 0);
+       if(f.get_fill()=='0')
+       {
+               /* Zero-fill, taking base/sign size into account.  The expression is a
+               bit ugly, but saves having to write code for creating the prefix both
+               ways. */
+               unsigned pfxsize=((f.get_showbase() && base!=10) ? base==8 ? 1 : 2 : 0) + (sign!=0);
+               for(unsigned i=(end-ptr)+pfxsize; i<f.get_width(); ++i)
+                       *--ptr='0';
+       }
+
+       if(f.get_showbase() && v!=0)
+       {
+               // Add base indicator
+               if(base==2)
+                       *--ptr=(f.get_uppercase() ? 'B' : 'b');
+               else if(base==16)
+                       *--ptr=(f.get_uppercase() ? 'X' : 'x');
+               if(base!=10)
+                       *--ptr='0';
+       }
+
+       if(sign)
+               *--ptr=sign;
+
+       return ptr;
+}
+
+template<typename T>
+string int_to_str(T v, const Fmt &f)
+{
+       unsigned size=max(f.get_width(), max(f.get_precision(), sizeof(T)*8+3));
+       char *buf=new char[size];
+       string result(int_to_str(v, f, buf+size), buf+size);
+       delete[] buf;
+       return result;
+}
+
+template<typename T>
+T str_to_int(const std::string &s, const Fmt &f)
+{
+       if(s.empty())
+               throw LexicalError("Empty input in integer conversion");
+
+       std::string::const_iterator i=s.begin();
+
+       // See if the input starts with a sign
+       bool neg=false;
+       if(*i=='-')
+       {
+               if(!IsSigned<T>::result)
+                       throw LexicalError("Negative sign in unsigned integer conversion");
+               neg=true;
+               ++i;
+       }
+       else if(*i=='+')
+               ++i;
+
+       // Must have some digits to convert
+       if(i==s.end())
+               throw LexicalError("Missing digits in integer conversion");
+
+       T base=f.get_base();
+       if(!base && i!=s.end())
+       {
+               // Automatic base detection requested, figure it out
+               if(*i=='0' && ++i!=s.end())
+               {
+                       if(*i=='x' || *i=='X')
+                       {
+                               base=16;
+                               ++i;
+                       }
+                       else if(*i=='b' || *i=='B')
+                       {
+                               base=2;
+                               ++i;
+                       }
+                       else
+                               base=8;
+               }
+               else
+                       base=10;
+       }
+
+       // Parse the digits
+       T result=0;
+       for(; i!=s.end(); ++i)
+       {
+               T digit=base;
+               if(*i>='0' && *i<='9')
+                       digit=*i-'0';
+               else if(*i>='A' && *i<='F')
+                       digit=*i-'A'+10;
+               else if(*i>='a' && *i<='f')
+                       digit=*i-'a'+10;
+               if(digit>=base)
+                       throw LexicalError("Invalid digit in integer conversion");
+               T next=result*base+digit;
+               if(next/base!=result)
+                       throw LexicalError("Overflow in integer conversion");
+               result=next;
+       }
+
+       if(neg)
+               result=-result;
+
+       return result;
+}
+
+
+/*** Boolean conversions ***/
+
+string bool_to_str(bool b, const Fmt &f)
+{
+       if(f.get_type()==Fmt::STR)
+               return b ? "true" : "false";
+       else
+               return b ? "1" : "0";
+}
+
+bool str_to_bool(const string &s)
+{
+       if(s=="1" || s=="true" || s=="yes" || s=="on")
+               return true;
+       else if(s=="0" || s=="false" || s=="no" || s=="off")
+               return true;
+       throw LexicalError("Invalid input in boolean conversion");
+}
+
+
+/*** Floating-point conversions ***/
+
+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");
+
+       Fmt::FloatMode mode=f.get_floatmode();
+       long double w=abs(v);
+       char sign=(v<0 ? '-' : f.get_showpos() ? '+' : 0);
+
+       // Handle infinity and not-a-number as special cases
+       if(!(w+w>w) && w!=0)
+       {
+               string result;
+               if(sign)
+                       result+=sign;
+               if(!(w>=0))
+                       result+=(f.get_uppercase() ? "NAN" : "nan");
+               else
+                       result+=(f.get_uppercase() ? "INF" : "inf");
+               if(result.size()<f.get_width())
+                       result=string(f.get_width()-result.size(), ' ')+result;
+               return result;
+       }
+
+       /* Find out the base-10 exponent.  Building up the multiplier / divisor
+       first helps with accuracy in some cases. */
+       int exp=0;
+       if(w>=10)
+       {
+               long double div=1;
+               while(div*10<w)
+               {
+                       ++exp;
+                       div*=10;
+               }
+               w/=div;
+       }
+       else if(mode!=Fmt::FIXED && w<1 && w!=0)
+       {
+               long double mul=1;
+               while(w*mul<1)
+               {
+                       --exp;
+                       mul*=10;
+               }
+               w*=mul;
+       }
+
+       // Decide how to format the number
+       unsigned digits;
+       unsigned point=1;
+       bool showexp=false;
+       if(mode==Fmt::FIXED)
+       {
+               point=exp+1;
+               digits=point+f.get_precision();
+       }
+       else if(mode==Fmt::SCI)
+       {
+               digits=f.get_precision()+1;
+               showexp=true;
+       }
+       else
+       {
+               digits=max(f.get_precision(), 1U);
+               if(exp<-4 || exp>=static_cast<int>(digits))
+               {
+                       point=1;
+                       showexp=true;
+               }
+               else
+               {
+                       point=max(exp, 0)+1;
+                       if(exp<0)
+                               digits+=-exp;
+               }
+       }
+
+       // Apply rounding
+       w+=5.0l/pow(10.0l, digits);
+       if(w>10)
+       {
+               // Rounding bumped us to the next exponent, deal with it
+               w/=10;
+               if(mode==Fmt::AUTOFLT && exp+1==static_cast<int>(digits))
+               {
+                       point=1;
+                       showexp=true;
+               }
+               if(!showexp)
+               {
+                       ++digits;
+                       ++point;
+               }
+               else
+                       ++exp;
+       }
+
+       // Create a buffer and start from the end
+       unsigned size=max(f.get_width(), digits+8);
+       char *buf=new char[size];
+       char *end=buf+size;
+       char *ptr=end;
+
+       // Format exponent
+       if(showexp)
+       {
+               ptr=int_to_str(exp, Fmt().showpos().fill('0').width(3), ptr);
+               *--ptr=(f.get_uppercase() ? 'E' : 'e');
+       }
+
+       // Format mantissa left-to-right
+       char *eptr=ptr;
+       ptr-=digits+(point<digits || f.get_showpoint());
+       char *mptr=ptr;
+       for(unsigned i=0; i<digits; ++i)
+       {
+               if(i==point)
+                       *mptr++='.';
+               if(showexp || static_cast<int>(i)>=-exp)
+               {
+                       int digit=static_cast<int>(w);
+                       *mptr++='0'+digit;
+                       w=(w-digit)*10;
+               }
+               else
+                       *mptr++='0';
+       }
+
+       if(f.get_showpoint())
+       {
+               // Radix point requested but not displayed yet, add it
+               if(digits<=point)
+                       *mptr++='.';
+       }
+       else if(mode==Fmt::AUTOFLT && digits>point)
+       {
+               // Remove trailing zeroes from fraction and a lone radix point
+               while(mptr[-1]=='0')
+                       --mptr;
+               if(mptr[-1]=='.')
+                       --mptr;
+               if(mptr!=eptr)
+               {
+                       while(mptr!=ptr)
+                               *--eptr=*--mptr;
+                       ptr=eptr;
+               }
+       }
+
+       // Add filling and sign
+       if(f.get_fill()=='0')
+       {
+               unsigned pfxlen=(sign!=0);
+               while(end-ptr+pfxlen<f.get_width())
+                       *--ptr='0';
+       }
+       if(sign)
+               *--ptr=sign;
+
+       string result(ptr, end);
+       delete[] buf;
+       return result;
+}
+
+template<typename T>
+T str_to_flt(const string &s, const Fmt &)
+{
+       if(s.empty())
+               throw LexicalError("Empty input in floating-point conversion");
+
+       std::string::const_iterator i=s.begin();
+
+       // See if the input starts with a sign
+       bool neg=false;
+       if(*i=='-')
+       {
+               neg=true;
+               ++i;
+       }
+       else if(*i=='+')
+               ++i;
+
+       // Must have some digits to convert
+       if(i==s.end())
+               throw LexicalError("Missing digits in floating-point conversion");
+
+       long double v=0;
+       int exp=0;
+
+       // Parse mantissa
+       bool point_seen=false;
+       for(; i!=s.end(); ++i)
+       {
+               if(*i=='.')
+               {
+                       if(point_seen)
+                               throw LexicalError("Extra point in floating-point conversion");
+                       point_seen=true;
+               }
+               else if(*i>='0' && *i<='9')
+               {
+                       v=v*10+(*i-'0');
+                       if(point_seen)
+                               --exp;
+               }
+               else if(*i=='e' || *i=='E')
+               {
+                       // We have an exponent
+                       ++i;
+
+                       exp+=str_to_int<int>(string(i, s.end()), Fmt());
+                       // str_to_int has eaten the rest of the input or thrown
+                       break;
+               }
+               else
+                       throw LexicalError("Invalid digit in floating-point conversion");
+       }
+
+       // Scale and negate the result as needed
+       while(exp>0)
+       {
+               v*=10;
+               --exp;
+       }
+       while(exp<0)
+       {
+               v/=10;
+               ++exp;
+       }
+
+       if(neg)
+               v=-v;
+
+       return v;
+}
+
+
+/*** String conversions ***/
+
+string str_to_str(const string &s, const Fmt &f)
+{
+       if(f.get_type()==Fmt::NUM)
+               throw LexicalError("Numeric format in string conversion");
+       return s;
+}
+
+}
+
+namespace Msp {
+
+void LexicalConverter::result(const string &s)
+{
+       if(s.size()<fmt.get_width())
+       {
+               if(fmt.get_align()==Fmt::RIGHT)
+                       buf=string(fmt.get_width()-s.size(), fmt.get_fill())+s;
+               else
+                       buf=s+string(fmt.get_width()-s.size(), fmt.get_fill());
+       }
+       else
+               buf=s;
+}
+
+
+/*** operator<< ***/
+
+void operator<<(LexicalConverter &c, char v)
+{
+       Fmt::Type type=c.get_fmt().get_type();
+       if(type==Fmt::NUM)
+               c.result(int_to_str(v, c.get_fmt()));
+       else
+               c.result(string(1, v));
+}
+
+void operator<<(LexicalConverter &c, signed char v)
+{ c.result(int_to_str(v, c.get_fmt())); }
+
+void operator<<(LexicalConverter &c, short v)
+{ c.result(int_to_str(v, c.get_fmt())); }
+
+void operator<<(LexicalConverter &c, int v)
+{ c.result(int_to_str(v, c.get_fmt())); }
+
+void operator<<(LexicalConverter &c, long v)
+{ c.result(int_to_str(v, c.get_fmt())); }
+
+void operator<<(LexicalConverter &c, unsigned char v)
+{ c.result(int_to_str(v, c.get_fmt())); }
+
+void operator<<(LexicalConverter &c, unsigned short v)
+{ c.result(int_to_str(v, c.get_fmt())); }
+
+void operator<<(LexicalConverter &c, unsigned v)
+{ c.result(int_to_str(v, c.get_fmt())); }
+
+void operator<<(LexicalConverter &c, unsigned long v)
+{ c.result(int_to_str(v, c.get_fmt())); }
+
+#ifdef __GNUC__
+void operator<<(LexicalConverter &c, long long v)
+{ c.result(int_to_str(v, c.get_fmt())); }
+
+void operator<<(LexicalConverter &c, unsigned long long v)
+{ c.result(int_to_str(v, c.get_fmt())); }
+#endif
+
+void operator<<(LexicalConverter &c, bool v)
+{ c.result(bool_to_str(v, c.get_fmt())); }
+
+void operator<<(LexicalConverter &c, float v)
+{ c.result(flt_to_str(v, c.get_fmt())); }
+
+void operator<<(LexicalConverter &c, double v)
+{ c.result(flt_to_str(v, c.get_fmt())); }
+
+void operator<<(LexicalConverter &c, long double v)
+{ c.result(flt_to_str(v, c.get_fmt())); }
+
+void operator<<(LexicalConverter &c, const string &s)
+{ c.result(str_to_str(s, c.get_fmt())); }
+
+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())); }
+
+
+/*** operator>> ***/
+
+void operator>>(const LexicalConverter &c, char &v)
+{
+       if(c.get_fmt().get_type()==Fmt::NUM)
+               v=str_to_int<char>(c.get(), c.get_fmt());
+       else
+       {
+               const std::string &s=c.get();
+               if(s.empty())
+                       throw LexicalError("Empty input in character conversion");
+               if(s.size()>1)
+                       throw LexicalError("Extra input in character conversion");
+               v=s[0];
+       }
+}
+
+void operator>>(const LexicalConverter &c, signed char &v)
+{ v=str_to_int<signed char>(c.get(), c.get_fmt()); }
+
+void operator>>(const LexicalConverter &c, short &v)
+{ v=str_to_int<short>(c.get(), c.get_fmt()); }
+
+void operator>>(const LexicalConverter &c, int &v)
+{ v=str_to_int<int>(c.get(), c.get_fmt()); }
+
+void operator>>(const LexicalConverter &c, long &v)
+{ v=str_to_int<long>(c.get(), c.get_fmt()); }
+
+void operator>>(const LexicalConverter &c, unsigned char &v)
+{ v=str_to_int<unsigned char>(c.get(), c.get_fmt()); }
+
+void operator>>(const LexicalConverter &c, unsigned short &v)
+{ v=str_to_int<unsigned short>(c.get(), c.get_fmt()); }
+
+void operator>>(const LexicalConverter &c, unsigned int &v)
+{ v=str_to_int<unsigned int>(c.get(), c.get_fmt()); }
+
+void operator>>(const LexicalConverter &c, unsigned long &v)
+{ v=str_to_int<unsigned long>(c.get(), c.get_fmt()); }
+
+#ifdef __GNUC__
+void operator>>(const LexicalConverter &c, long long &v)
+{ v=str_to_int<long long>(c.get(), c.get_fmt()); }
+
+void operator>>(const LexicalConverter &c, unsigned long long &v)
+{ v=str_to_int<unsigned long long>(c.get(), c.get_fmt()); }
+#endif
+
+void operator>>(const LexicalConverter &c, bool &v)
+{ v=str_to_bool(c.get()); }
+
+void operator>>(const LexicalConverter &c, float &v)
+{ v=str_to_flt<float>(c.get(), c.get_fmt()); }
+
+void operator>>(const LexicalConverter &c, double &v)
+{ v=str_to_flt<double>(c.get(), c.get_fmt()); }
+
+void operator>>(const LexicalConverter &c, long double &v)
+{ v=str_to_flt<long double>(c.get(), c.get_fmt()); }
+
+void operator>>(const LexicalConverter &c, string &s)
+{ s=str_to_str(c.get(), c.get_fmt()); }
+
+} // namespace Msp
index a2bb43953d43d764e24ff517d62e1ffd0052f6ee..42393fe251c9bbaee8b56c3767437d70a4ad18e3 100644 (file)
@@ -1,7 +1,7 @@
 /* $Id$
 
 This file is part of libmspstrings
-Copyright © 2006-2007 Mikko Rasa
+Copyright © 2006-2008 Mikko Rasa
 Distributed under the LGPL
 */
 
@@ -15,39 +15,110 @@ Distributed under the LGPL
 
 namespace Msp {
 
+/**
+Thrown for errors in lexical conversions
+*/
 class LexicalError: public Exception
 {
 public:
        LexicalError(const std::string &w_): Exception(w_) { }
 };
 
-template<typename T>
-T lexical_cast(const std::string &s)
+/**
+Helper class for lexical_cast to facilitate operator overloading.
+*/
+class LexicalConverter
 {
-       std::istringstream ss(s);
-       ss.setf(std::ios_base::fmtflags(0), std::ios_base::skipws);
+private:
+       Fmt fmt;
+       std::string buf;
+
+public:
+       LexicalConverter(const Fmt &f): fmt(f) { }
+       LexicalConverter(const std::string &s, const Fmt &f): fmt(f), buf(s) { }
+
+       const Fmt &get_fmt() const { return fmt; }
+       const std::string &get() const { return buf; }
+       void result(const std::string &);
+};
+
+void operator<<(LexicalConverter &, char);
+void operator<<(LexicalConverter &, signed char);
+void operator<<(LexicalConverter &, short);
+void operator<<(LexicalConverter &, int);
+void operator<<(LexicalConverter &, long);
+void operator<<(LexicalConverter &, unsigned char);
+void operator<<(LexicalConverter &, unsigned short);
+void operator<<(LexicalConverter &, unsigned);
+void operator<<(LexicalConverter &, unsigned long);
+#ifdef __GNUC__
+void operator<<(LexicalConverter &, long long);
+void operator<<(LexicalConverter &, unsigned long long);
+#endif
+void operator<<(LexicalConverter &, bool);
+void operator<<(LexicalConverter &, float);
+void operator<<(LexicalConverter &, double);
+void operator<<(LexicalConverter &, long double);
+void operator<<(LexicalConverter &, const std::string &);
+void operator<<(LexicalConverter &, const char *);
+void operator<<(LexicalConverter &, const void *);
+
+void operator>>(const LexicalConverter &, char &);
+void operator>>(const LexicalConverter &, signed char &);
+void operator>>(const LexicalConverter &, short &);
+void operator>>(const LexicalConverter &, int &);
+void operator>>(const LexicalConverter &, long &);
+void operator>>(const LexicalConverter &, unsigned char &);
+void operator>>(const LexicalConverter &, unsigned short &);
+void operator>>(const LexicalConverter &, unsigned int &);
+void operator>>(const LexicalConverter &, unsigned long &);
+#ifdef __GNUC__
+void operator>>(const LexicalConverter &, long long &);
+void operator>>(const LexicalConverter &, unsigned long long &);
+#endif
+void operator>>(const LexicalConverter &, bool &);
+void operator>>(const LexicalConverter &, float &);
+void operator>>(const LexicalConverter &, double &);
+void operator>>(const LexicalConverter &, long double &);
+void operator>>(const LexicalConverter &, std::string &);
 
-       T tmp;
-       ss>>tmp;
+// Generic operators using stringstream
 
+template<typename T>
+void operator<<(LexicalConverter &c, const T &v)
+{
+       std::ostringstream ss;
+       ss<<c.get_fmt()<<v;
+       c.result(ss.str());
+}
+
+template<typename T>
+void operator>>(const LexicalConverter &c, T &v)
+{
+       std::istringstream ss(c.get());
+       ss.setf(std::ios_base::fmtflags(0), std::ios_base::skipws);
+       ss>>v;
        if(ss.fail() || !ss.eof())
                throw LexicalError("Conversion failure");
-
-       return tmp;
 }
 
-template<>
-inline std::string lexical_cast<std::string>(const std::string &s)
+// The main interface to the lexical conversion machinery
+
+template<typename T>
+inline T lexical_cast(const std::string &s, const Fmt &f=Fmt())
 {
-       return s;
+       LexicalConverter conv(s, f);
+       T result;
+       conv>>result;
+       return result;
 }
 
 template<typename T>
-std::string lexical_cast(const T &v, const Fmt &f=Fmt())
+inline std::string lexical_cast(const T &v, const Fmt &f=Fmt())
 {
-       std::ostringstream ss;
-       ss<<f<<v;
-       return ss.str();
+       LexicalConverter conv(f);
+       conv<<v;
+       return conv.get();
 }
 
 } // namespace Msp