X-Git-Url: http://git.tdb.fi/?p=libs%2Fcore.git;a=blobdiff_plain;f=source%2Flexicalcast.cpp;fp=source%2Flexicalcast.cpp;h=687a37b4e3ebb85250e699d30c54da9f0e66b7ed;hp=0000000000000000000000000000000000000000;hb=f5aa787e1a715867a8024816ccd58e9a4c7e23a4;hpb=08b94e4156f1444f196667a715bb0375ec97b931 diff --git a/source/lexicalcast.cpp b/source/lexicalcast.cpp new file mode 100644 index 0000000..687a37b --- /dev/null +++ b/source/lexicalcast.cpp @@ -0,0 +1,619 @@ +/* $Id$ + +This file is part of libmspstrings +Copyright © 2006-2008 Mikko Rasa +Distributed under the LGPL +*/ + +#include +#include +#include "lexicalcast.h" + +using namespace std; + +namespace { + +using namespace Msp; + +template +struct IsSigned +{ enum { result=!(static_cast(-1)>0) }; }; + +templatesizeof(unsigned long))> +struct Temporary +{ typedef unsigned long Type; }; + +template +struct Temporary +{ +#ifdef WIN32 + typedef __int64 Type; +#else + typedef unsigned long long Type; +#endif +}; + +/* Helper to avoid warnings about an unsigned type never being < 0 */ +template::result> +struct IsNegative +{ static bool eval(T v) { return v<0; } }; + +template +struct IsNegative +{ 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::result> +struct Absolute +{ static T eval(T v) { return v<0 ? -v : v; } }; + +template +struct Absolute +{ static T eval(T v) { return v; } }; + + +/*** Integer conversions ***/ + +const char udigits[]="0123456789ABCDEF"; +const char ldigits[]="0123456789abcdef"; + +template +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::Type w=Absolute::eval(v); + while(w) + { + *--ptr=digits[w%base]; + w/=base; + } + } + else + *--ptr=digits[0]; + + char sign=(IsNegative::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 +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 +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::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 +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()=10) + { + long double div=1; + while(div*10=static_cast(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(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(i)>=-exp) + { + int digit=static_cast(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 +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(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()(p), c.get_fmt())); } + + +/*** operator>> ***/ + +void operator>>(const LexicalConverter &c, char &v) +{ + if(c.get_fmt().get_type()==Fmt::NUM) + v=str_to_int(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(c.get(), c.get_fmt()); } + +void operator>>(const LexicalConverter &c, short &v) +{ v=str_to_int(c.get(), c.get_fmt()); } + +void operator>>(const LexicalConverter &c, int &v) +{ v=str_to_int(c.get(), c.get_fmt()); } + +void operator>>(const LexicalConverter &c, long &v) +{ v=str_to_int(c.get(), c.get_fmt()); } + +void operator>>(const LexicalConverter &c, unsigned char &v) +{ v=str_to_int(c.get(), c.get_fmt()); } + +void operator>>(const LexicalConverter &c, unsigned short &v) +{ v=str_to_int(c.get(), c.get_fmt()); } + +void operator>>(const LexicalConverter &c, unsigned int &v) +{ v=str_to_int(c.get(), c.get_fmt()); } + +void operator>>(const LexicalConverter &c, unsigned long &v) +{ v=str_to_int(c.get(), c.get_fmt()); } + +#ifdef __GNUC__ +void operator>>(const LexicalConverter &c, long long &v) +{ v=str_to_int(c.get(), c.get_fmt()); } + +void operator>>(const LexicalConverter &c, unsigned long long &v) +{ v=str_to_int(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(c.get(), c.get_fmt()); } + +void operator>>(const LexicalConverter &c, double &v) +{ v=str_to_flt(c.get(), c.get_fmt()); } + +void operator>>(const LexicalConverter &c, long double &v) +{ v=str_to_flt(c.get(), c.get_fmt()); } + +void operator>>(const LexicalConverter &c, string &s) +{ s=str_to_str(c.get(), c.get_fmt()); } + +} // namespace Msp