]> git.tdb.fi Git - libs/core.git/blob - source/strings/lexicalcast.cpp
Throw from LexicalConverter::get if no conversion was performed
[libs/core.git] / source / strings / lexicalcast.cpp
1 #include <cmath>
2 #include <limits>
3 #include <msp/core/inttypes.h>
4 #include "format.h"
5 #include "lexicalcast.h"
6
7 using namespace std;
8
9 namespace {
10
11 using namespace Msp;
12
13 template<typename T>
14 struct IsSigned
15 { enum { result = !(static_cast<T>(-1)>0) }; };
16
17 /* Helper to avoid warnings about an unsigned type never being < 0 */
18 template<typename T, bool f = IsSigned<T>::result>
19 struct IsNegative
20 { static bool eval(T v) { return v<0; } };
21
22 template<typename T>
23 struct IsNegative<T, false>
24 { static bool eval(T) { return false; } };
25
26 /* Helper to avoid errors about ambiguous function calls since there are no
27 overloads of abs for unsigned types */
28 template<typename T, bool f = IsSigned<T>::result>
29 struct Absolute
30 { static T eval(T v) { return v<0 ? -v : v; } };
31
32 template<typename T>
33 struct Absolute<T, false>
34 { static T eval(T v) { return v; } };
35
36
37 /*** Integer conversions ***/
38
39 const char udigits[] = "0123456789ABCDEF";
40 const char ldigits[] = "0123456789abcdef";
41
42 template<typename T>
43 char *int_to_str(T v, const Fmt &f, char *end)
44 {
45         if(f.get_type()==Fmt::CHAR)
46         {
47                 *--end = v;
48                 return end;
49         }
50
51         char *ptr = end;
52
53         // Find out the base to use
54         unsigned base = f.get_base();
55         if(!base)
56                 base = 10;
57
58         // Format the number, starting from the least significant digit
59         const char *digits = (f.get_uppercase() ? udigits : ldigits);
60         if(v)
61         {
62                 typename MatchingInt<T>::UnsignedType w = Absolute<T>::eval(v);
63                 while(w)
64                 {
65                         *--ptr = digits[w%base];
66                         w /= base;
67                 }
68         }
69         else
70                 *--ptr = digits[0];
71
72         char sign = (IsNegative<T>::eval(v) ? '-' : f.get_showpos() ? '+' : 0);
73         if(f.get_fill()=='0')
74         {
75                 /* Zero-fill, taking base/sign size into account.  The expression is a
76                 bit ugly, but saves having to write code for creating the prefix both
77                 ways. */
78                 unsigned pfxsize = ((f.get_showbase() && base!=10) ? base==8 ? 1 : 2 : 0) + (sign!=0);
79                 for(unsigned i=(end-ptr)+pfxsize; i<f.get_width(); ++i)
80                         *--ptr = '0';
81         }
82
83         if(f.get_showbase() && v!=0)
84         {
85                 // Add base indicator
86                 if(base==2)
87                         *--ptr = (f.get_uppercase() ? 'B' : 'b');
88                 else if(base==16)
89                         *--ptr = (f.get_uppercase() ? 'X' : 'x');
90                 if(base!=10)
91                         *--ptr = '0';
92         }
93
94         if(sign)
95                 *--ptr = sign;
96
97         return ptr;
98 }
99
100 template<typename T>
101 string int_to_str(T v, const Fmt &f)
102 {
103         unsigned size = max(f.get_width(), max<unsigned>(f.get_precision(), sizeof(T)*8+3));
104         char *buf = new char[size];
105         string result(int_to_str(v, f, buf+size), buf+size);
106         delete[] buf;
107         return result;
108 }
109
110 template<typename T>
111 T str_to_int(const std::string &s, const Fmt &f)
112 {
113         if(s.empty())
114                 throw lexical_error("conversion of '' to integer");
115
116         std::string::const_iterator i = s.begin();
117
118         // See if the input starts with a sign
119         bool neg = false;
120         if(*i=='-')
121         {
122                 if(!IsSigned<T>::result)
123                         throw lexical_error(format("conversion of '%s' to unsigned integer", s));
124                 neg = true;
125                 ++i;
126         }
127         else if(*i=='+')
128                 ++i;
129
130         // Must have some digits to convert
131         if(i==s.end())
132                 throw lexical_error(format("conversion of '%s' to integer", s));
133
134         T base = f.get_base();
135         if(!base && i!=s.end())
136         {
137                 // Automatic base detection requested, figure it out
138                 if(*i=='0' && ++i!=s.end())
139                 {
140                         if(*i=='x' || *i=='X')
141                         {
142                                 base = 16;
143                                 ++i;
144                         }
145                         else if(*i=='b' || *i=='B')
146                         {
147                                 base = 2;
148                                 ++i;
149                         }
150                         else
151                                 base = 8;
152                 }
153                 else
154                         base = 10;
155         }
156
157         // Parse the digits
158         T result = 0;
159         for(; i!=s.end(); ++i)
160         {
161                 T digit = base;
162                 if(*i>='0' && *i<='9')
163                         digit = *i-'0';
164                 else if(*i>='A' && *i<='F')
165                         digit = *i-'A'+10;
166                 else if(*i>='a' && *i<='f')
167                         digit = *i-'a'+10;
168                 if(digit>=base)
169                         throw lexical_error(format("conversion of '%s' to integer (base-%d)", s, base));
170                 T next = result*base+digit;
171                 if(next/base!=result)
172                         throw lexical_error(format("conversion of '%s' to %d-bit integer", s, sizeof(T)*8));
173                 result = next;
174         }
175
176         if(neg)
177                 result = -result;
178
179         return result;
180 }
181
182
183 /*** Boolean conversions ***/
184
185 string bool_to_str(bool b, const Fmt &f)
186 {
187         if(f.get_type()==Fmt::STR)
188                 return b ? "true" : "false";
189         else
190                 return b ? "1" : "0";
191 }
192
193 bool str_to_bool(const string &s)
194 {
195         if(s.empty())
196                 throw lexical_error("conversion of '' to boolean");
197
198         if(s=="1" || s=="true" || s=="yes" || s=="on")
199                 return true;
200         else if(s=="0" || s=="false" || s=="no" || s=="off")
201                 return false;
202
203         throw lexical_error(format("conversion of '%s' to boolean", s));
204 }
205
206
207 /*** Floating-point conversions ***/
208
209 template<typename T>
210 string flt_to_str(T v, const Fmt &f)
211 {
212         if(f.get_type()==Fmt::CHAR)
213                 throw format_mismatch("floating-point conversion with character format");
214
215         Fmt::FloatMode mode = f.get_floatmode();
216         long double w = abs(v);
217         char sign = (v<0 ? '-' : f.get_showpos() ? '+' : 0);
218
219         // Handle infinity and not-a-number as special cases
220         if(!(w+w>w) && w!=0)
221         {
222                 string result;
223                 if(sign)
224                         result += sign;
225                 if(!(w>=0))
226                         result += (f.get_uppercase() ? "NAN" : "nan");
227                 else
228                         result += (f.get_uppercase() ? "INF" : "inf");
229                 if(result.size()<f.get_width())
230                         result = string(f.get_width()-result.size(), ' ')+result;
231                 return result;
232         }
233
234         /* Find out the base-10 exponent.  Building up the multiplier / divisor
235         first helps with accuracy in some cases. */
236         int exp = 0;
237         if(w>=10)
238         {
239                 long double div = 1;
240                 while(div*10<=w)
241                 {
242                         ++exp;
243                         div *= 10;
244                 }
245                 w /= div;
246         }
247         else if(mode!=Fmt::FIXED && w<1 && w!=0)
248         {
249                 long double mul = 1;
250                 while(w*mul<1)
251                 {
252                         --exp;
253                         mul *= 10;
254                 }
255                 w *= mul;
256         }
257
258         // Decide how to format the number
259         unsigned digits;
260         unsigned leading_zeroes = 0;
261         unsigned point = 1;
262         bool showexp = false;
263         if(mode==Fmt::FIXED)
264         {
265                 point = exp+1;
266                 digits = point+f.get_precision();
267         }
268         else if(mode==Fmt::SCI)
269         {
270                 digits = f.get_precision()+1;
271                 showexp = true;
272         }
273         else
274         {
275                 digits = max(f.get_precision(), 1U);
276                 if(exp<-4 || exp>=static_cast<int>(digits))
277                 {
278                         point = 1;
279                         showexp = true;
280                 }
281                 else
282                 {
283                         if(exp<0)
284                                 leading_zeroes = -exp;
285                         else
286                                 point = exp+1;
287                 }
288         }
289
290         // Apply rounding
291         w += 5.0l/pow(10.0l, static_cast<long double>(digits));
292         if(w>=10)
293         {
294                 // Rounding bumped us to the next exponent, deal with it
295                 w /= 10;
296                 if(mode==Fmt::AUTOFLT && exp+1==static_cast<int>(digits))
297                 {
298                         point = 1;
299                         showexp = true;
300                 }
301                 if(!showexp)
302                 {
303                         if(mode==Fmt::FIXED)
304                                 ++digits;
305                         if(leading_zeroes)
306                                 --leading_zeroes;
307                         else
308                                 ++point;
309                 }
310                 else
311                         ++exp;
312         }
313
314         digits += leading_zeroes;
315
316         // Create a buffer and start from the end
317         unsigned size = max(f.get_width(), digits+8);
318         char *buf = new char[size];
319         char *end = buf+size;
320         char *ptr = end;
321
322         // Format exponent
323         if(showexp)
324         {
325                 ptr = int_to_str(exp, Fmt().showpos().fill('0').width(3), ptr);
326                 *--ptr = (f.get_uppercase() ? 'E' : 'e');
327         }
328
329         // Format mantissa left-to-right
330         char *eptr = ptr;
331         ptr -= digits+(point<digits || f.get_showpoint());
332         char *mptr = ptr;
333         for(unsigned i=0; i<digits; ++i)
334         {
335                 if(i==point)
336                         *mptr++ = '.';
337                 if(!leading_zeroes)
338                 {
339                         int digit = static_cast<int>(w);
340                         *mptr++ = '0'+digit;
341                         w = (w-digit)*10;
342                 }
343                 else
344                 {
345                         *mptr++ = '0';
346                         --leading_zeroes;
347                 }
348         }
349
350         if(f.get_showpoint())
351         {
352                 // Radix point requested but not displayed yet, add it
353                 if(digits<=point)
354                         *mptr++ = '.';
355         }
356         else if(mode==Fmt::AUTOFLT && digits>point)
357         {
358                 // Remove trailing zeroes from fraction and a lone radix point
359                 while(mptr[-1]=='0')
360                         --mptr;
361                 if(mptr[-1]=='.')
362                         --mptr;
363                 if(mptr!=eptr)
364                 {
365                         while(mptr!=ptr)
366                                 *--eptr = *--mptr;
367                         ptr = eptr;
368                 }
369         }
370
371         // Add filling and sign
372         if(f.get_fill()=='0')
373         {
374                 unsigned pfxlen = (sign!=0);
375                 while(end-ptr+pfxlen<f.get_width())
376                         *--ptr = '0';
377         }
378         if(sign)
379                 *--ptr = sign;
380
381         string result(ptr, end);
382         delete[] buf;
383         return result;
384 }
385
386 template<typename T>
387 T str_to_flt(const string &s, const Fmt &)
388 {
389         if(s.empty())
390                 throw lexical_error("conversion of '' to floating-point");
391
392         std::string::const_iterator i = s.begin();
393
394         // See if the input starts with a sign
395         bool neg = false;
396         if(*i=='-')
397         {
398                 neg = true;
399                 ++i;
400         }
401         else if(*i=='+')
402                 ++i;
403
404         // Must have some digits to convert
405         if(i==s.end())
406                 throw lexical_error(format("conversion of '%s' to floating-point", s));
407
408         long double v = 0;
409         int exp = 0;
410
411         // Parse mantissa
412         bool point_seen = false;
413         for(; i!=s.end(); ++i)
414         {
415                 if(*i=='.')
416                 {
417                         if(point_seen)
418                                 throw lexical_error(format("conversion of '%s' to floating-point", s));
419                         point_seen = true;
420                 }
421                 else if(*i>='0' && *i<='9')
422                 {
423                         v = v*10+(*i-'0');
424                         if(point_seen)
425                                 --exp;
426                 }
427                 else if(*i=='e' || *i=='E')
428                 {
429                         // We have an exponent
430                         ++i;
431
432                         exp += str_to_int<int>(string(i, s.end()), Fmt());
433                         // str_to_int has eaten the rest of the input or thrown
434                         break;
435                 }
436                 else
437                         throw lexical_error(format("conversion of '%s' to floating-point", s));
438         }
439
440         // Scale and negate the result as needed
441         while(exp>0)
442         {
443                 v *= 10;
444                 --exp;
445         }
446         while(exp<0)
447         {
448                 v /= 10;
449                 ++exp;
450         }
451
452         if(neg)
453                 v = -v;
454
455         return v;
456 }
457
458
459 /*** String conversions ***/
460
461 string str_to_str(const string &s, const Fmt &f)
462 {
463         if(f.get_type()==Fmt::NUM)
464                 throw format_mismatch("string conversion with numeric format");
465         return s;
466 }
467
468 }
469
470 namespace Msp {
471
472 void LexicalConverter::result(const string &s)
473 {
474         filled = true;
475         if(s.size()<fmt.get_width())
476         {
477                 if(fmt.get_align()==Fmt::RIGHT)
478                         buf = string(fmt.get_width()-s.size(), fmt.get_fill())+s;
479                 else
480                         buf = s+string(fmt.get_width()-s.size(), fmt.get_fill());
481         }
482         else
483                 buf = s;
484 }
485
486 const string &LexicalConverter::get() const
487 {
488         if(!filled)
489                 throw lexical_error("conversion not performed");
490         return buf;
491 }
492
493
494 /*** operator<< ***/
495
496 void operator<<(LexicalConverter &c, char v)
497 {
498         Fmt::Type type = c.get_fmt().get_type();
499         if(type==Fmt::NUM)
500                 c.result(int_to_str(v, c.get_fmt()));
501         else
502                 c.result(string(1, v));
503 }
504
505 void operator<<(LexicalConverter &c, signed char v)
506 { c.result(int_to_str(v, c.get_fmt())); }
507
508 void operator<<(LexicalConverter &c, short v)
509 { c.result(int_to_str(v, c.get_fmt())); }
510
511 void operator<<(LexicalConverter &c, int v)
512 { c.result(int_to_str(v, c.get_fmt())); }
513
514 void operator<<(LexicalConverter &c, long v)
515 { c.result(int_to_str(v, c.get_fmt())); }
516
517 void operator<<(LexicalConverter &c, unsigned char v)
518 { c.result(int_to_str(v, c.get_fmt())); }
519
520 void operator<<(LexicalConverter &c, unsigned short v)
521 { c.result(int_to_str(v, c.get_fmt())); }
522
523 void operator<<(LexicalConverter &c, unsigned v)
524 { c.result(int_to_str(v, c.get_fmt())); }
525
526 void operator<<(LexicalConverter &c, unsigned long v)
527 { c.result(int_to_str(v, c.get_fmt())); }
528
529 #ifdef __GNUC__
530 void operator<<(LexicalConverter &c, long long v)
531 { c.result(int_to_str(v, c.get_fmt())); }
532
533 void operator<<(LexicalConverter &c, unsigned long long v)
534 { c.result(int_to_str(v, c.get_fmt())); }
535 #endif
536
537 void operator<<(LexicalConverter &c, bool v)
538 { c.result(bool_to_str(v, c.get_fmt())); }
539
540 void operator<<(LexicalConverter &c, float v)
541 { c.result(flt_to_str(v, c.get_fmt())); }
542
543 void operator<<(LexicalConverter &c, double v)
544 { c.result(flt_to_str(v, c.get_fmt())); }
545
546 void operator<<(LexicalConverter &c, long double v)
547 { c.result(flt_to_str(v, c.get_fmt())); }
548
549 void operator<<(LexicalConverter &c, const string &s)
550 { c.result(str_to_str(s, c.get_fmt())); }
551
552 void operator<<(LexicalConverter &c, const char *s)
553 { c.result(str_to_str(s, c.get_fmt())); }
554
555 void operator<<(LexicalConverter &c, const void *p)
556 { c.result(int_to_str(reinterpret_cast<IntPtr>(p), c.get_fmt())); }
557
558
559 /*** operator>> ***/
560
561 void operator>>(const LexicalConverter &c, char &v)
562 {
563         if(c.get_fmt().get_type()==Fmt::NUM)
564                 v = str_to_int<char>(c.get(), c.get_fmt());
565         else
566         {
567                 const std::string &s = c.get();
568                 if(s.empty())
569                         throw lexical_error("conversion of '' to character");
570                 if(s.size()>1)
571                         throw lexical_error(format("conversion of '%s' to character", s));
572                 v = s[0];
573         }
574 }
575
576 void operator>>(const LexicalConverter &c, signed char &v)
577 { v = str_to_int<signed char>(c.get(), c.get_fmt()); }
578
579 void operator>>(const LexicalConverter &c, short &v)
580 { v = str_to_int<short>(c.get(), c.get_fmt()); }
581
582 void operator>>(const LexicalConverter &c, int &v)
583 { v = str_to_int<int>(c.get(), c.get_fmt()); }
584
585 void operator>>(const LexicalConverter &c, long &v)
586 { v = str_to_int<long>(c.get(), c.get_fmt()); }
587
588 void operator>>(const LexicalConverter &c, unsigned char &v)
589 { v = str_to_int<unsigned char>(c.get(), c.get_fmt()); }
590
591 void operator>>(const LexicalConverter &c, unsigned short &v)
592 { v = str_to_int<unsigned short>(c.get(), c.get_fmt()); }
593
594 void operator>>(const LexicalConverter &c, unsigned int &v)
595 { v = str_to_int<unsigned int>(c.get(), c.get_fmt()); }
596
597 void operator>>(const LexicalConverter &c, unsigned long &v)
598 { v = str_to_int<unsigned long>(c.get(), c.get_fmt()); }
599
600 #ifdef __GNUC__
601 void operator>>(const LexicalConverter &c, long long &v)
602 { v = str_to_int<long long>(c.get(), c.get_fmt()); }
603
604 void operator>>(const LexicalConverter &c, unsigned long long &v)
605 { v = str_to_int<unsigned long long>(c.get(), c.get_fmt()); }
606 #endif
607
608 void operator>>(const LexicalConverter &c, bool &v)
609 { v = str_to_bool(c.get()); }
610
611 void operator>>(const LexicalConverter &c, float &v)
612 { v = str_to_flt<float>(c.get(), c.get_fmt()); }
613
614 void operator>>(const LexicalConverter &c, double &v)
615 { v = str_to_flt<double>(c.get(), c.get_fmt()); }
616
617 void operator>>(const LexicalConverter &c, long double &v)
618 { v = str_to_flt<long double>(c.get(), c.get_fmt()); }
619
620 void operator>>(const LexicalConverter &c, string &s)
621 { s = str_to_str(c.get(), c.get_fmt()); }
622
623 } // namespace Msp