X-Git-Url: http://git.tdb.fi/?p=libs%2Fcore.git;a=blobdiff_plain;f=source%2Fstrings%2Flexicalcast.cpp;fp=source%2Fstrings%2Flexicalcast.cpp;h=5903bb1197d6364382714dd4f1772b477844b919;hp=0000000000000000000000000000000000000000;hb=b42ed73a1b241c0e93ee03c43c4584b41c549bac;hpb=5b1368cb791cab043f0435628cacbaff36e39b7b diff --git a/source/strings/lexicalcast.cpp b/source/strings/lexicalcast.cpp new file mode 100644 index 0000000..5903bb1 --- /dev/null +++ b/source/strings/lexicalcast.cpp @@ -0,0 +1,623 @@ +/* $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.empty()) + throw LexicalError("Empty input in boolean conversion"); + + 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"); +} + + +/*** 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, static_cast(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