blob: cd9de152efba5a027365a2b2b2bfc33dcc4eaddc [file] [log] [blame]
Victor Zverovichb076df42012-12-07 08:31:09 -08001/*
Victor Zverovichfaccb4c2012-12-12 07:44:41 -08002 String formatting library for C++
3
4 Copyright (c) 2012, Victor Zverovich
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9
10 1. Redistributions of source code must retain the above copyright notice, this
11 list of conditions and the following disclaimer.
12 2. Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
17 ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18 WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
19 DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
20 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
21 (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
22 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
23 ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
25 SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Victor Zverovichb076df42012-12-07 08:31:09 -080026 */
27
Victor Zverovichd53f2092012-12-16 15:06:31 -080028// Disable useless MSVC warnings.
Victor Zverovichf8c91062012-12-17 15:41:00 -080029#undef _CRT_SECURE_NO_WARNINGS
Victor Zverovichd53f2092012-12-16 15:06:31 -080030#define _CRT_SECURE_NO_WARNINGS
Victor Zverovichc240a122012-12-21 15:02:25 -080031#undef _SCL_SECURE_NO_WARNINGS
32#define _SCL_SECURE_NO_WARNINGS
Victor Zverovichd53f2092012-12-16 15:06:31 -080033
Victor Zverovichfbfedcf2013-01-14 15:16:20 -080034#include "format.h"
35
Victor Zverovich72f896d2012-12-12 09:17:28 -080036#include <cctype>
Victor Zverovich9ff3b972013-09-07 10:15:08 -070037#include <cmath>
38
39namespace {
40
41#ifndef _MSC_VER
42
43inline int SignBit(double value) {
44 // When compiled in C++11 mode signbit is no longer a macro but a function
45 // defined in namespace std and the macro is undefined.
46 using namespace std;
47 return signbit(value);
48}
49
50inline int IsInf(double x) {
51#ifdef isinf
52 return isinf(x);
53#else
54 return std::isinf(x);
55#endif
56}
57
58#define FMT_SNPRINTF snprintf
59
60#else
61
62inline int SignBit(double value) {
63 if (value < 0) return 1;
64 if (value == value) return 0;
65 int dec = 0, sign = 0;
66 char buffer[2]; // The buffer size must be >= 2 or _ecvt_s will fail.
67 _ecvt_s(buffer, sizeof(buffer), value, 0, &dec, &sign);
68 return sign;
69}
70
71inline int IsInf(double x) { return !_finite(x); }
72
73#define FMT_SNPRINTF sprintf_s
74
75#endif // _MSC_VER
76
77template <typename Char>
78struct CharTraits;
79
80template <>
81struct CharTraits<char> {
82 template <typename T>
83 static int FormatFloat(char *buffer, std::size_t size,
84 const char *format, unsigned width, int precision, T value) {
85 if (width == 0) {
86 return precision < 0 ?
87 FMT_SNPRINTF(buffer, size, format, value) :
88 FMT_SNPRINTF(buffer, size, format, precision, value);
89 }
90 return precision < 0 ?
91 FMT_SNPRINTF(buffer, size, format, width, value) :
92 FMT_SNPRINTF(buffer, size, format, width, precision, value);
93 }
94};
95
96template <>
97struct CharTraits<wchar_t> {
98 template <typename T>
99 static int FormatFloat(wchar_t *buffer, std::size_t size,
100 const wchar_t *format, unsigned width, int precision, T value) {
101 if (width == 0) {
102 return precision < 0 ?
103 swprintf(buffer, size, format, value) :
104 swprintf(buffer, size, format, precision, value);
105 }
106 return precision < 0 ?
107 swprintf(buffer, size, format, width, value) :
108 swprintf(buffer, size, format, width, precision, value);
109 }
110};
Victor Zveroviche8ba9602012-12-12 09:29:50 -0800111
Victor Zverovich93e41252013-09-08 13:07:04 -0700112const char DIGITS[] =
Victor Zverovich687301c2013-01-26 16:07:28 -0800113 "0001020304050607080910111213141516171819"
114 "2021222324252627282930313233343536373839"
115 "4041424344454647484950515253545556575859"
116 "6061626364656667686970717273747576777879"
117 "8081828384858687888990919293949596979899";
Victor Zverovich93e41252013-09-08 13:07:04 -0700118}
Victor Zverovich877abaf2013-01-08 09:56:05 -0800119
Victor Zverovich687301c2013-01-26 16:07:28 -0800120void fmt::internal::ReportUnknownType(char code, const char *type) {
Victor Zverovich877abaf2013-01-08 09:56:05 -0800121 if (std::isprint(static_cast<unsigned char>(code))) {
122 throw fmt::FormatError(fmt::str(
Victor Zverovich687301c2013-01-26 16:07:28 -0800123 fmt::Format("unknown format code '{}' for {}") << code << type));
Victor Zverovich877abaf2013-01-08 09:56:05 -0800124 }
125 throw fmt::FormatError(
Victor Zverovich687301c2013-01-26 16:07:28 -0800126 fmt::str(fmt::Format("unknown format code '\\x{:02x}' for {}")
Victor Zverovich877abaf2013-01-08 09:56:05 -0800127 << static_cast<unsigned>(code) << type));
128}
Victor Zverovich7cae7632013-09-06 20:23:42 -0700129
130
131// Fills the padding around the content and returns the pointer to the
132// content area.
133template <typename Char>
Victor Zverovich93e41252013-09-08 13:07:04 -0700134typename fmt::BasicWriter<Char>::CharPtr
135 fmt::BasicWriter<Char>::FillPadding(CharPtr buffer,
136 unsigned total_size, std::size_t content_size, wchar_t fill) {
Victor Zverovich7cae7632013-09-06 20:23:42 -0700137 std::size_t padding = total_size - content_size;
138 std::size_t left_padding = padding / 2;
139 std::fill_n(buffer, left_padding, fill);
140 buffer += left_padding;
141 CharPtr content = buffer;
142 std::fill_n(buffer + content_size, padding - left_padding, fill);
143 return content;
144}
145
146template <typename Char>
147void fmt::BasicWriter<Char>::FormatDecimal(
148 CharPtr buffer, uint64_t value, unsigned num_digits) {
149 --num_digits;
150 while (value >= 100) {
151 // Integer division is slow so do it for a group of two digits instead
152 // of for every digit. The idea comes from the talk by Alexandrescu
153 // "Three Optimization Tips for C++". See speed-test for a comparison.
154 unsigned index = (value % 100) * 2;
155 value /= 100;
Victor Zverovich93e41252013-09-08 13:07:04 -0700156 buffer[num_digits] = DIGITS[index + 1];
157 buffer[num_digits - 1] = DIGITS[index];
Victor Zverovich7cae7632013-09-06 20:23:42 -0700158 num_digits -= 2;
159 }
160 if (value < 10) {
161 *buffer = static_cast<char>('0' + value);
162 return;
163 }
164 unsigned index = static_cast<unsigned>(value * 2);
Victor Zverovich93e41252013-09-08 13:07:04 -0700165 buffer[1] = DIGITS[index + 1];
166 buffer[0] = DIGITS[index];
Victor Zverovich7cae7632013-09-06 20:23:42 -0700167}
168
169template <typename Char>
170typename fmt::BasicWriter<Char>::CharPtr
171 fmt::BasicWriter<Char>::PrepareFilledBuffer(
172 unsigned size, const AlignSpec &spec, char sign) {
173 unsigned width = spec.width();
174 if (width <= size) {
175 CharPtr p = GrowBuffer(size);
176 *p = sign;
177 return p + size - 1;
178 }
179 CharPtr p = GrowBuffer(width);
180 CharPtr end = p + width;
181 Alignment align = spec.align();
182 if (align == ALIGN_LEFT) {
183 *p = sign;
184 p += size;
185 std::fill(p, end, spec.fill());
186 } else if (align == ALIGN_CENTER) {
187 p = FillPadding(p, width, size, spec.fill());
188 *p = sign;
189 p += size;
190 } else {
191 if (align == ALIGN_NUMERIC) {
192 if (sign) {
193 *p++ = sign;
194 --size;
195 }
196 } else {
197 *(end - size) = sign;
198 }
199 std::fill(p, end - size, spec.fill());
200 p = end;
201 }
202 return p - 1;
203}
204
205template <typename Char>
206template <typename T>
207void fmt::BasicWriter<Char>::FormatDouble(
208 T value, const FormatSpec &spec, int precision) {
209 // Check type.
210 char type = spec.type();
211 bool upper = false;
212 switch (type) {
213 case 0:
214 type = 'g';
215 break;
216 case 'e': case 'f': case 'g':
217 break;
218 case 'F':
219#ifdef _MSC_VER
220 // MSVC's printf doesn't support 'F'.
221 type = 'f';
222#endif
223 // Fall through.
224 case 'E': case 'G':
225 upper = true;
226 break;
227 default:
228 internal::ReportUnknownType(type, "double");
229 break;
230 }
231
232 char sign = 0;
233 // Use SignBit instead of value < 0 because the latter is always
234 // false for NaN.
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700235 if (SignBit(value)) {
Victor Zverovich7cae7632013-09-06 20:23:42 -0700236 sign = '-';
237 value = -value;
238 } else if (spec.sign_flag()) {
239 sign = spec.plus_flag() ? '+' : ' ';
240 }
241
242 if (value != value) {
243 // Format NaN ourselves because sprintf's output is not consistent
244 // across platforms.
245 std::size_t size = 4;
246 const char *nan = upper ? " NAN" : " nan";
247 if (!sign) {
248 --size;
249 ++nan;
250 }
251 CharPtr out = FormatString(nan, size, spec);
252 if (sign)
253 *out = sign;
254 return;
255 }
256
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700257 if (IsInf(value)) {
Victor Zverovich7cae7632013-09-06 20:23:42 -0700258 // Format infinity ourselves because sprintf's output is not consistent
259 // across platforms.
260 std::size_t size = 4;
261 const char *inf = upper ? " INF" : " inf";
262 if (!sign) {
263 --size;
264 ++inf;
265 }
266 CharPtr out = FormatString(inf, size, spec);
267 if (sign)
268 *out = sign;
269 return;
270 }
271
272 std::size_t offset = buffer_.size();
273 unsigned width = spec.width();
274 if (sign) {
275 buffer_.reserve(buffer_.size() + (std::max)(width, 1u));
276 if (width > 0)
277 --width;
278 ++offset;
279 }
280
281 // Build format string.
282 enum { MAX_FORMAT_SIZE = 10}; // longest format: %#-*.*Lg
283 Char format[MAX_FORMAT_SIZE];
284 Char *format_ptr = format;
285 *format_ptr++ = '%';
286 unsigned width_for_sprintf = width;
287 if (spec.hash_flag())
288 *format_ptr++ = '#';
289 if (spec.align() == ALIGN_CENTER) {
290 width_for_sprintf = 0;
291 } else {
292 if (spec.align() == ALIGN_LEFT)
293 *format_ptr++ = '-';
294 if (width != 0)
295 *format_ptr++ = '*';
296 }
297 if (precision >= 0) {
298 *format_ptr++ = '.';
299 *format_ptr++ = '*';
300 }
301 if (internal::IsLongDouble<T>::VALUE)
302 *format_ptr++ = 'L';
303 *format_ptr++ = type;
304 *format_ptr = '\0';
305
306 // Format using snprintf.
Victor Zverovich88972f42013-09-08 13:30:14 -0700307 Char fill = static_cast<Char>(spec.fill());
Victor Zverovich7cae7632013-09-06 20:23:42 -0700308 for (;;) {
309 std::size_t size = buffer_.capacity() - offset;
310 Char *start = &buffer_[offset];
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700311 int n = CharTraits<Char>::FormatFloat(
Victor Zverovich7cae7632013-09-06 20:23:42 -0700312 start, size, format, width_for_sprintf, precision, value);
313 if (n >= 0 && offset + n < buffer_.capacity()) {
314 if (sign) {
315 if ((spec.align() != ALIGN_RIGHT && spec.align() != ALIGN_DEFAULT) ||
316 *start != ' ') {
317 *(start - 1) = sign;
318 sign = 0;
319 } else {
Victor Zverovich88972f42013-09-08 13:30:14 -0700320 *(start - 1) = fill;
Victor Zverovich7cae7632013-09-06 20:23:42 -0700321 }
322 ++n;
323 }
324 if (spec.align() == ALIGN_CENTER &&
325 spec.width() > static_cast<unsigned>(n)) {
326 unsigned width = spec.width();
327 CharPtr p = GrowBuffer(width);
328 std::copy(p, p + n, p + (width - n) / 2);
Victor Zverovich88972f42013-09-08 13:30:14 -0700329 FillPadding(p, spec.width(), n, fill);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700330 return;
331 }
332 if (spec.fill() != ' ' || sign) {
333 while (*start == ' ')
Victor Zverovich88972f42013-09-08 13:30:14 -0700334 *start++ = fill;
Victor Zverovich7cae7632013-09-06 20:23:42 -0700335 if (sign)
336 *(start - 1) = sign;
337 }
338 GrowBuffer(n);
339 return;
340 }
341 buffer_.reserve(n >= 0 ? offset + n + 1 : 2 * buffer_.capacity());
342 }
343}
344
345// Throws Exception(message) if format contains '}', otherwise throws
346// FormatError reporting unmatched '{'. The idea is that unmatched '{'
347// should override other errors.
348template <typename Char>
349void fmt::BasicFormatter<Char>::ReportError(
350 const Char *s, StringRef message) const {
351 for (int num_open_braces = num_open_braces_; *s; ++s) {
352 if (*s == '{') {
353 ++num_open_braces;
354 } else if (*s == '}') {
355 if (--num_open_braces == 0)
356 throw fmt::FormatError(message);
357 }
358 }
359 throw fmt::FormatError("unmatched '{' in format");
360}
361
362// Parses an unsigned integer advancing s to the end of the parsed input.
363// This function assumes that the first character of s is a digit.
364template <typename Char>
365unsigned fmt::BasicFormatter<Char>::ParseUInt(const Char *&s) const {
366 assert('0' <= *s && *s <= '9');
367 unsigned value = 0;
368 do {
369 unsigned new_value = value * 10 + (*s++ - '0');
370 if (new_value < value) // Check if value wrapped around.
371 ReportError(s, "number is too big in format");
372 value = new_value;
373 } while ('0' <= *s && *s <= '9');
374 return value;
375}
376
377template <typename Char>
378inline const typename fmt::BasicFormatter<Char>::Arg
379 &fmt::BasicFormatter<Char>::ParseArgIndex(const Char *&s) {
380 unsigned arg_index = 0;
381 if (*s < '0' || *s > '9') {
382 if (*s != '}' && *s != ':')
383 ReportError(s, "invalid argument index in format string");
384 if (next_arg_index_ < 0) {
385 ReportError(s,
386 "cannot switch from manual to automatic argument indexing");
387 }
388 arg_index = next_arg_index_++;
389 } else {
390 if (next_arg_index_ > 0) {
391 ReportError(s,
392 "cannot switch from automatic to manual argument indexing");
393 }
394 next_arg_index_ = -1;
395 arg_index = ParseUInt(s);
396 }
397 if (arg_index >= args_.size())
398 ReportError(s, "argument index is out of range in format");
399 return *args_[arg_index];
400}
401
402template <typename Char>
403void fmt::BasicFormatter<Char>::CheckSign(const Char *&s, const Arg &arg) {
404 if (arg.type > LAST_NUMERIC_TYPE) {
405 ReportError(s,
406 Format("format specifier '{0}' requires numeric argument") << *s);
407 }
408 if (arg.type == UINT || arg.type == ULONG) {
409 ReportError(s,
410 Format("format specifier '{0}' requires signed argument") << *s);
411 }
412 ++s;
413}
414
415template <typename Char>
416void fmt::BasicFormatter<Char>::DoFormat() {
417 const Char *start = format_;
418 format_ = 0;
419 next_arg_index_ = 0;
420 const Char *s = start;
421 typedef internal::Array<Char, BasicWriter<Char>::INLINE_BUFFER_SIZE> Buffer;
422 BasicWriter<Char> &writer = *writer_;
423 while (*s) {
Victor Zverovich0fc73162013-09-07 12:52:52 -0700424 Char c = *s++;
Victor Zverovich7cae7632013-09-06 20:23:42 -0700425 if (c != '{' && c != '}') continue;
426 if (*s == c) {
427 writer.buffer_.append(start, s);
428 start = ++s;
429 continue;
430 }
431 if (c == '}')
432 throw FormatError("unmatched '}' in format");
433 num_open_braces_= 1;
434 writer.buffer_.append(start, s - 1);
435
436 const Arg &arg = ParseArgIndex(s);
437
438 FormatSpec spec;
439 int precision = -1;
440 if (*s == ':') {
441 ++s;
442
443 // Parse fill and alignment.
Victor Zverovich0fc73162013-09-07 12:52:52 -0700444 if (Char c = *s) {
Victor Zverovich7cae7632013-09-06 20:23:42 -0700445 const Char *p = s + 1;
446 spec.align_ = ALIGN_DEFAULT;
447 do {
448 switch (*p) {
449 case '<':
450 spec.align_ = ALIGN_LEFT;
451 break;
452 case '>':
453 spec.align_ = ALIGN_RIGHT;
454 break;
455 case '=':
456 spec.align_ = ALIGN_NUMERIC;
457 break;
458 case '^':
459 spec.align_ = ALIGN_CENTER;
460 break;
461 }
462 if (spec.align_ != ALIGN_DEFAULT) {
463 if (p != s) {
464 if (c == '}') break;
465 if (c == '{')
466 ReportError(s, "invalid fill character '{'");
467 s += 2;
468 spec.fill_ = c;
469 } else ++s;
470 if (spec.align_ == ALIGN_NUMERIC && arg.type > LAST_NUMERIC_TYPE)
471 ReportError(s, "format specifier '=' requires numeric argument");
472 break;
473 }
474 } while (--p >= s);
475 }
476
477 // Parse sign.
478 switch (*s) {
479 case '+':
480 CheckSign(s, arg);
481 spec.flags_ |= SIGN_FLAG | PLUS_FLAG;
482 break;
483 case '-':
484 CheckSign(s, arg);
485 break;
486 case ' ':
487 CheckSign(s, arg);
488 spec.flags_ |= SIGN_FLAG;
489 break;
490 }
491
492 if (*s == '#') {
493 if (arg.type > LAST_NUMERIC_TYPE)
494 ReportError(s, "format specifier '#' requires numeric argument");
495 spec.flags_ |= HASH_FLAG;
496 ++s;
497 }
498
499 // Parse width and zero flag.
500 if ('0' <= *s && *s <= '9') {
501 if (*s == '0') {
502 if (arg.type > LAST_NUMERIC_TYPE)
503 ReportError(s, "format specifier '0' requires numeric argument");
504 spec.align_ = ALIGN_NUMERIC;
505 spec.fill_ = '0';
506 }
507 // Zero may be parsed again as a part of the width, but it is simpler
508 // and more efficient than checking if the next char is a digit.
509 unsigned value = ParseUInt(s);
510 if (value > INT_MAX)
511 ReportError(s, "number is too big in format");
512 spec.width_ = value;
513 }
514
515 // Parse precision.
516 if (*s == '.') {
517 ++s;
518 precision = 0;
519 if ('0' <= *s && *s <= '9') {
520 unsigned value = ParseUInt(s);
521 if (value > INT_MAX)
522 ReportError(s, "number is too big in format");
523 precision = value;
524 } else if (*s == '{') {
525 ++s;
526 ++num_open_braces_;
527 const Arg &precision_arg = ParseArgIndex(s);
528 unsigned long value = 0;
529 switch (precision_arg.type) {
530 case INT:
531 if (precision_arg.int_value < 0)
532 ReportError(s, "negative precision in format");
533 value = precision_arg.int_value;
534 break;
535 case UINT:
536 value = precision_arg.uint_value;
537 break;
538 case LONG:
539 if (precision_arg.long_value < 0)
540 ReportError(s, "negative precision in format");
541 value = precision_arg.long_value;
542 break;
543 case ULONG:
544 value = precision_arg.ulong_value;
545 break;
546 default:
547 ReportError(s, "precision is not integer");
548 }
549 if (value > INT_MAX)
550 ReportError(s, "number is too big in format");
551 precision = value;
552 if (*s++ != '}')
553 throw FormatError("unmatched '{' in format");
554 --num_open_braces_;
555 } else {
556 ReportError(s, "missing precision in format");
557 }
558 if (arg.type != DOUBLE && arg.type != LONG_DOUBLE) {
559 ReportError(s,
560 "precision specifier requires floating-point argument");
561 }
562 }
563
564 // Parse type.
565 if (*s != '}' && *s)
Victor Zverovich0fc73162013-09-07 12:52:52 -0700566 spec.type_ = static_cast<char>(*s++);
Victor Zverovich7cae7632013-09-06 20:23:42 -0700567 }
568
569 if (*s++ != '}')
570 throw FormatError("unmatched '{' in format");
571 start = s;
572
573 // Format argument.
574 switch (arg.type) {
575 case INT:
576 writer.FormatInt(arg.int_value, spec);
577 break;
578 case UINT:
579 writer.FormatInt(arg.uint_value, spec);
580 break;
581 case LONG:
582 writer.FormatInt(arg.long_value, spec);
583 break;
584 case ULONG:
585 writer.FormatInt(arg.ulong_value, spec);
586 break;
587 case DOUBLE:
588 writer.FormatDouble(arg.double_value, spec, precision);
589 break;
590 case LONG_DOUBLE:
591 writer.FormatDouble(arg.long_double_value, spec, precision);
592 break;
593 case CHAR: {
594 if (spec.type_ && spec.type_ != 'c')
595 internal::ReportUnknownType(spec.type_, "char");
596 typedef typename BasicWriter<Char>::CharPtr CharPtr;
597 CharPtr out = CharPtr();
598 if (spec.width_ > 1) {
599 out = writer.GrowBuffer(spec.width_);
600 if (spec.align_ == ALIGN_RIGHT) {
601 std::fill_n(out, spec.width_ - 1, spec.fill_);
602 out += spec.width_ - 1;
603 } else if (spec.align_ == ALIGN_CENTER) {
604 out = writer.FillPadding(out, spec.width_, 1, spec.fill_);
605 } else {
606 std::fill_n(out + 1, spec.width_ - 1, spec.fill_);
607 }
608 } else {
609 out = writer.GrowBuffer(1);
610 }
611 *out = arg.int_value;
612 break;
613 }
614 case STRING: {
615 if (spec.type_ && spec.type_ != 's')
616 internal::ReportUnknownType(spec.type_, "string");
617 const Char *str = arg.string.value;
618 std::size_t size = arg.string.size;
619 if (size == 0) {
620 if (!str)
621 throw FormatError("string pointer is null");
622 if (*str)
623 size = std::char_traits<Char>::length(str);
624 }
625 writer.FormatString(str, size, spec);
626 break;
627 }
628 case POINTER:
629 if (spec.type_ && spec.type_ != 'p')
630 internal::ReportUnknownType(spec.type_, "pointer");
631 spec.flags_= HASH_FLAG;
632 spec.type_ = 'x';
633 writer.FormatInt(reinterpret_cast<uintptr_t>(arg.pointer_value), spec);
634 break;
635 case CUSTOM:
636 if (spec.type_)
637 internal::ReportUnknownType(spec.type_, "object");
638 arg.custom.format(writer, arg.custom.value, spec);
639 break;
640 default:
641 assert(false);
642 break;
643 }
644 }
645 writer.buffer_.append(start, s);
646}
647
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700648// Explicit instantiations for char.
649
Victor Zverovich7cae7632013-09-06 20:23:42 -0700650template void fmt::BasicWriter<char>::FormatDouble<double>(
651 double value, const FormatSpec &spec, int precision);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700652
Victor Zverovich7cae7632013-09-06 20:23:42 -0700653template void fmt::BasicWriter<char>::FormatDouble<long double>(
654 long double value, const FormatSpec &spec, int precision);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700655
Victor Zverovich93e41252013-09-08 13:07:04 -0700656template fmt::BasicWriter<char>::CharPtr
657 fmt::BasicWriter<char>::FillPadding(CharPtr buffer,
658 unsigned total_size, std::size_t content_size, wchar_t fill);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700659
Victor Zverovich7cae7632013-09-06 20:23:42 -0700660template void fmt::BasicWriter<char>::FormatDecimal(
661 CharPtr buffer, uint64_t value, unsigned num_digits);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700662
Victor Zverovich7cae7632013-09-06 20:23:42 -0700663template fmt::BasicWriter<char>::CharPtr
664 fmt::BasicWriter<char>::PrepareFilledBuffer(
665 unsigned size, const AlignSpec &spec, char sign);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700666
Victor Zverovich7cae7632013-09-06 20:23:42 -0700667template void fmt::BasicFormatter<char>::ReportError(
668 const char *s, StringRef message) const;
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700669
Victor Zverovich7cae7632013-09-06 20:23:42 -0700670template unsigned fmt::BasicFormatter<char>::ParseUInt(const char *&s) const;
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700671
Victor Zverovich7cae7632013-09-06 20:23:42 -0700672template const fmt::BasicFormatter<char>::Arg
673 &fmt::BasicFormatter<char>::ParseArgIndex(const char *&s);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700674
Victor Zverovich7cae7632013-09-06 20:23:42 -0700675template void fmt::BasicFormatter<char>::CheckSign(
676 const char *&s, const Arg &arg);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700677
Victor Zverovich7cae7632013-09-06 20:23:42 -0700678template void fmt::BasicFormatter<char>::DoFormat();
679
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700680// Explicit instantiations for wchar_t.
681
Victor Zverovich7cae7632013-09-06 20:23:42 -0700682template void fmt::BasicWriter<wchar_t>::FormatDouble<double>(
683 double value, const FormatSpec &spec, int precision);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700684
Victor Zverovich7cae7632013-09-06 20:23:42 -0700685template void fmt::BasicWriter<wchar_t>::FormatDouble<long double>(
686 long double value, const FormatSpec &spec, int precision);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700687
Victor Zverovich7cae7632013-09-06 20:23:42 -0700688template fmt::BasicWriter<wchar_t>::CharPtr
Victor Zverovich93e41252013-09-08 13:07:04 -0700689 fmt::BasicWriter<wchar_t>::FillPadding(CharPtr buffer,
690 unsigned total_size, std::size_t content_size, wchar_t fill);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700691
Victor Zverovich7cae7632013-09-06 20:23:42 -0700692template void fmt::BasicWriter<wchar_t>::FormatDecimal(
693 CharPtr buffer, uint64_t value, unsigned num_digits);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700694
Victor Zverovich7cae7632013-09-06 20:23:42 -0700695template fmt::BasicWriter<wchar_t>::CharPtr
696 fmt::BasicWriter<wchar_t>::PrepareFilledBuffer(
697 unsigned size, const AlignSpec &spec, char sign);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700698
Victor Zverovich7cae7632013-09-06 20:23:42 -0700699template void fmt::BasicFormatter<wchar_t>::ReportError(
700 const wchar_t *s, StringRef message) const;
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700701
Victor Zverovich7cae7632013-09-06 20:23:42 -0700702template unsigned fmt::BasicFormatter<wchar_t>::ParseUInt(
703 const wchar_t *&s) const;
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700704
Victor Zverovich7cae7632013-09-06 20:23:42 -0700705template const fmt::BasicFormatter<wchar_t>::Arg
706 &fmt::BasicFormatter<wchar_t>::ParseArgIndex(const wchar_t *&s);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700707
Victor Zverovich7cae7632013-09-06 20:23:42 -0700708template void fmt::BasicFormatter<wchar_t>::CheckSign(
709 const wchar_t *&s, const Arg &arg);
Victor Zverovich9ff3b972013-09-07 10:15:08 -0700710
Victor Zverovich7cae7632013-09-06 20:23:42 -0700711template void fmt::BasicFormatter<wchar_t>::DoFormat();