|
// Type-safe C++0x printf library
// Copyright (c) 2011 HHD Software Ltd. http://www.hhdsoftware.com
// Written by Alexander Bessonov
// Distributed under the terms of The Code Project Open License (http://www.codeproject.com)
#pragma once
//////////////////////////////////////////
/*
Format declaration:
{<param-index>[width-decl][alignment-decl][plus-decl][precision-decl][base-decl][padding-decl][ellipsis-decl][char-decl][locale-decl][time-decl]}
To escape the '{' character, duplicate it
<param-index> is zero-based parameter's index and is mandatory
All other parts are optional and may come in any order
[char-decl]
c
Treat the integer parameter as a character (ANSI or UNICODE)
[locale-decl]
l
Separate thousands with default locale's thousand separator
[width-decl]
w<min-width>,<max-width> or
w<min-width> or
w,<max-width>
[alignment-decl]
al | ar | ac
Sets left, right or center alignment
Default alignment is left
[plus-decl]
+
Forces the plus sign for numbers
[precision-decl]
p<number>
Sets the number of digits after the comma
[base-decl]
b<number> or
b[0]16[x] | b[0]16[X]
number may only be 2, 8, 10 or 16. Default is 10. If 'x' is put after 16, lowercase hex is used, otherwise - uppercase (default)
if 0 is specified, "0x" or "0X" will be rendered before the number
[padding-decl]
f<character>
Specify the character to fill when min width is specified. Default is ' '
[ellipsis-decl]
e
Add the ellipsis sign when truncating output. Is not compatible with center alignment (will act as left alignment)
[time-decl]
t(format-string)
Interpret the passed parameter as a date, time or date+time and display, according to format-string (see strftime CRT function)
*/
//////////////////////////////////////////
#include <boost/range/algorithm.hpp>
#include "generic_exception.h"
namespace ts_printf
{
// This exception is thrown if format string cannot be parsed
struct bad_extended_format_string : generic_exception
{
};
namespace _details
{
template<class Formatter,class char_type>
class ExtendedFormat
{
template<class IteratorT>
static int _atoi(IteratorT &cur) throw()
{
int result=0;
for (;'0'<=*cur && *cur<='9';++cur)
{
result=result*10+*cur-'0';
}
return result;
}
public:
template<class Range>
void iSetFormat(const Range &src)
{
const size_t maxcount=boost::count_if(
src,[](char_type c) { return c=='{'; }
);
static_cast<Formatter *>(this)->reserve_parts(maxcount*2+1);
auto last=boost::begin(src);
decltype(last) cur=last;
auto end=boost::end(src);
bool bClearEscapes=false;
for (;cur!=end;++cur)
{
if (*cur=='{')
{
if (cur+1==end || cur[1]!='{')
{
if (last!=cur)
static_cast<Formatter *>(this)->add_part(FormatDesc<char_type>(boost::make_iterator_range(last,cur),bClearEscapes));
bClearEscapes=false;
++cur;
FormatDesc<char_type> &fd=static_cast<Formatter *>(this)->next_part();
fd.parameter=(short) _atoi(cur);
while (cur!=end)
{
switch (*cur)
{
case 'a': // parse alignment
switch (*++cur)
{
case 'l':
fd.align=AlignLeft;
break;
case 'r':
fd.align=AlignRight;
break;
case 'c':
fd.align=AlignCenter;
break;
}
++cur;
break;
case '+':
fd.flags|=FF_ALWAYSPLUS;
++cur;
break;
case 'c':
fd.flags|=FF_FORCECHAR;
++cur;
break;
case 't': // time format
if (*++cur!='(')
throw bad_extended_format_string();
++cur;
fd.tmfBegin=std::addressof(*cur);
for (;;++cur)
{
if (*cur==')')
{
fd.tmfEnd=std::addressof(*cur);
break;
} else if (*cur=='}')
throw bad_extended_format_string();
}
fd.flags|=FF_TIMEFORMAT;
++cur;
break;
case 'l':
fd.flags|=FF_THOUSANDS;
++cur;
break;
case 'w':
if (*++cur==',')
{
++cur;
fd.maxwidth=(short) _atoi(cur);
}
else
{
int width=_atoi(cur);
if (*cur==',')
{
fd.minwidth=(short) width;
++cur;
fd.maxwidth=(short) _atoi(cur);
assert(fd.maxwidth>=fd.minwidth);
} else
fd.minwidth=(short) width;
}
break;
case 'p':
fd.precision=(short) _atoi(++cur);
break;
case 'b':
++cur;
if (*cur=='0')
{
fd.flags|=FF_NUMBERPREFIX;
++cur;
}
fd.base=(char) _atoi(cur);
assert(fd.base==2 || fd.base==8 || fd.base==10 || fd.base==16);
if (fd.base==16)
{
if (*cur=='x')
{
fd.flags|=FF_LOWHEX;
++cur;
} else if (*cur=='X')
++cur;
}
break;
case 'f':
fd.pad=*++cur;
++cur;
break;
case 'e':
fd.flags|=FF_ELLIPSIS;
++cur;
break;
case '}':
last=cur+1;
goto endparse;
default:
__assume(false);
break;
}
}
endparse: ;
} else
{
bClearEscapes=true;
++cur;
}
}
}
if (last!=end)
static_cast<Formatter *>(this)->add_part(FormatDesc<char_type>(boost::make_iterator_range(last,end),bClearEscapes));
}
template<class Adaptor>
static void WritePart(Adaptor &adaptor,const char_type *begin,const char_type *end,bool bHasEscapedCharacters) throw()
{
if (!bHasEscapedCharacters)
adaptor.write(boost::make_iterator_range(begin,end));
else
{
const char_type *last=begin;
for (;begin!=end;++begin)
{
if (*begin=='{')
{
adaptor.write(boost::make_iterator_range(last,++begin));
last=begin+1;
}
}
if (last!=end)
adaptor.write(boost::make_iterator_range(last,end));
}
}
};
};
};
|
By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.
If a file you wish to view isn't highlighted, and is a text file (not binary), please
let us know and we'll add colourisation support for it.
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.