├── .gitignore ├── Formatters.h ├── LICENSE ├── Printf.h ├── README.md └── Test.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files 2 | *.slo 3 | *.lo 4 | *.o 5 | *.obj 6 | 7 | # Precompiled Headers 8 | *.gch 9 | *.pch 10 | 11 | # Compiled Dynamic libraries 12 | *.so 13 | *.dylib 14 | *.dll 15 | 16 | # Fortran module files 17 | *.mod 18 | *.smod 19 | 20 | # Compiled Static libraries 21 | *.lai 22 | *.la 23 | *.a 24 | *.lib 25 | 26 | # Executables 27 | *.exe 28 | *.out 29 | *.app 30 | -------------------------------------------------------------------------------- /Formatters.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef FORMATTERS_20160922_H_ 3 | #define FORMATTERS_20160922_H_ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | namespace cxx11 { 10 | 11 | // This context writes to a buffer 12 | struct buffer_writer { 13 | 14 | buffer_writer(char *buffer, size_t size) : ptr_(buffer), size_(size) { 15 | } 16 | 17 | void write(char ch) noexcept { 18 | if(size_ > 1) { 19 | *ptr_++ = ch; 20 | --size_; 21 | } 22 | ++written; 23 | } 24 | 25 | void write(const char *p, size_t n) { 26 | size_t count = std::min(size_, n); 27 | memcpy(ptr_, p, count); 28 | size_ -= count; 29 | written += count; 30 | } 31 | 32 | void done() noexcept { 33 | if(size_ != 0) { 34 | *ptr_ = '\0'; 35 | } 36 | } 37 | 38 | char *ptr_; 39 | size_t size_; 40 | size_t written = 0; 41 | }; 42 | 43 | // This context writes to a container using a std::back_inserter 44 | struct ostream_writer { 45 | 46 | ostream_writer(std::ostream &os) : os_(os) { 47 | } 48 | 49 | void write(char ch) { 50 | os_.put(ch); 51 | ++written; 52 | } 53 | 54 | void write(const char *p, size_t n) { 55 | while(n--) { 56 | write(*p++); 57 | } 58 | } 59 | 60 | void done() noexcept {} 61 | 62 | std::ostream &os_; 63 | size_t written = 0; 64 | }; 65 | 66 | // This context writes to a container using a std::back_inserter 67 | template 68 | struct container_writer { 69 | 70 | container_writer(C &s) : it_(std::back_inserter(s)) { 71 | } 72 | 73 | void write(char ch) { 74 | *it_++ = ch; 75 | ++written; 76 | } 77 | 78 | void write(const char *p, size_t n) { 79 | while(n--) { 80 | write(*p++); 81 | } 82 | } 83 | 84 | void done() noexcept {} 85 | 86 | std::back_insert_iterator it_; 87 | size_t written = 0; 88 | }; 89 | 90 | // this context writes to an STDIO stream 91 | struct stdio_writer { 92 | 93 | stdio_writer(FILE *stream) : stream_(stream) { 94 | } 95 | 96 | void write(char ch) noexcept { 97 | putc(ch, stream_); 98 | ++written; 99 | } 100 | 101 | void write(const char *p, size_t n) { 102 | while(n--) { 103 | write(*p++); 104 | } 105 | } 106 | 107 | void done() noexcept {} 108 | 109 | FILE *stream_; 110 | size_t written = 0; 111 | }; 112 | 113 | // this context writes to the stdout stream 114 | struct stdout_writer { 115 | 116 | void write(char ch) noexcept { 117 | putchar(ch); 118 | ++written; 119 | } 120 | 121 | void write(const char *p, size_t n) { 122 | while(n--) { 123 | write(*p++); 124 | } 125 | } 126 | 127 | void done() noexcept {} 128 | 129 | size_t written = 0; 130 | }; 131 | 132 | } 133 | 134 | #endif 135 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | This project is dual licensed under the BSD 3-Clause License and under the Apache License version 2.0 2 | 3 | -------------------------------------------------------------------------------- 4 | BSD 3-Clause License 5 | 6 | Copyright (c) 2019, Evan Teran 7 | All rights reserved. 8 | 9 | Redistribution and use in source and binary forms, with or without 10 | modification, are permitted provided that the following conditions are met: 11 | 12 | * Redistributions of source code must retain the above copyright notice, this 13 | list of conditions and the following disclaimer. 14 | 15 | * Redistributions in binary form must reproduce the above copyright notice, 16 | this list of conditions and the following disclaimer in the documentation 17 | and/or other materials provided with the distribution. 18 | 19 | * Neither the name of the copyright holder nor the names of its 20 | contributors may be used to endorse or promote products derived from 21 | this software without specific prior written permission. 22 | 23 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 24 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 26 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 27 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 29 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 30 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 31 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 32 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 33 | -------------------------------------------------------------------------------- 34 | Copyright 2019 Evan Teran 35 | 36 | Licensed under the Apache License, Version 2.0 (the "License"); 37 | you may not use this file except in compliance with the License. 38 | You may obtain a copy of the License at 39 | 40 | http://www.apache.org/licenses/LICENSE-2.0 41 | 42 | Unless required by applicable law or agreed to in writing, software 43 | distributed under the License is distributed on an "AS IS" BASIS, 44 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 45 | See the License for the specific language governing permissions and 46 | limitations under the License. 47 | -------------------------------------------------------------------------------- /Printf.h: -------------------------------------------------------------------------------- 1 | 2 | #ifndef PRINTF_20160922_H_ 3 | #define PRINTF_20160922_H_ 4 | 5 | #include "Formatters.h" 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #define CXX11_PRINTF_EXTENSIONS 14 | 15 | namespace cxx11 { 16 | 17 | struct format_error : std::runtime_error { 18 | format_error(const char *what_arg) : std::runtime_error(what_arg) { 19 | }; 20 | }; 21 | 22 | namespace detail { 23 | 24 | enum class Modifiers { 25 | MOD_NONE, 26 | MOD_CHAR, 27 | MOD_SHORT, 28 | MOD_LONG, 29 | MOD_LONG_LONG, 30 | MOD_LONG_DOUBLE, 31 | MOD_INTMAX_T, 32 | MOD_SIZE_T, 33 | MOD_PTRDIFF_T 34 | }; 35 | 36 | struct Flags { 37 | uint8_t justify : 1; 38 | uint8_t sign : 1; 39 | uint8_t space : 1; 40 | uint8_t prefix : 1; 41 | uint8_t padding : 1; 42 | uint8_t reserved : 3; 43 | }; 44 | 45 | static_assert(sizeof(Flags) == sizeof(uint8_t), ""); 46 | 47 | // NOTE(eteran): by placing this in a class, it allows us to do things like specialization a lot easier 48 | template 49 | struct itoa_helper; 50 | 51 | template <> 52 | struct itoa_helper<10> { 53 | static constexpr int Divisor = 10; 54 | 55 | public: 56 | //------------------------------------------------------------------------------ 57 | // Name: format 58 | // Desc: returns the value of d as a C-string, formatted based on Divisor, 59 | // and flags. places the length of the resultant string in *rlen 60 | //------------------------------------------------------------------------------ 61 | template 62 | static const char *format(char (&buf)[N], T d, int width, Flags flags, const char *alphabet, size_t *rlen) { 63 | 64 | typename std::make_unsigned::type ud = d; 65 | 66 | char *p = buf + N; 67 | *--p = '\0'; 68 | 69 | // reserve space for leading chars as needed 70 | // and if necessary negate the value in ud 71 | if (d < 0) { 72 | ud = -d; 73 | width -= 1; 74 | } else if (flags.space) { 75 | width -= 1; 76 | } else if (flags.sign) { 77 | width -= 1; 78 | } 79 | 80 | // Divide UD by Divisor until UD == 0. 81 | int digits = 0; 82 | for (; ud; ud /= Divisor) { 83 | const int remainder = (ud % Divisor); 84 | *--p = alphabet[remainder]; 85 | ++digits; 86 | } 87 | 88 | // add in any necessary padding 89 | if (flags.padding) { 90 | while (width-- > digits) { 91 | *--p = '0'; 92 | } 93 | } 94 | 95 | // add the prefix as needed 96 | if (d < 0) { 97 | *--p = '-'; 98 | } else if (flags.space) { 99 | *--p = ' '; 100 | } else if (flags.sign) { 101 | *--p = '+'; 102 | } 103 | 104 | *rlen = (buf + N) - p; 105 | return p; 106 | } 107 | }; 108 | 109 | // Specialization for base 16 so we can make some assumptions 110 | template <> 111 | struct itoa_helper<16> { 112 | static constexpr int Shift = 4; 113 | static constexpr int Mask = 0x0f; 114 | 115 | public: 116 | //------------------------------------------------------------------------------ 117 | // Name: format 118 | // Desc: returns the value of d as a C-string, formatted based on Divisor, 119 | // and flags. places the length of the resultant string in *rlen 120 | //------------------------------------------------------------------------------ 121 | template 122 | static const char *format(char (&buf)[N], T d, int width, Flags flags, const char *alphabet, size_t *rlen) { 123 | 124 | typename std::make_unsigned::type ud = d; 125 | 126 | char *p = buf + N; 127 | *--p = '\0'; 128 | 129 | // add the prefix as needed 130 | if (flags.prefix) { 131 | width -= 2; 132 | } 133 | 134 | // Divide UD by Divisor until UD == 0. 135 | int digits = 0; 136 | for (; ud; ud >>= Shift) { 137 | const int remainder = (ud & Mask); 138 | *--p = alphabet[remainder]; 139 | ++digits; 140 | } 141 | 142 | // add in any necessary padding 143 | if (flags.padding) { 144 | while (width-- > digits) { 145 | *--p = '0'; 146 | } 147 | } 148 | 149 | // add the prefix as needed 150 | if (flags.prefix) { 151 | *--p = alphabet[16]; 152 | *--p = '0'; 153 | } 154 | 155 | *rlen = (buf + N) - p; 156 | return p; 157 | } 158 | }; 159 | 160 | // Specialization for base 8 so we can make some assumptions 161 | template <> 162 | struct itoa_helper<8> { 163 | static constexpr int Shift = 3; 164 | static constexpr int Mask = 0x07; 165 | 166 | public: 167 | //------------------------------------------------------------------------------ 168 | // Name: format 169 | // Desc: returns the value of d as a C-string, formatted based on Divisor, 170 | // and flags. places the length of the resultant string in *rlen 171 | //------------------------------------------------------------------------------ 172 | template 173 | static const char *format(char (&buf)[N], T d, int width, Flags flags, const char *alphabet, size_t *rlen) { 174 | 175 | typename std::make_unsigned::type ud = d; 176 | 177 | char *p = buf + N; 178 | *--p = '\0'; 179 | 180 | // add the prefix as needed 181 | if (flags.prefix) { 182 | width -= 1; 183 | } 184 | 185 | // Divide UD by Divisor until UD == 0. 186 | int digits = 0; 187 | for (; ud; ud >>= Shift) { 188 | const int remainder = (ud & Mask); 189 | *--p = alphabet[remainder]; 190 | ++digits; 191 | } 192 | 193 | // add in any necessary padding 194 | if (flags.padding) { 195 | while (width-- > digits) { 196 | *--p = '0'; 197 | } 198 | } 199 | 200 | // add the prefix as needed 201 | if (flags.prefix) { 202 | *--p = '0'; 203 | } 204 | 205 | *rlen = (buf + N) - p; 206 | return p; 207 | } 208 | }; 209 | 210 | // Specialization for base 2 so we can make some assumptions 211 | template <> 212 | struct itoa_helper<2> { 213 | static constexpr int Shift = 1; 214 | static constexpr int Mask = 0x01; 215 | 216 | public: 217 | //------------------------------------------------------------------------------ 218 | // Name: format 219 | // Desc: returns the value of d as a C-string, formatted based on Divisor, 220 | // and flags. places the length of the resultant string in *rlen 221 | //------------------------------------------------------------------------------ 222 | template 223 | static const char *format(char (&buf)[N], T d, int width, Flags flags, const char *alphabet, size_t *rlen) { 224 | 225 | typename std::make_unsigned::type ud = d; 226 | 227 | char *p = buf + N; 228 | *--p = '\0'; 229 | 230 | // add the prefix as needed 231 | if (flags.prefix) { 232 | width -= 2; 233 | } 234 | 235 | // Divide UD by Divisor until UD == 0. 236 | int digits = 0; 237 | for (; ud; ud >>= Shift) { 238 | const int remainder = (ud & Mask); 239 | *--p = alphabet[remainder]; 240 | ++digits; 241 | } 242 | 243 | // add in any necessary padding 244 | if (flags.padding) { 245 | while (width-- > digits) { 246 | *--p = '0'; 247 | } 248 | } 249 | 250 | // add the prefix as needed 251 | if (flags.prefix) { 252 | *--p = 'b'; 253 | *--p = '0'; 254 | } 255 | 256 | *rlen = (buf + N) - p; 257 | return p; 258 | } 259 | }; 260 | 261 | //------------------------------------------------------------------------------ 262 | // Name: itoa 263 | // Desc: as a minor optimization, let's determine a few things up front and pass 264 | // them as template parameters enabling some more aggressive optimizations 265 | // when the division can use more efficient operations 266 | //------------------------------------------------------------------------------ 267 | template 268 | const char *itoa(char (&buf)[N], char base, int precision, T d, int width, Flags flags, size_t *rlen) { 269 | 270 | if (d == 0 && precision == 0) { 271 | *buf = '\0'; 272 | *rlen = 0; 273 | return buf; 274 | } 275 | 276 | // NOTE(eteran): we include the x/X, here as an easy way to put the 277 | // upper/lower case prefix for hex numbers 278 | static const char alphabet_l[] = "0123456789abcdefx"; 279 | static const char alphabet_u[] = "0123456789ABCDEFX"; 280 | 281 | switch (base) { 282 | case 'i': 283 | case 'd': 284 | case 'u': 285 | return itoa_helper<10>::format(buf, d, width, flags, alphabet_l, rlen); 286 | #ifdef CXX11_PRINTF_EXTENSIONS 287 | case 'b': 288 | return itoa_helper<2>::format(buf, d, width, flags, alphabet_l, rlen); 289 | #endif 290 | case 'X': 291 | return itoa_helper<16>::format(buf, d, width, flags, alphabet_u, rlen); 292 | case 'x': 293 | return itoa_helper<16>::format(buf, d, width, flags, alphabet_l, rlen); 294 | case 'o': 295 | return itoa_helper<8>::format(buf, d, width, flags, alphabet_l, rlen); 296 | default: 297 | return itoa_helper<10>::format(buf, d, width, flags, alphabet_l, rlen); 298 | } 299 | } 300 | 301 | //------------------------------------------------------------------------------ 302 | // Name: output_string 303 | // Desc: prints a string to the Context object, taking into account padding flags 304 | // Note: ch is the current format specifier 305 | //------------------------------------------------------------------------------ 306 | template 307 | void output_string(char ch, const char *s_ptr, int precision, long int width, Flags flags, int len, Context &ctx) { 308 | 309 | if ((ch == 's' && precision >= 0 && precision < len)) { 310 | len = precision; 311 | } 312 | 313 | // if not left justified padding goes first... 314 | if (!flags.justify) { 315 | // spaces go before the prefix... 316 | while (width-- > len) { 317 | ctx.write(' '); 318 | } 319 | } 320 | 321 | // output the string 322 | // NOTE(eteran): len is at most strlen, possible is less 323 | // so we can just loop len times 324 | width -= len; 325 | ctx.write(s_ptr, len); 326 | 327 | // if left justified padding goes last... 328 | if (flags.justify) { 329 | while (width-- > 0) { 330 | ctx.write(' '); 331 | } 332 | } 333 | } 334 | 335 | // NOTE(eteran): Here is some code to fetch arguments of specific types. We also need a few 336 | // default handlers, this code should never really be encountered, but 337 | // but we need it to keep the linker happy. 338 | 339 | #ifdef CXX11_PRINTF_EXTENSIONS 340 | inline std::string formatted_object(std::string obj) { 341 | return obj; 342 | } 343 | 344 | template 345 | std::string to_string(T) { 346 | throw format_error("No to_string found for this object type"); 347 | } 348 | 349 | template 350 | std::string formatted_object(T obj) { 351 | using std::to_string; 352 | using detail::to_string; 353 | return to_string(obj); 354 | } 355 | #endif 356 | 357 | template 358 | const char *formatted_string(T s, typename std::enable_if::value>::type * = 0) { 359 | return s; 360 | } 361 | 362 | template 363 | const char *formatted_string(T s, typename std::enable_if::value>::type * = 0) { 364 | (void)s; 365 | throw format_error("Non-String Argument For String Format"); 366 | } 367 | 368 | template 369 | R formatted_pointer(T p, typename std::enable_if::value>::type * = 0) { 370 | return reinterpret_cast(reinterpret_cast(p)); 371 | } 372 | 373 | template 374 | R formatted_pointer(T p, typename std::enable_if::value>::type * = 0) { 375 | (void)p; 376 | throw format_error("Non-Pointer Argument For Pointer Format"); 377 | } 378 | 379 | template 380 | R formatted_integer(T n, typename std::enable_if::value>::type * = 0) { 381 | return static_cast(n); 382 | } 383 | 384 | template 385 | R formatted_integer(T n, typename std::enable_if::value>::type * = 0) { 386 | (void)n; 387 | throw format_error("Non-Integer Argument For Integer Format"); 388 | } 389 | 390 | //------------------------------------------------------------------------------ 391 | // Name: process_format 392 | // Desc: default handler that should never be called at runtime 393 | //------------------------------------------------------------------------------ 394 | template 395 | int process_format(Context &ctx, const char *format, Flags flags, long int width, long int precision, Modifiers modifier) { 396 | (void)ctx; 397 | (void)format; 398 | (void)flags; 399 | (void)width; 400 | (void)precision; 401 | (void)modifier; 402 | throw format_error("Should Never Happen"); 403 | } 404 | 405 | //------------------------------------------------------------------------------ 406 | // Name: get_modifier 407 | // Desc: default handler that should never be called at runtime 408 | //------------------------------------------------------------------------------ 409 | template 410 | int get_modifier(Context &ctx, const char *format, Flags flags, long int width, long int precision) { 411 | (void)ctx; 412 | (void)format; 413 | (void)flags; 414 | (void)width; 415 | (void)precision; 416 | throw format_error("Should Never Happen"); 417 | } 418 | 419 | //------------------------------------------------------------------------------ 420 | // Name: get_precision 421 | // Desc: default handler that should never be called at runtime 422 | //------------------------------------------------------------------------------ 423 | template 424 | int get_precision(Context &ctx, const char *format, Flags flags, long int width) { 425 | (void)ctx; 426 | (void)format; 427 | (void)flags; 428 | (void)width; 429 | throw format_error("Should Never Happen"); 430 | } 431 | 432 | //------------------------------------------------------------------------------ 433 | // Name: process_format 434 | // Desc: prints the next argument to the Context taking into account the flags, 435 | // width, precision, and modifiers collected along the way. Then will 436 | // recursively continue processing the string 437 | //------------------------------------------------------------------------------ 438 | template 439 | int process_format(Context &ctx, const char *format, Flags flags, long int width, long int precision, Modifiers modifier, const T &arg, const Ts &... ts) { 440 | 441 | // enough to contain a 64-bit number in bin notation + optional prefix 442 | char num_buf[67]; 443 | 444 | size_t slen; 445 | const char *s_ptr = nullptr; 446 | 447 | char ch = *format; 448 | switch (ch) { 449 | case 'e': 450 | case 'E': 451 | case 'f': 452 | case 'F': 453 | case 'a': 454 | case 'A': 455 | case 'g': 456 | case 'G': 457 | // TODO(eteran): implement float formatting... for now, just consume the argument 458 | return Printf(ctx, format + 1, ts...); 459 | 460 | case 'p': 461 | precision = 1; 462 | ch = 'x'; 463 | flags.prefix = 1; 464 | // NOTE(eteran): GNU printf prints "(nil)" for NULL pointers, we print 0x0 465 | s_ptr = itoa(num_buf, ch, precision, formatted_pointer(arg), width, flags, &slen); 466 | 467 | output_string(ch, s_ptr, precision, width, flags, slen, ctx); 468 | return Printf(ctx, format + 1, ts...); 469 | 470 | case 'x': 471 | case 'X': 472 | case 'u': 473 | case 'o': 474 | #ifdef CXX11_PRINTF_EXTENSIONS 475 | case 'b': // extension, BINARY mode 476 | #endif 477 | if (precision < 0) { 478 | precision = 1; 479 | } 480 | 481 | switch (modifier) { 482 | case Modifiers::MOD_CHAR: 483 | s_ptr = itoa(num_buf, ch, precision, formatted_integer(arg), width, flags, &slen); 484 | break; 485 | case Modifiers::MOD_SHORT: 486 | s_ptr = itoa(num_buf, ch, precision, formatted_integer(arg), width, flags, &slen); 487 | break; 488 | case Modifiers::MOD_LONG: 489 | s_ptr = itoa(num_buf, ch, precision, formatted_integer(arg), width, flags, &slen); 490 | break; 491 | case Modifiers::MOD_LONG_LONG: 492 | s_ptr = itoa(num_buf, ch, precision, formatted_integer(arg), width, flags, &slen); 493 | break; 494 | case Modifiers::MOD_INTMAX_T: 495 | s_ptr = itoa(num_buf, ch, precision, formatted_integer(arg), width, flags, &slen); 496 | break; 497 | case Modifiers::MOD_SIZE_T: 498 | s_ptr = itoa(num_buf, ch, precision, formatted_integer(arg), width, flags, &slen); 499 | break; 500 | case Modifiers::MOD_PTRDIFF_T: 501 | s_ptr = itoa(num_buf, ch, precision, formatted_integer::type>(arg), width, flags, &slen); 502 | break; 503 | default: 504 | s_ptr = itoa(num_buf, ch, precision, formatted_integer(arg), width, flags, &slen); 505 | break; 506 | } 507 | 508 | output_string(ch, s_ptr, precision, width, flags, slen, ctx); 509 | return Printf(ctx, format + 1, ts...); 510 | 511 | case 'i': 512 | case 'd': 513 | if (precision < 0) { 514 | precision = 1; 515 | } 516 | 517 | switch (modifier) { 518 | case Modifiers::MOD_CHAR: 519 | s_ptr = itoa(num_buf, ch, precision, formatted_integer(arg), width, flags, &slen); 520 | break; 521 | case Modifiers::MOD_SHORT: 522 | s_ptr = itoa(num_buf, ch, precision, formatted_integer(arg), width, flags, &slen); 523 | break; 524 | case Modifiers::MOD_LONG: 525 | s_ptr = itoa(num_buf, ch, precision, formatted_integer(arg), width, flags, &slen); 526 | break; 527 | case Modifiers::MOD_LONG_LONG: 528 | s_ptr = itoa(num_buf, ch, precision, formatted_integer(arg), width, flags, &slen); 529 | break; 530 | case Modifiers::MOD_INTMAX_T: 531 | s_ptr = itoa(num_buf, ch, precision, formatted_integer(arg), width, flags, &slen); 532 | break; 533 | case Modifiers::MOD_SIZE_T: 534 | s_ptr = itoa(num_buf, ch, precision, formatted_integer::type>(arg), width, flags, &slen); 535 | break; 536 | case Modifiers::MOD_PTRDIFF_T: 537 | s_ptr = itoa(num_buf, ch, precision, formatted_integer(arg), width, flags, &slen); 538 | break; 539 | default: 540 | s_ptr = itoa(num_buf, ch, precision, formatted_integer(arg), width, flags, &slen); 541 | break; 542 | } 543 | 544 | output_string(ch, s_ptr, precision, width, flags, slen, ctx); 545 | return Printf(ctx, format + 1, ts...); 546 | 547 | case 'c': 548 | // char is promoted to an int when pushed on the stack 549 | num_buf[0] = formatted_integer(arg); 550 | num_buf[1] = '\0'; 551 | s_ptr = num_buf; 552 | output_string('c', s_ptr, precision, width, flags, 1, ctx); 553 | return Printf(ctx, format + 1, ts...); 554 | 555 | case 's': 556 | s_ptr = formatted_string(arg); 557 | if (!s_ptr) { 558 | s_ptr = "(null)"; 559 | } 560 | output_string('s', s_ptr, precision, width, flags, strlen(s_ptr), ctx); 561 | return Printf(ctx, format + 1, ts...); 562 | 563 | #ifdef CXX11_PRINTF_EXTENSIONS 564 | case '?': { 565 | std::string s = formatted_object(arg); 566 | output_string('s', s.data(), precision, width, flags, s.size(), ctx); 567 | } 568 | return Printf(ctx, format + 1, ts...); 569 | #endif 570 | 571 | case 'n': 572 | switch (modifier) { 573 | case Modifiers::MOD_CHAR: 574 | *formatted_pointer(arg) = ctx.written; 575 | break; 576 | case Modifiers::MOD_SHORT: 577 | *formatted_pointer(arg) = ctx.written; 578 | break; 579 | case Modifiers::MOD_LONG: 580 | *formatted_pointer(arg) = ctx.written; 581 | break; 582 | case Modifiers::MOD_LONG_LONG: 583 | *formatted_pointer(arg) = ctx.written; 584 | break; 585 | case Modifiers::MOD_INTMAX_T: 586 | *formatted_pointer(arg) = ctx.written; 587 | break; 588 | case Modifiers::MOD_SIZE_T: 589 | *formatted_pointer::type *>(arg) = ctx.written; 590 | break; 591 | case Modifiers::MOD_PTRDIFF_T: 592 | *formatted_pointer(arg) = ctx.written; 593 | break; 594 | default: 595 | *formatted_pointer(arg) = ctx.written; 596 | break; 597 | } 598 | 599 | return Printf(ctx, format + 1, ts...); 600 | 601 | default: 602 | ctx.write('%'); 603 | // FALL THROUGH 604 | case '\0': 605 | case '%': 606 | ctx.write(ch); 607 | break; 608 | } 609 | 610 | return Printf(ctx, format + 1, ts...); 611 | } 612 | 613 | //------------------------------------------------------------------------------ 614 | // Name: get_modifier 615 | // Desc: gets the modifier, if any, from the format string, then calls 616 | // process_format 617 | //------------------------------------------------------------------------------ 618 | template 619 | int get_modifier(Context &ctx, const char *format, Flags flags, long int width, long int precision, const T &arg, const Ts &... ts) { 620 | 621 | 622 | Modifiers modifier = Modifiers::MOD_NONE; 623 | 624 | switch (*format) { 625 | case 'h': 626 | modifier = Modifiers::MOD_SHORT; 627 | ++format; 628 | if (*format == 'h') { 629 | modifier = Modifiers::MOD_CHAR; 630 | ++format; 631 | } 632 | break; 633 | case 'l': 634 | modifier = Modifiers::MOD_LONG; 635 | ++format; 636 | if (*format == 'l') { 637 | modifier = Modifiers::MOD_LONG_LONG; 638 | ++format; 639 | } 640 | break; 641 | case 'L': 642 | modifier = Modifiers::MOD_LONG_DOUBLE; 643 | ++format; 644 | break; 645 | case 'j': 646 | modifier = Modifiers::MOD_INTMAX_T; 647 | ++format; 648 | break; 649 | case 'z': 650 | modifier = Modifiers::MOD_SIZE_T; 651 | ++format; 652 | break; 653 | case 't': 654 | modifier = Modifiers::MOD_PTRDIFF_T; 655 | ++format; 656 | break; 657 | default: 658 | break; 659 | } 660 | 661 | return process_format(ctx, format, flags, width, precision, modifier, arg, ts...); 662 | } 663 | 664 | //------------------------------------------------------------------------------ 665 | // Name: get_precision 666 | // Desc: gets the precision, if any, either from the format string or as an arg 667 | // as needed, then calls get_modifier 668 | //------------------------------------------------------------------------------ 669 | template 670 | int get_precision(Context &ctx, const char *format, Flags flags, long int width, const T &arg, const Ts &... ts) { 671 | 672 | // default to non-existant 673 | long int p = -1; 674 | 675 | if (*format == '.') { 676 | 677 | ++format; 678 | if (*format == '*') { 679 | ++format; 680 | // pull an int off the stack for processing 681 | p = formatted_integer(arg); 682 | return get_modifier(ctx, format, flags, width, p, ts...); 683 | } else { 684 | char *endptr; 685 | p = strtol(format, &endptr, 10); 686 | format = endptr; 687 | return get_modifier(ctx, format, flags, width, p, arg, ts...); 688 | } 689 | } 690 | 691 | return get_modifier(ctx, format, flags, width, p, arg, ts...); 692 | } 693 | 694 | //------------------------------------------------------------------------------ 695 | // Name: get_width 696 | // Desc: gets the width if any, either from the format string or as an arg as 697 | // needed, then calls get_precision 698 | //------------------------------------------------------------------------------ 699 | template 700 | int get_width(Context &ctx, const char *format, Flags flags, const T &arg, const Ts &... ts) { 701 | 702 | int width = 0; 703 | 704 | if (*format == '*') { 705 | ++format; 706 | // pull an int off the stack for processing 707 | width = formatted_integer(arg); 708 | 709 | return get_precision(ctx, format, flags, width, ts...); 710 | } else { 711 | char *endptr; 712 | width = strtol(format, &endptr, 10); 713 | format = endptr; 714 | 715 | return get_precision(ctx, format, flags, width, arg, ts...); 716 | } 717 | } 718 | 719 | //------------------------------------------------------------------------------ 720 | // Name: get_flags 721 | // Desc: gets the flags, if any, from the format string, then calls get_width 722 | //------------------------------------------------------------------------------ 723 | template 724 | int get_flags(Context &ctx, const char *format, const Ts &... ts) { 725 | 726 | Flags f = {0, 0, 0, 0, 0, 0}; 727 | bool done = false; 728 | 729 | // skip past the % char 730 | ++format; 731 | 732 | while (!done) { 733 | 734 | char ch = *format++; 735 | 736 | switch (ch) { 737 | case '-': 738 | // justify, overrides padding 739 | f.justify = 1; 740 | f.padding = 0; 741 | break; 742 | case '+': 743 | // sign, overrides space 744 | f.sign = 1; 745 | f.space = 0; 746 | break; 747 | case ' ': 748 | if (!f.sign) { 749 | f.space = 1; 750 | } 751 | break; 752 | case '#': 753 | f.prefix = 1; 754 | break; 755 | case '0': 756 | if (!f.justify) { 757 | f.padding = 1; 758 | } 759 | break; 760 | default: 761 | done = true; 762 | --format; 763 | } 764 | } 765 | 766 | return get_width(ctx, format, f, ts...); 767 | } 768 | } 769 | 770 | //------------------------------------------------------------------------------ 771 | // Name: Printf 772 | // Desc: 0 argument version of Printf. Asserts on any format character found 773 | //------------------------------------------------------------------------------ 774 | template 775 | int Printf(Context &ctx, const char *format) { 776 | 777 | assert(format); 778 | 779 | for (; *format; ++format) { 780 | if (*format != '%' || *++format == '%') { 781 | ctx.write(*format); 782 | continue; 783 | } 784 | 785 | throw format_error("Bad Format"); 786 | } 787 | 788 | // this will usually null terminate the string 789 | ctx.done(); 790 | 791 | // return the amount of bytes that should have been written if there was sufficient space 792 | return ctx.written; 793 | } 794 | 795 | //------------------------------------------------------------------------------ 796 | // Name: Printf 797 | // Desc: 1+ argument version of Printf. 798 | //------------------------------------------------------------------------------ 799 | template 800 | int Printf(Context &ctx, const char *format, const Ts &... ts) { 801 | 802 | assert(format); 803 | 804 | while (*format != '\0') { 805 | if (*format == '%') { 806 | // %[flag][width][.precision][length]char 807 | 808 | // this recurses into get_width -> get_precision -> get_length -> process_format 809 | return detail::get_flags(ctx, format, ts...); 810 | } else { 811 | ctx.write(*format); 812 | } 813 | 814 | ++format; 815 | } 816 | 817 | // clean up any trailing stuff 818 | return Printf(ctx, format + 1, ts...); 819 | } 820 | 821 | //------------------------------------------------------------------------------ 822 | // Name: snprintf 823 | // Desc: implementation of what snprintf compatible interface 824 | //------------------------------------------------------------------------------ 825 | template 826 | int sprintf(std::ostream &os, const char *format, const Ts &... ts) { 827 | ostream_writer ctx(os); 828 | return Printf(ctx, format, ts...); 829 | } 830 | 831 | //------------------------------------------------------------------------------ 832 | // Name: sprintf 833 | // Desc: implementation of what s[n]printf compatible interface 834 | //------------------------------------------------------------------------------ 835 | template 836 | int sprintf(char *str, size_t size, const char *format, const Ts &... ts) { 837 | buffer_writer ctx(str, size); 838 | return Printf(ctx, format, ts...); 839 | } 840 | 841 | //------------------------------------------------------------------------------ 842 | // Name: printf 843 | // Desc: implementation of what printf compatible interface 844 | //------------------------------------------------------------------------------ 845 | template 846 | int printf(const char *format, const Ts &... ts) { 847 | stdout_writer ctx; 848 | return Printf(ctx, format, ts...); 849 | } 850 | } 851 | 852 | #endif 853 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/eteran/cxx11_printf/151b72901c6e105affe217d3fe68f8b9d0817947/README.md -------------------------------------------------------------------------------- /Test.cpp: -------------------------------------------------------------------------------- 1 | 2 | #include "Printf.h" 3 | 4 | #include 5 | #include 6 | 7 | template 8 | R time_code(F func) { 9 | 10 | // test the timing of our printf 11 | auto then = std::chrono::system_clock::now(); 12 | 13 | for (int i = 0; i < Count; ++i) { 14 | func(); 15 | } 16 | 17 | auto now = std::chrono::system_clock::now(); 18 | auto dur = now - then; 19 | 20 | return std::chrono::duration_cast(dur); 21 | } 22 | 23 | #ifdef CXX11_PRINTF_EXTENSIONS 24 | class Test {}; 25 | 26 | std::string to_string(Test) { 27 | return "Test!"; 28 | } 29 | #endif 30 | 31 | int main() { 32 | 33 | int Foo = 1234; 34 | 35 | // first test correctness 36 | cxx11::printf("hello %*s, %c, %d, %08x %p %016u %02x %016o\n", 10, "world", 0x41, -123, 0x1234, static_cast(&Foo), -4, -1, 1234); 37 | printf("hello %*s, %c, %d, %08x %p %016u %02x %016o\n", 10, "world", 0x41, -123, 0x1234, static_cast(&Foo), -4, -1, 1234); 38 | 39 | typedef std::chrono::microseconds ms; 40 | 41 | constexpr int count = 1000000; 42 | 43 | auto time1 = time_code([&Foo]() { 44 | char buf[128]; 45 | cxx11::sprintf(buf, sizeof(buf), "hello %*s, %c, %d, %08x %p %016u %02x %016o\n", 10, "world", 0x41, -123, 0x1234, static_cast(&Foo), -4, -1, 1234); 46 | }); 47 | 48 | auto time2 = time_code([&Foo]() { 49 | char buf[128]; 50 | snprintf(buf, sizeof(buf), "hello %*s, %c, %d, %08x %p %016u %02x %016o\n", 10, "world", 0x41, -123, 0x1234, static_cast(&Foo), -4, -1, 1234); 51 | }); 52 | 53 | std::cerr << "First Took: " << time1.count() << " \xC2\xB5s to execute." << std::endl; 54 | std::cerr << "Second Took: " << time2.count() << " \xC2\xB5s to execute." << std::endl; 55 | 56 | #ifdef CXX11_PRINTF_EXTENSIONS 57 | { 58 | std::string s = "[std::string]!"; 59 | cxx11::sprintf(std::cout, "hello %10? %?\n", s, Test()); 60 | 61 | cxx11::printf("%032b\n", 5ul); 62 | cxx11::printf("%032b\n", 1234ul); 63 | } 64 | #endif 65 | } 66 | --------------------------------------------------------------------------------