6 void empty_buf(std::ostringstream
& os
) {
7 static const std::string emptyStr
;
11 // applies centered / left / right padding to the string s.
12 // Effects : string s is padded.
13 void do_pad(std::string
& s
, std::streamsize w
, const char c
,
14 std::ios_base::fmtflags f
, bool center
) {
16 std::streamsize n
=w
-s
.size();
21 s
.reserve(w
); // allocate once for the 2 inserts
22 const std::streamsize n1
= n
/2, n0
= n
- n1
;
23 s
.insert(s
.begin(), n0
, c
);
26 if(f
& std::ios_base::left
)
29 s
.insert(s
.begin(), n
, c
);
33 format::format(const char *str
)
34 : style(0), cur_arg(0), num_args(0), dumped(false),
35 items(), oss(), exceptions_flag(all_error_bits
) {
37 state0
.set_by_stream(oss
);
41 str
= emptyStr
.c_str();
46 format::format(const std::string
& s
)
47 : style(0), cur_arg(0), num_args(0), dumped(false),
48 items(), oss(), exceptions_flag(all_error_bits
) {
50 state0
.set_by_stream(oss
);
54 format::format(const format
& x
)
55 : style(x
.style
), cur_arg(x
.cur_arg
), num_args(x
.num_args
), dumped(false),
56 items(x
.items
), prefix(x
.prefix
), bound(x
.bound
),
57 oss(), // <- we obviously can't copy x.oss
58 state0(x
.state0
), exceptions_flag(x
.exceptions_flag
){
63 format
& format::operator =(const format
& x
) {
70 // plus all the other (trivial) assignments :
71 exceptions_flag
= x
.exceptions_flag
;
82 // empty the string buffers (except bound arguments, see clear_binds() )
83 // and make the format object ready for formatting a new set of arguments
84 format
& format::clear() {
85 for(unsigned long i
=0; i
<items
.size(); ++i
) {
86 items
[i
].state
= items
[i
].ref_state
;
88 // clear converted strings only if the corresponding argument is not bound :
89 if( bound
.size()==0 || !bound
[ items
[i
].argN
] ) items
[i
].res
.resize(0);
92 cur_arg
=0; dumped
=false;
94 // maybe first arg is bound:
96 while( (cur_arg
< num_args
) && bound
[cur_arg
] ) ++cur_arg
;
101 // cancel all bindings, and clear()
102 format
& format::clear_binds() {
109 // cancel the binding of ONE argument, and clear()
110 format
& format::clear_bind(int argN
) {
111 if(argN
<1 || argN
> num_args
|| bound
.size()==0 || !bound
[argN
-1] ) {
112 if( exceptions() & out_of_range_bit
)
113 throw out_of_range(); // arg not in range.
124 std::string
format::str() const {
127 if( items
.size()==0 )
130 if( cur_arg
< num_args
)
131 if( exceptions() & too_few_args_bit
)
132 throw too_few_args(); // not enough variables have been supplied !
134 unsigned long sz
= prefix
.size();
136 for(i
=0; i
< items
.size(); ++i
)
137 sz
+= items
[i
].res
.size() + items
[i
].appendix
.size();
143 for(i
=0; i
< items
.size(); ++i
) {
144 const format_item
& item
= items
[i
];
146 if( item
.argN
== format_item::argN_tabulation
) {
147 std::streamsize n
= item
.state
.width
- res
.size();
150 res
.append( n
, item
.state
.fill
);
153 res
+= item
.appendix
;
159 // Input : char string, with starting index
160 // a basic_ios& merely to call its widen/narrow member function in the desired locale.
161 // Effects : reads s[start:] and converts digits into an integral n, of type Res
164 inline Res
str2int(const std::string
& s
,
165 std::string::size_type start
,
166 std::ios
& os
, const Res
= Res(0) ) {
169 while( start
<s
.size() && isdigit(s
[start
]) ) {
170 char cur_ch
= os
.narrow( s
[start
], 0);
172 n
+= cur_ch
- '0'; // 22.2.1.1.2 of the C++ standard
179 // skip printf's "asterisk-fields" directives in the format-string buf
180 // Input : char string, with starting index *pos_p
181 // a basic_ios& merely to call its widen/narrow member function in the desired locale.
182 // Effects : advance *pos_p by skipping printf's asterisk fields.
184 static void skip_asterisk(const std::string
& buf
,
185 std::string::size_type
*pos_p
,
188 if(*pos_p
>= buf
.size() ) return;
189 if(buf
[ *pos_p
]==os
.widen('*')) {
191 while (*pos_p
< buf
.size() && isdigit(buf
[*pos_p
])) ++(*pos_p
);
192 if(buf
[*pos_p
]==os
.widen('$')) ++(*pos_p
);
196 // Input : a 'printf-directive' in the format-string, starting at buf[ *pos_p ]
197 // a basic_ios& merely to call its widen/narrow member function in the desired locale.
198 // a bitset'exceptions' telling whether to throw exceptions on errors.
199 // Returns : true if parse somehow succeeded (possibly ignoring errors if exceptions disabled)
200 // false if it failed so bad that the directive should be printed verbatim
201 // Effects : - *pos_p is incremented so that buf[*pos_p] is the first char after the directive
202 // - *fpar is set with the parameters read in the directive
203 bool parse_printf_directive(const std::string
& buf
,
204 std::string::size_type
*pos_p
,
205 format::format_item
*fpar
, std::ios
& os
,
206 unsigned char exceptions
) {
207 std::string::size_type
& i1
= *pos_p
, i0
;
208 fpar
->argN
= format::format_item::argN_no_posit
; // if no positional-directive
210 bool in_brackets
=false;
211 if(buf
[i1
]==os
.widen('|')) {
213 if( ++i1
>= buf
.size() ) {
214 if(exceptions
& format::bad_format_string_bit
)
215 throw bad_format_string();
221 // the flag '0' would be picked as a digit for argument order, but here it's a flag :
222 if(buf
[i1
]==os
.widen('0'))
225 // handle argument order (%2$d) or possibly width specification: %2d
226 i0
= i1
; // save position before digits
227 while( i1
< buf
.size() && isdigit(buf
[i1
]) )
231 if( i1
>= buf
.size() ) {
232 if( exceptions
& format::bad_format_string_bit
)
233 throw bad_format_string();
238 int n
=str2int(buf
,i0
, os
, int(0) );
239 // %N% case : this is already the end of the directive
241 if( buf
[i1
] == os
.widen('%') ) {
245 if( in_brackets
&& (exceptions
& format::bad_format_string_bit
) )
246 throw bad_format_string();
248 // but don't return. maybe "%" was used in lieu of '$', so we go on.
253 if ( buf
[i1
]==os
.widen('$') ) {
257 // non-positionnal directive
258 fpar
->ref_state
.width
= n
;
259 fpar
->argN
= format::format_item::argN_no_posit
;
260 goto parse_precision
;
267 while ( i1
<buf
.size() ) {
268 // as long as char is one of + - = # 0 l h or ' '
271 switch( os
.narrow(buf
[i1
], 0) ) {
272 case '\'' : break; // no effect yet. (painful to implement)
274 case 'h': // short/long modifier : for printf-comaptibility (no action needed)
278 fpar
->ref_state
.flags
|= std::ios_base::left
;
282 fpar
->pad_scheme
|= format::format_item::centered
;
286 fpar
->pad_scheme
|= format::format_item::spacepad
;
290 fpar
->ref_state
.flags
|= std::ios_base::showpos
;
294 fpar
->pad_scheme
|= format::format_item::zeropad
;
295 // need to know alignment before really setting flags,
296 // so just add 'zeropad' flag for now, it will be processed later.
300 fpar
->ref_state
.flags
|= std::ios_base::showpoint
| std::ios_base::showbase
;
310 if( i1
>=buf
.size()) {
311 if( exceptions
& format::bad_format_string_bit
)
312 throw bad_format_string();
319 skip_asterisk(buf
, &i1
, os
); // skips 'asterisk fields' : *, or *N$
320 i0
= i1
; // save position before digits
321 while (i1
<buf
.size() && isdigit(buf
[i1
]))
325 fpar
->ref_state
.width
= str2int( buf
,i0
, os
, std::streamsize(0) );
329 if( i1
>=buf
.size()) {
330 if( exceptions
& format::bad_format_string_bit
)
331 throw bad_format_string();
335 // handle precision spec
336 if (buf
[i1
]==os
.widen('.')) {
338 skip_asterisk(buf
, &i1
, os
);
339 i0
= i1
; // save position before digits
340 while (i1
<buf
.size() && isdigit(buf
[i1
]))
344 fpar
->ref_state
.precision
= 0;
346 fpar
->ref_state
.precision
= str2int(buf
,i0
, os
, std::streamsize(0) );
349 // handle formatting-type flags :
351 while( i1
<buf
.size() &&
352 ( buf
[i1
]==os
.widen('l') || buf
[i1
]==os
.widen('L') || buf
[i1
]==os
.widen('h')) )
355 if( i1
>=buf
.size()) {
356 if( exceptions
& format::bad_format_string_bit
)
357 throw bad_format_string();
362 if( in_brackets
&& buf
[i1
]==os
.widen('|') ) {
367 switch (os
.narrow(buf
[i1
], 0) ) {
369 fpar
->ref_state
.flags
|= std::ios_base::uppercase
;
371 case 'p': // pointer => set hex.
373 fpar
->ref_state
.flags
&= ~std::ios_base::basefield
;
374 fpar
->ref_state
.flags
|= std::ios_base::hex
;
378 fpar
->ref_state
.flags
&= ~std::ios_base::basefield
;
379 fpar
->ref_state
.flags
|= std::ios_base::oct
;
383 fpar
->ref_state
.flags
|= std::ios_base::uppercase
;
385 fpar
->ref_state
.flags
&= ~std::ios_base::floatfield
;
386 fpar
->ref_state
.flags
|= std::ios_base::scientific
;
387 fpar
->ref_state
.flags
&= ~std::ios_base::basefield
;
388 fpar
->ref_state
.flags
|= std::ios_base::dec
;
392 fpar
->ref_state
.flags
&= ~std::ios_base::floatfield
;
393 fpar
->ref_state
.flags
|= std::ios_base::fixed
;
397 fpar
->ref_state
.flags
&= ~std::ios_base::basefield
;
398 fpar
->ref_state
.flags
|= std::ios_base::dec
;
403 if( i1
>= buf
.size())
404 if( exceptions
& format::bad_format_string_bit
)
405 throw bad_format_string();
407 fpar
->ref_state
.fill
= buf
[i1
];
409 fpar
->pad_scheme
|= format::format_item::tabulation
;
410 fpar
->argN
= format::format_item::argN_tabulation
;
414 fpar
->ref_state
.fill
= os
.widen(' ');
415 fpar
->pad_scheme
|= format::format_item::tabulation
;
416 fpar
->argN
= format::format_item::argN_tabulation
;
420 fpar
->ref_state
.flags
|= std::ios_base::uppercase
;
423 case 'g': // 'g' conversion is default for floats.
424 fpar
->ref_state
.flags
&= ~std::ios_base::basefield
;
425 fpar
->ref_state
.flags
|= std::ios_base::dec
;
426 // CLEAR all floatield flags, so stream will CHOOSE
427 fpar
->ref_state
.flags
&= ~std::ios_base::floatfield
;
437 fpar
->truncate
= fpar
->ref_state
.precision
;
438 fpar
->ref_state
.precision
= -1;
442 fpar
->argN
= format::format_item::argN_ignored
;
446 if( exceptions
& format::bad_format_string_bit
)
447 throw bad_format_string();
453 if( i1
<buf
.size() && buf
[i1
]==os
.widen('|') ) {
456 } else if( exceptions
& format::bad_format_string_bit
)
457 throw bad_format_string();
463 // parse the format-string
464 void format::parse(const std::string
& buf
) {
466 const char arg_mark
= oss
.widen('%');
467 bool ordered_args
=true;
469 string::size_type i1
=0;
472 // A: find upper_bound on num_items and allocates arrays
474 while( (i1
=buf
.find(arg_mark
,i1
)) != string::npos
) {
475 if( i1
+1 >= buf
.size() ) {
476 if( exceptions() & bad_format_string_bit
)
477 throw bad_format_string(); // must not end in "bla bla %"
479 else break; // stop there, ignore last '%'
482 if(buf
[i1
+1] == buf
[i1
] ) { i1
+=2; continue; } // escaped "%%" / "##"
485 // in case of %N% directives, dont count it double (wastes allocations..) :
486 while(i1
< buf
.size() && isdigit(buf
[i1
])) ++i1
;
487 if( i1
< buf
.size() && buf
[i1
] == arg_mark
) ++ i1
;
493 items
.assign( num_items
, format_item() );
495 // B: Now the real parsing of the format string :
498 string::size_type i0
= i1
;
499 bool special_things
=false;
501 while( (i1
=buf
.find(arg_mark
,i1
)) != string::npos
) {
502 string
& piece
= (cur_it
==0) ? prefix
: items
[cur_it
-1].appendix
;
504 // escaped mark, '%%'
505 if( buf
[i1
+1] == buf
[i1
] ) {
506 piece
+= buf
.substr(i0
, i1
-i0
) + buf
[i1
];
511 if(i1
!=i0
) piece
+= buf
.substr(i0
, i1
-i0
);
515 parse_ok
= parse_printf_directive(buf
, &i1
, &items
[cur_it
], oss
, exceptions());
516 if( ! parse_ok
) continue; // the directive will be printed verbatim
519 items
[cur_it
].compute_states(); // process complex options, like zeropad, into stream params.
521 int argN
=items
[cur_it
].argN
;
523 if( argN
== format_item::argN_ignored
)
526 if(argN
==format_item::argN_no_posit
)
528 else if(argN
== format_item::argN_tabulation
)
530 else if(argN
> max_argN
)
537 // store the final piece of string
538 string
& piece
= (cur_it
==0) ? prefix
: items
[cur_it
-1].appendix
;
539 piece
+= buf
.substr(i0
);
542 // dont mix positional with non-positionnal directives
544 if( exceptions() & bad_format_string_bit
)
545 throw bad_format_string();
546 // else do nothing. => positional arguments are processed as non-positional
549 // set things like it would have been with positional directives :
550 int non_ordered_items
= 0;
551 for(int i
=0; i
< num_items
; ++i
)
552 if(items
[i
].argN
== format_item::argN_no_posit
) {
553 items
[i
].argN
= non_ordered_items
;
556 max_argN
= non_ordered_items
-1;
559 // C: set some member data :
560 items
.resize(num_items
);
563 style
|= special_needs
;
565 num_args
= max_argN
+ 1;
573 // effect: "return os << str(f);" but we can try to do it faster
574 std::ostream
& operator <<(std::ostream
& os
, const format
& f
) {
575 if( f
.items
.size() == 0 ) {
578 if( f
.cur_arg
< f
.num_args
)
579 if( f
.exceptions() & format::too_few_args_bit
)
580 throw too_few_args(); // not enough variables have been supplied !
582 if( f
.style
& format::special_needs
) {
585 // else we dont have to count chars output, so we dump directly to os :
587 for(unsigned long i
=0; i
<f
.items
.size(); ++i
) {
589 const format::format_item
& item
= f
.items
[i
];