SafeFormat.h

00001 
00002 // Copyright (c) 2005 by Andrei Alexandrescu
00003 // Copyright (c) 2006 Peter Kümmel
00004 // Permission to use, copy, modify, distribute, and sell this software for any
00005 //     purpose is hereby granted without fee, provided that the above copyright
00006 //     notice appear in all copies and that both that copyright notice and this
00007 //     permission notice appear in supporting documentation.
00008 // The author makes no representations about the suitability of this software 
00009 //     for any purpose. It is provided "as is" without express or implied 
00010 //     warranty.
00012 #ifndef LOKI_SAFEFORMAT_INC_
00013 #define LOKI_SAFEFORMAT_INC_
00014 
00015 // $Id: SafeFormat.h 747 2006-10-17 19:48:40Z syntheticpp $
00016 
00017 
00019 // This file contains definitions for SafePrintf. SafeScanf coming soon (the 
00020 //   design is similar). 
00021 // See Alexandrescu, Andrei: Type-safe Formatting, C/C++ Users Journal, Aug 2005
00023 
00024 #include <cstdio>
00025 #include <string>
00026 #include <stdexcept>
00027 #include <utility>
00028 #include <cassert>
00029 #include <locale>
00030 #include <iostream>
00031 
00032 #include <loki/LokiExport.h>
00033 
00034 
00035 // long is 32 bit on 64-bit Windows!
00036 // intptr_t used to get 64 bit on Win64
00037 #if defined(_WIN32) || defined(_WIN64)
00038 #  define LOKI_SAFEFORMAT_SIGNED_LONG intptr_t
00039 #  define LOKI_SAFEFORMAT_UNSIGNED_LONG uintptr_t
00040 #else
00041 #  define LOKI_SAFEFORMAT_SIGNED_LONG signed long
00042 #  define LOKI_SAFEFORMAT_UNSIGNED_LONG unsigned long
00043 #endif
00044 
00045 // Windows headers could have min/max defined
00046 #ifdef max 
00047 #  undef max 
00048 #endif 
00049 #ifdef min 
00050 #  undef min 
00051 #endif 
00052 
00053 namespace Loki
00054 {
00055 
00056     // Crude writing method: writes straight to the file, unbuffered
00057     // Must be combined with a buffer to work properly (and efficiently)
00058     LOKI_EXPORT
00059     void write(std::FILE* f, const char* from, const char* to);
00060 
00061     // Write to an ostream
00062     LOKI_EXPORT
00063     void write(std::ostream& f, const char* from, const char* to);
00064 
00065     // Write to a string
00066     LOKI_EXPORT
00067     void write(std::string& s, const char* from, const char* to);
00068 
00069     // Write to a fixed-size buffer
00070     template <class Char>
00071     void write(std::pair<Char*, std::size_t>& s, const Char* from, const Char* to) {
00072         assert(from <= to);
00073         if(from + s.second < to)
00074             throw std::overflow_error("");
00075         // s.first: position one past the final copied element 
00076         s.first = std::copy(from, to, s.first);
00077         // remaining buffer size
00078         s.second -= to - from;
00079     }
00080 
00082     // PrintfState class template
00083     // Holds the formatting state, and implements operator() to format stuff
00084     // Todo: make sure errors are handled properly
00086 
00087     template <class Device, class Char>
00088     struct PrintfState {
00089         PrintfState(Device dev, const Char * format) 
00090                 : device_(dev)
00091                 , format_(format)
00092                 , width_(0)
00093                 , prec_(0)
00094                 , flags_(0)
00095                 , result_(0) {
00096             Advance();
00097         }
00098         
00099         ~PrintfState() {
00100         }
00101 
00102         #define LOKI_PRINTF_STATE_FORWARD(type) \
00103             PrintfState& operator()(type par) {\
00104                 return (*this)(static_cast< LOKI_SAFEFORMAT_UNSIGNED_LONG >(par)); \
00105             }
00106 
00107         LOKI_PRINTF_STATE_FORWARD(bool)
00108         LOKI_PRINTF_STATE_FORWARD(char)
00109         LOKI_PRINTF_STATE_FORWARD(signed char)
00110         LOKI_PRINTF_STATE_FORWARD(unsigned char)
00111         LOKI_PRINTF_STATE_FORWARD(signed short)
00112         LOKI_PRINTF_STATE_FORWARD(unsigned short)
00113         LOKI_PRINTF_STATE_FORWARD(signed int)
00114         LOKI_PRINTF_STATE_FORWARD(signed long)
00115 #if (defined(_WIN32) || defined(_WIN64))
00116         LOKI_PRINTF_STATE_FORWARD(unsigned long)
00117 #else
00118         // on Windows already defined by uintptr_t
00119         LOKI_PRINTF_STATE_FORWARD(unsigned int)
00120 #endif
00121 
00122         // Print (or gobble in case of the "*" specifier) an int
00123         PrintfState& operator()(LOKI_SAFEFORMAT_UNSIGNED_LONG i) {
00124             if (result_ == -1) return *this; // don't even bother
00125             // % [flags] [width] [.prec] [modifier] type_char
00126             // Fetch the flags 
00127             ReadFlags();
00128             if (*format_ == '*') {
00129                 // read the width and get out
00130                 SetWidth(static_cast<size_t>(i));
00131                 ++format_;
00132                 return *this;
00133             }
00134             ReadWidth();
00135             // precision
00136             if (*format_ == '.') {
00137                 // deal with precision
00138                 if (format_[1] == '*') {
00139                     // read the precision and get out
00140                     SetPrec(static_cast<size_t>(i));
00141                     format_ += 2;
00142                     return *this;
00143                 }
00144                 ReadPrecision();
00145             }
00146             ReadModifiers();
00147             // input size modifier
00148             if (ForceShort()) {
00149                 // short int
00150                 const Char c = *format_;
00151                 if (c == 'x' || c == 'X' || c == 'u' || c == 'o') {
00152                     i = static_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(static_cast<unsigned short>(i));
00153                 }
00154             }
00155             FormatWithCurrentFlags(i);
00156             return *this;
00157         }
00158 
00159         PrintfState& operator()(void* n) {
00160             if (result_ == -1) return *this; // don't even bother
00161             PrintUsing_snprintf(n,"p");
00162             return *this;
00163         }
00164         
00165         PrintfState& operator()(double n) {
00166             if (result_ == -1) return *this; // don't even bother
00167             PrintUsing_snprintf(n,"eEfgG");
00168             return *this;
00169         }
00170 
00171         PrintfState& operator()(long double n) {
00172             if (result_ == -1) return *this; // don't even bother
00173             PrintUsing_snprintf(n,"eEfgG");
00174             return *this;
00175         }
00176 
00177         // Store the number of characters printed so far
00178         PrintfState& operator()(int * pi) {
00179             return StoreCountHelper(pi);
00180         }
00181         
00182         // Store the number of characters printed so far
00183         PrintfState& operator()(short * pi) {
00184             return StoreCountHelper(pi);
00185         }
00186         
00187         // Store the number of characters printed so far
00188         PrintfState& operator()(long * pi) {
00189             return StoreCountHelper(pi);
00190         }
00191         
00192         PrintfState& operator()(const std::string& stdstr) {
00193             return operator()(stdstr.c_str());
00194         }
00195 
00196         PrintfState& operator()(const char *const s) {
00197             if (result_ == -1) return *this;
00198             ReadLeaders();
00199             const char fmt = *format_;
00200             if (fmt == 'p') {
00201                 FormatWithCurrentFlags(reinterpret_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(s));
00202                 return *this;
00203             }
00204             if (fmt != 's') {
00205                 result_ = -1;
00206                 return *this;
00207             }
00208             const size_t len = std::min(strlen(s), prec_);
00209             if (width_ > len) {
00210                 if (LeftJustify()) {
00211                     Write(s, s + len);
00212                     Fill(' ', width_ - len);
00213                 } else {
00214                     Fill(' ', width_ - len);
00215                     Write(s, s + len);
00216                 }
00217             } else {
00218                 Write(s, s + len);
00219             }
00220             Next();
00221             return *this;
00222         }
00223         
00224         PrintfState& operator()(const void *const p) {
00225             return (*this)(reinterpret_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(p));
00226         }
00227         
00228         // read the result
00229         operator int() const {
00230             return static_cast<int>(result_);
00231         }
00232         
00233     private:
00234         PrintfState& operator=(const PrintfState&);
00235         template <typename T>
00236         PrintfState& StoreCountHelper(T *const pi) {
00237             if (result_ == -1) return *this; // don't even bother
00238             ReadLeaders();
00239             const char fmt = *format_;
00240             if (fmt == 'p') { // pointer
00241                 FormatWithCurrentFlags(reinterpret_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(pi));
00242                 return *this;
00243             }
00244             if (fmt != 'n') {
00245                 result_ = -1;
00246                 return *this;
00247             }
00248             assert(pi != 0);
00249             *pi = result_;
00250             Next();
00251             return *this;
00252         }
00253 
00254         void FormatWithCurrentFlags(const LOKI_SAFEFORMAT_UNSIGNED_LONG i) {
00255             // look at the format character
00256             Char formatChar = *format_;
00257             bool isSigned = formatChar == 'd' || formatChar == 'i';
00258             if (formatChar == 'p') {
00259                 formatChar = 'x'; // pointers go to hex
00260                 SetAlternateForm(); // printed with '0x' in front
00261                 isSigned = true; // that's what gcc does
00262             }
00263             if (!strchr("cdiuoxX", formatChar)) {
00264                 result_ = -1;
00265                 return;
00266             }
00267             Char buf[
00268                 sizeof(LOKI_SAFEFORMAT_UNSIGNED_LONG) * 3 // digits
00269                 + 1 // sign or ' '
00270                 + 2 // 0x or 0X
00271                 + 1]; // terminating zero
00272             const Char *const bufEnd = buf + (sizeof(buf) / sizeof(Char));
00273             Char * bufLast = buf + (sizeof(buf) / sizeof(Char) - 1);
00274             Char signChar = 0;
00275             unsigned int base = 10;
00276             
00277             if (formatChar == 'c') {
00278                 // Format only one character
00279                 // The 'fill with zeros' flag is ignored
00280                 ResetFillZeros();
00281                 *bufLast = static_cast<char>(i);
00282             } else {
00283                 // TODO: inefficient code, refactor
00284                 const bool negative = isSigned && static_cast<LOKI_SAFEFORMAT_SIGNED_LONG>(i) < 0;
00285                 if (formatChar == 'o') base = 8;
00286                 else if (formatChar == 'x' || formatChar == 'X') base = 16;
00287                 bufLast = isSigned
00288                     ? RenderWithoutSign(static_cast<LOKI_SAFEFORMAT_SIGNED_LONG>(i), bufLast, base,
00289                         formatChar == 'X')
00290                     : RenderWithoutSign(i, bufLast, base, 
00291                         formatChar == 'X');
00292                 // Add the sign
00293                 if (isSigned) {
00294                     negative ? signChar = '-'
00295                     : ShowSignAlways() ? signChar = '+'
00296                     : Blank() ? signChar = ' '
00297                     : 0;
00298                 }
00299             }
00300             // precision 
00301             size_t 
00302                 countDigits = bufEnd - bufLast,
00303                 countZeros = prec_ != size_t(-1) && countDigits < prec_ && 
00304                         formatChar != 'c'
00305                     ? prec_ - countDigits 
00306                     : 0,
00307                 countBase = base != 10 && AlternateForm() && i != 0
00308                     ? (base == 16 ? 2 : countZeros > 0 ? 0 : 1)
00309                     : 0,
00310                 countSign = (signChar != 0),
00311                 totalPrintable = countDigits + countZeros + countBase + countSign;
00312             size_t countPadLeft = 0, countPadRight = 0;
00313             if (width_ > totalPrintable) {
00314                 if (LeftJustify()) {
00315                     countPadRight = width_ - totalPrintable;
00316                     countPadLeft = 0;
00317                 } else {
00318                     countPadLeft = width_ - totalPrintable;
00319                     countPadRight = 0;
00320                 }
00321             }
00322             if (FillZeros() && prec_ == size_t(-1)) {
00323                 // pad with zeros and no precision - transfer padding to precision
00324                 countZeros = countPadLeft;
00325                 countPadLeft = 0;
00326             }
00327             // ok, all computed, ready to print to device
00328             Fill(' ', countPadLeft);
00329             if (signChar != 0) Write(&signChar, &signChar + 1);
00330             if (countBase > 0) Fill('0', 1);
00331             if (countBase == 2) Fill(formatChar, 1);
00332             Fill('0', countZeros);
00333             Write(bufLast, bufEnd);
00334             Fill(' ', countPadRight);
00335             // done, advance
00336             Next();
00337         }
00338         
00339         void Write(const Char* b, const Char* e) {
00340             if (result_ < 0) return;
00341             const LOKI_SAFEFORMAT_SIGNED_LONG x = e - b;
00342             write(device_, b, e);
00343             result_ += x;
00344         }
00345 
00346         template <class Value>
00347         void PrintUsing_snprintf(Value n, const char* check_fmt_char) {
00348             const Char *const fmt = format_ - 1;
00349             assert(*fmt == '%');
00350             // enforce format string validity
00351             ReadLeaders();
00352             // enforce format spec
00353             if (!strchr(check_fmt_char, *format_)) {
00354                 result_ = -1;
00355                 return;
00356             }
00357             // format char validated, copy it to a temp and use legacy sprintf
00358             ++format_;
00359             Char fmtBuf[128], resultBuf[1024];
00360             if (format_  >= fmt + sizeof(fmtBuf) / sizeof(Char)) {
00361                 result_ = -1;
00362                 return;
00363             }
00364             memcpy(fmtBuf, fmt, (format_ - fmt) * sizeof(Char));
00365             fmtBuf[format_ - fmt] = 0;
00366 
00367             const int stored = 
00368 #ifdef _MSC_VER
00369 #if _MSC_VER < 1400
00370             _snprintf
00371 #else
00372             _snprintf_s
00373 #endif
00374 #else
00375             snprintf 
00376 #endif        
00377                      (resultBuf, sizeof(resultBuf) / sizeof(Char), fmtBuf, n);
00378                      
00379             if (stored < 0) {
00380                 result_ = -1;
00381                 return;
00382             }
00383             Write(resultBuf, resultBuf + strlen(resultBuf));
00384             Advance(); // output stuff to the next format directive
00385         }
00386 
00387         void Fill(const Char c, size_t n) {
00388             for (; n > 0; --n) {
00389                 Write(&c, &c + 1);
00390             }
00391         }
00392        
00393         Char* RenderWithoutSign(LOKI_SAFEFORMAT_UNSIGNED_LONG n, char* bufLast, 
00394                 unsigned int base, bool uppercase) {
00395             const Char hex1st = uppercase ? 'A' : 'a';
00396             for (;;) {
00397                 const LOKI_SAFEFORMAT_UNSIGNED_LONG next = n / base;
00398 #ifdef _MSC_VER
00399 #pragma warning(push)
00400 #pragma warning(disable: 4244)
00401 #endif
00402                 Char c = n - next * base;
00403 #ifdef _MSC_VER
00404 #pragma warning(pop)
00405 #endif
00406                 c += (c <= 9) ? '0' : hex1st - 10;
00407                 *bufLast = c;
00408                 n = next;
00409                 if (n == 0) break;
00410                 --bufLast;
00411             }
00412             return bufLast;
00413         }
00414 
00415         char* RenderWithoutSign(LOKI_SAFEFORMAT_SIGNED_LONG n, char* bufLast, unsigned int base, 
00416                 bool uppercase) {
00417             if (n != LONG_MIN) {
00418                 return RenderWithoutSign(static_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(n < 0 ? -n : n),
00419                     bufLast, base, uppercase);            
00420             }
00421             // annoying corner case
00422             char* save = bufLast;
00423             ++n;
00424             bufLast = RenderWithoutSign(static_cast<LOKI_SAFEFORMAT_UNSIGNED_LONG>(n),
00425                 bufLast, base, uppercase);
00426             --(*save);
00427             return bufLast;
00428         }
00429         
00430         void Next() {
00431             ++format_;
00432             Advance();
00433         }
00434         
00435         void Advance() {
00436             ResetAll();
00437             const Char* begin = format_;
00438             for (;;) {
00439                 if (*format_ == '%') { 
00440                     if (format_[1] != '%') { // It's a format specifier
00441                         Write(begin, format_);
00442                         ++format_;
00443                         break;
00444                     }
00445                     // It's a "%%"
00446                     Write(begin, ++format_);
00447                     begin = ++format_;
00448                     continue; 
00449                 }
00450                 if (*format_ == 0) {
00451                     Write(begin, format_);
00452                     break;
00453                 }
00454                 ++format_;
00455             }
00456         }
00457         
00458         void ReadFlags() {
00459             for (;; ++format_) {
00460                 switch (*format_) {
00461                     case '-': SetLeftJustify(); break;
00462                     case '+': SetShowSignAlways(); break;
00463                     case ' ': SetBlank(); break;
00464                     case '#': SetAlternateForm(); break;
00465                     case '0': SetFillZeros(); break;
00466                     default: return;
00467                 }
00468             }
00469         }
00470         
00471         void ParseDecimalSizeT(size_t& dest) {
00472             if (!std::isdigit(*format_, std::locale())) return;
00473             size_t r = 0;
00474             do {
00475                 // TODO: inefficient - rewrite
00476                 r *= 10;
00477                 r += *format_ - '0';
00478                 ++format_;
00479             } while (std::isdigit(*format_, std::locale()));
00480             dest = r;
00481         }
00482         
00483         void ReadWidth() {
00484             ParseDecimalSizeT(width_);
00485         }    
00486         
00487         void ReadPrecision() {
00488             assert(*format_ == '.');
00489             ++format_;
00490             ParseDecimalSizeT(prec_);
00491         }    
00492         
00493         void ReadModifiers() {
00494             switch (*format_) {
00495                 case 'h': SetForceShort(); ++format_; break;
00496                 case 'l': ++format_; break;
00497                 // more (C99 and platform-specific modifiers) to come
00498             }
00499         }
00500         
00501         void ReadLeaders() {
00502             ReadFlags();
00503             ReadWidth();
00504             if (*format_ == '.') ReadPrecision();
00505             ReadModifiers();
00506         }
00507         
00508         enum { 
00509             leftJustify = 1,
00510             showSignAlways = 2,
00511             blank = 4,
00512             alternateForm = 8,
00513             fillZeros = 16,
00514             forceShort = 32
00515         };
00516         
00517         bool LeftJustify() const { return (flags_ & leftJustify) != 0; }
00518         bool ShowSignAlways() const { return (flags_ & showSignAlways) != 0; }
00519         void SetWidth(size_t w) { width_  = w; }
00520         void SetLeftJustify() { flags_  |= leftJustify; }
00521         void SetShowSignAlways() { flags_ |= showSignAlways; }
00522         bool Blank() const { return (flags_ & blank) != 0; }
00523         bool AlternateForm() const { return (flags_ & alternateForm) != 0; }
00524         bool FillZeros() const { return (flags_ & fillZeros) != 0; }
00525         bool ForceShort() const { return (flags_ & forceShort) != 0; }
00526 
00527         void SetPrec(size_t p) { prec_ = p; }
00528         void SetBlank() { flags_ |= blank; }
00529         void SetAlternateForm() { flags_ |=  alternateForm; }
00530         void SetFillZeros() { flags_ |= fillZeros; }
00531         void ResetFillZeros() { flags_ &= ~fillZeros; }
00532         void SetForceShort() { flags_ |= forceShort; }
00533         
00534         void ResetAll() {
00535             assert(result_ != EOF);
00536             width_ = 0;
00537             prec_ = size_t(-1);
00538             flags_ = 0;
00539         }
00540 
00541         // state
00542         Device device_;
00543         const Char* format_;
00544         size_t width_;
00545         size_t prec_;
00546         unsigned int flags_;
00547         LOKI_SAFEFORMAT_SIGNED_LONG result_;
00548     };
00549 
00550     LOKI_EXPORT
00551     PrintfState<std::FILE*, char> Printf(const char* format);
00552 
00553     LOKI_EXPORT
00554     PrintfState<std::FILE*, char> Printf(const std::string& format);
00555 
00556     LOKI_EXPORT
00557     PrintfState<std::FILE*, char> FPrintf(std::FILE* f, const char* format);
00558 
00559     LOKI_EXPORT
00560     PrintfState<std::FILE*, char> FPrintf(std::FILE* f, const std::string& format);
00561 
00562     LOKI_EXPORT
00563     PrintfState<std::ostream&, char> FPrintf(std::ostream& f, const char* format);
00564 
00565     LOKI_EXPORT
00566     PrintfState<std::ostream&, char> FPrintf(std::ostream& f, const std::string& format);
00567 
00568     LOKI_EXPORT
00569     PrintfState<std::string&, char> SPrintf(std::string& s, const char* format);
00570 
00571     LOKI_EXPORT
00572     PrintfState<std::string&, char> SPrintf(std::string& s, const std::string& format);
00573 
00574     template <class T, class Char>
00575     PrintfState<T&, Char> XPrintf(T& device, const Char* format) {
00576         return PrintfState<T&, Char>(device, format);
00577     }
00578 
00579     template <class T>
00580     PrintfState<T&, char> XPrintf(T& device, const std::string& format) {
00581         return PrintfState<T&, char>(device, format.c_str());
00582     }
00583 
00584     template <class Char, std::size_t N>
00585     PrintfState<std::pair<Char*, std::size_t>, Char> 
00586     BufPrintf(Char (&buf)[N], const Char* format) {
00587         std::pair<Char*, std::size_t> temp(buf, N);
00588         return PrintfState<std::pair<Char*, std::size_t>, Char>(temp, format);
00589     }
00590 
00591 }// namespace Loki
00592 
00593 
00594 #endif // end file guardian
00595 

Generated on Sun Feb 25 16:52:27 2007 for Loki by  doxygen 1.5.1-p1