Fixed problem in DeviceSettings::strParam, returned wrong string
[avr-sim.git] / src / Format.h
blob640f7dcfc18805ee50b1ec4c6c3f11a8034fcfb0
1 #ifndef UTIL_FORMAT_H
2 #define UTIL_FORMAT_H
4 #include <sstream>
5 #include <vector>
7 #include "FormatExceptions.h"
8 #include "FormatGroup.h"
10 namespace util {
11 /**
12 @author Tom Haber
13 @brief C++ formatting
15 How it works
16 When you call format(s), where s is the format-string,
17 it constructs an object, which parses the format string
18 and looks for all directives in it and prepares internal
19 structures for the next step.
21 Then, either immediately, as in
22 cout << format("%2% %1%") % 36 % 77 )
23 or later on, as in
24 format fmter("%2% %1%");
25 fmter % 36; fmter % 77;
27 you feed variables into the formatter. Those variables
28 are dumped into an internal stream, which state is set
29 according to the given formatting options in the format-string,
30 and the format object stores the string results for the last step.
32 Once all arguments have been fed you can dump the format object
33 to a stream, or get its string value by using the str() member
34 function. The result string stays accessible in the format object
35 until another argument is passed, at which time it is reinitialized.
37 // fmter was previously created and fed arguments, it can print the result :
38 cout << fmter ;
40 // You can take the string result :
41 string s = fmter.str();
43 // possibly several times :
44 s = fmter.str( );
46 // You can also do all steps at once :
47 cout << boost::format("%2% %1%") % 36 % 77;
49 Optionally, after step 3, you can re-use a format object and
50 restart at step2 : fmter % 18 % 39; to format new variables
51 with the same format-string, saving the expensive processing
52 involved at step 1. All in all, the format class translates a
53 format-string (with eventually printf-like directives) into
54 operations on an internal stream, and finally returns the result
55 of the formatting, as a string, or directly into an output stream.
57 class format {
58 private:
59 // set of params that define the format state of a stream
60 struct stream_format_state {
61 std::streamsize width;
62 std::streamsize precision;
63 char fill;
64 std::ios_base::fmtflags flags;
65 stream_format_state() : width(-1), precision(-1), fill(0),
66 flags(std::ios_base::dec) {}
68 stream_format_state( std::ios & os) { set_by_stream(os); }
70 //- applies format_state to the stream
71 void apply_on(std::ios & os) const;
73 //- modifies state by applying manipulator.
74 template<class T> void apply_manip(T manipulator);
76 //- sets to default state.
77 void reset();
78 //- sets to os's state.
79 void set_by_stream(const std::ios & os);
83 #ifdef _MSC_VER
84 // error C2248: 'util::format::format_item' : cannot access private struct declared in class 'util::format
85 public:
86 #endif
87 // format_item : stores all parameters that can be defined by directives in the format-string
88 struct format_item {
89 enum pad_values { zeropad = 1, spacepad =2, centered=4, tabulation = 8 };
90 enum arg_values {
91 argN_no_posit = -1, // non-positional directive. argN will be set later.
92 argN_tabulation = -2, // tabulation directive. (no argument read)
93 argN_ignored = -3 // ignored directive. (no argument read)
96 int argN; //- argument number (starts at 0, eg : %1 => argN=0)
98 // negative values are used for items that don't process an argument
99 std::string res; //- result of the formatting of this item
100 std::string appendix; //- piece of string between this item and the next
102 stream_format_state ref_state;// set by parsing the format_string, is only affected by modify_item
103 stream_format_state state; // always same as ref_state, _unless_ modified by manipulators 'group(..)'
105 // non-stream format-state parameters
106 signed int truncate; //- is >=0 for directives like %.5s (take 5 chars from the string)
108 unsigned int pad_scheme; //- several possible padding schemes can mix. see pad_values
110 format_item() : argN(argN_no_posit), truncate(-1), pad_scheme(0) {}
111 void compute_states(); // sets states according to truncate and pad_scheme.
114 public:
115 format(const char *str);
116 format(const std::string & s);
117 format(const format & x);
119 public:
120 format & operator =(const format & x);
121 format & clear(); // empty the string buffers (except bound arguments, see clear_binds() )
123 // pass arguments through those operators :
124 template<class T> format & operator %(const T & x) {
125 return feed<const T &>(x);
128 // system for binding arguments :
129 template<class T> format & bind_arg(int argN, const T& val) {
130 return bind_arg_body(argN, val);
133 format & clear_bind(int argN);
134 format & clear_binds();
136 // modify the params of a directive, by applying a manipulator :
137 template<class T> format & modify_item(int itemN, const T & manipulator) {
138 return modify_item_body(itemN, manipulator);
141 public:
142 enum format_error_bits {
143 bad_format_string_bit = 1,
144 too_few_args_bit = 2,
145 too_many_args_bit = 4,
146 out_of_range_bit = 8,
147 all_error_bits = 255,
148 no_error_bits = 0
151 // Choosing which errors will throw exceptions :
152 unsigned char exceptions() const { return exceptions_flag; }
153 unsigned char exceptions(unsigned char newexcept) {
154 unsigned char swp = exceptions_flag;
155 exceptions_flag = newexcept;
156 return swp;
159 public:
160 std::string str() const;
161 operator std::string () const;
162 friend std::ostream & operator<< (std::ostream & ostr, const format & f);
164 private:
165 template<class T> format & feed(T);
166 template<class T> void distribute(T);
167 template<class T> format & modify_item_body(int, const T&);
168 template<class T> format & bind_arg_body(int, const T&);
170 private:
171 // flag bits, used for style_
172 enum style_values {
173 ordered = 1, // set only if all directives are positional directives
174 special_needs = 4
177 // parse the format string :
178 void parse(const std::string &);
180 private:
181 int style; // style of format-string : positional or not, etc
182 int cur_arg; // keep track of which argument will come
183 int num_args; // number of expected arguments
184 mutable bool dumped; // true only after call to str() or <<
186 std::vector<format_item> items; // vector of directives (aka items)
187 std::string prefix; // piece of string to insert before first item
188 std::vector<bool> bound; // stores which arguments were bound
190 // size = num_args OR zero
191 std::ostringstream oss; // the internal stream.
192 stream_format_state state0; // reference state for oss_
193 unsigned char exceptions_flag;
195 template<class T> friend void put(T x, const format::format_item & specs,
196 std::string & res, std::ostringstream & oss);
197 friend bool parse_printf_directive(const std::string & buf,
198 std::string::size_type *pos_p,
199 format::format_item *fpar, std::ios & os,
200 unsigned char exceptions);
203 // set the state of this stream according to our params
204 inline void format::stream_format_state::apply_on(std::ios & os) const {
205 if( width != -1 )
206 os.width(width);
208 if( precision != -1 )
209 os.precision(precision);
211 if( fill != 0 )
212 os.fill(fill);
214 os.flags(flags);
217 // set our params according to the state of this stream
218 inline void format::stream_format_state::set_by_stream(const std::ios & os) {
219 flags = os.flags();
220 width = os.width();
221 precision = os.precision();
222 fill = os.fill();
225 // modify our params according to the manipulator
226 template <class T>
227 inline void format::stream_format_state::apply_manip(T manipulator) {
228 std::stringstream ss;
229 apply_on( ss );
230 ss << manipulator;
231 set_by_stream( ss );
234 // set our params to standard's default state
235 inline void format::stream_format_state::reset() {
236 width=-1; precision=-1; fill=0;
237 flags = std::ios_base::dec;
240 // reflect pad_scheme on state and ref_state
241 // because some pad_schemes has complex consequences on several state params.
242 inline void format::format_item::compute_states() {
243 if( pad_scheme & zeropad ) {
244 if( ref_state.flags & std::ios_base::left ) {
245 pad_scheme = pad_scheme & (~zeropad); // ignore zeropad in left alignment
246 }else {
247 ref_state.fill='0';
248 ref_state.flags |= std::ios_base::internal;
252 state = ref_state;
255 // bind one argument to a fixed value
256 // this is persistent over clear() calls, thus also over str() and <<
257 template <class T>
258 format & format::bind_arg_body(int argN, const T & val) {
259 if(dumped) clear(); // needed, because we will modify cur_arg..
261 if(argN<1 || argN > num_args) {
262 if( exceptions() & out_of_range_bit )
263 throw out_of_range(); // arg not in range.
264 else return *this;
268 if(bound.size()==0)
269 bound.assign(num_args,false);
271 int o_cur_arg = cur_arg;
272 cur_arg = argN-1; // arrays begin at 0
274 bound[cur_arg]=false; // if already set, we unset and re-sets..
275 operator%(val); // put val at the right place, because cur_arg is set
277 // Now re-position cur_arg before leaving :
278 cur_arg = o_cur_arg;
279 bound[argN-1]=true;
281 // hum, now this arg is bound, so move to next free arg
282 if( cur_arg == argN-1 ) {
283 while( (cur_arg < num_args) && bound[cur_arg] ) ++cur_arg;
286 return *this;
289 // applies a manipulator to the format_item describing a given directive.
290 // this is a permanent change, clear or clear_binds won't cancel that.
291 template <class T>
292 format & format::modify_item_body(int itemN, const T& manipulator) {
293 if(itemN<1 || itemN >= static_cast<signed int>(items.size() )) {
294 if( exceptions() & out_of_range_bit )
295 throw out_of_range(); // item not in range.
296 else return *this;
299 items[itemN-1].ref_state.apply_manip( manipulator );
300 items[itemN-1].state = items[itemN-1].ref_state;
302 return *this;
305 template<class T>
306 inline void put_head(std::ostream &, const T& ) {
309 template<class T>
310 inline void put_head(std::ostream & os, const format_interal::group1<T>& x ) {
311 os << format_interal::group_head(x.a1_); // send the first N-1 items, not the last
314 template<class T>
315 inline void put_last(std::ostream & os, const T& x ) {
316 os << x ;
319 template<class T>
320 inline void put_last(std::ostream & os, const format_interal::group1<T> & x ) {
321 os << format_interal::group_last(x.a1_); // this selects the last element
324 void do_pad(std::string & s, std::streamsize w, const char c,
325 std::ios_base::fmtflags f, bool center);
326 void empty_buf(std::ostringstream & os);
328 // does the actual conversion of x, with given params, into a string
329 // using the *supplied* stringstream. (the stream state is important)
330 template<class T> void put(T x, const format::format_item & specs,
331 std::string & res, std::ostringstream & oss) {
333 format::stream_format_state prev_state(oss);
334 specs.state.apply_on(oss);
336 // in case x is a group, apply the manip part of it,
338 // in order to find width
340 put_head( oss, x );
341 empty_buf( oss );
343 const std::streamsize w = oss.width();
344 const std::ios_base::fmtflags fl=oss.flags();
346 const bool internal = (fl & std::ios_base::internal) != 0;
348 const bool two_stepped_padding = internal
349 && ! ( specs.pad_scheme & format::format_item::spacepad )
350 && specs.truncate < 0 ;
352 if(! two_stepped_padding ) {
353 if(w>0) // handle simple padding via do_pad, not natively in stream
354 oss.width(0);
356 put_last( oss, x);
357 res = oss.str();
359 if( specs.truncate >= 0 && static_cast<unsigned int>(specs.truncate) < res.size() )
360 res.erase(specs.truncate);
362 // complex pads :
363 if( specs.pad_scheme & format::format_item::spacepad ) {
364 if( res.size()==0 || ( res[0]!='+' && res[0]!='-' )) {
365 res.insert(res.begin(), 1, ' '); // insert 1 space at pos 0
369 // need do_pad
370 if(w > 0) {
371 do_pad(res, w, oss.fill(), fl, (specs.pad_scheme & format::format_item::centered) !=0 );
374 } else {
375 // 2-stepped padding
376 put_last( oss, x); // oss_.width() may result in padding.
377 res = oss.str();
379 if( specs.truncate >= 0 )
380 res.erase(specs.truncate);
382 if( res.size() - w > 0 ) {
383 // length w exceeded
384 // either it was multi-output with first output padding up all width..
385 // either it was one big arg and we are fine.
386 empty_buf( oss );
387 oss.width(0);
388 put_last(oss, x );
390 std::string tmp = oss.str(); // minimal-length output
391 std::streamsize d;
392 if( (d=w - tmp.size()) <=0 ) {
393 // minimal length is already >= w, so no padding (cool!)
394 res.swap(tmp);
395 } else {
396 // hum.. we need to pad (it was necessarily multi-output)
397 typedef typename std::string::size_type size_type;
399 size_type i = 0;
400 while( i<tmp.size() && tmp[i] == res[i] ) // find where we should pad.
401 ++i;
403 tmp.insert(i, static_cast<size_type>( d ), oss.fill());
404 res.swap( tmp );
409 prev_state.apply_on(oss);
410 empty_buf( oss );
411 oss.clear();
414 // call put(x, ..) on every occurence of the current argument :
415 template<class T>
416 void format::distribute(T x) {
417 if( cur_arg >= num_args ) {
418 if( exceptions() & too_many_args_bit )
419 throw too_many_args(); // too many variables have been supplied !
421 else return;
424 for(unsigned long i=0; i < items.size(); ++i) {
425 if(items[i].argN == cur_arg) {
426 put<T>(x, items[i], items[i].res, oss );
431 template<class T>
432 format & format::feed(T x) {
433 if( dumped ) clear();
435 distribute<T>(x);
436 ++cur_arg;
438 if( bound.size() != 0 ) {
439 while( cur_arg < num_args && bound[cur_arg] )
440 ++cur_arg;
443 // this arg is finished, reset the stream's format state
444 state0.apply_on(oss);
445 return *this;
448 inline format::operator std::string () const {
449 return str();
452 #endif