| 1 | // ---------------------------------------------------------------------------- | 
|---|
| 2 | //  feed_args.hpp :  functions for processing each argument  | 
|---|
| 3 | //                      (feed, feed_manip, and distribute) | 
|---|
| 4 | // ---------------------------------------------------------------------------- | 
|---|
| 5 |  | 
|---|
| 6 | //  Copyright Samuel Krempp 2003. Use, modification, and distribution are | 
|---|
| 7 | //  subject to the Boost Software License, Version 1.0. (See accompanying | 
|---|
| 8 | //  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) | 
|---|
| 9 |  | 
|---|
| 10 | //  See http://www.boost.org/libs/format for library home page | 
|---|
| 11 |  | 
|---|
| 12 | // ---------------------------------------------------------------------------- | 
|---|
| 13 |  | 
|---|
| 14 | #ifndef BOOST_FORMAT_FEED_ARGS_HPP | 
|---|
| 15 | #define BOOST_FORMAT_FEED_ARGS_HPP | 
|---|
| 16 |  | 
|---|
| 17 | #include <boost/config.hpp> | 
|---|
| 18 | #include <boost/assert.hpp> | 
|---|
| 19 | #include <boost/throw_exception.hpp> | 
|---|
| 20 |  | 
|---|
| 21 | #include <boost/format/format_class.hpp> | 
|---|
| 22 | #include <boost/format/group.hpp> | 
|---|
| 23 | #include <boost/format/detail/msvc_disambiguater.hpp> | 
|---|
| 24 |  | 
|---|
| 25 | namespace boost { | 
|---|
| 26 | namespace io { | 
|---|
| 27 | namespace detail { | 
|---|
| 28 |  | 
|---|
| 29 |     template<class Ch, class Tr, class Alloc> | 
|---|
| 30 |     void mk_str( std::basic_string<Ch,Tr, Alloc> & res,  | 
|---|
| 31 |                  const Ch * beg, | 
|---|
| 32 |                  typename std::basic_string<Ch,Tr,Alloc>::size_type size, | 
|---|
| 33 |                  std::streamsize w,  | 
|---|
| 34 |                  const Ch fill_char, | 
|---|
| 35 |                  std::ios_base::fmtflags f,  | 
|---|
| 36 |                  const Ch prefix_space, // 0 if no space-padding | 
|---|
| 37 |                  bool center)  | 
|---|
| 38 |     // applies centered/left/right  padding  to the string  [beg, beg+size[ | 
|---|
| 39 |     // Effects : the result is placed in res. | 
|---|
| 40 |     { | 
|---|
| 41 |         typedef typename std::basic_string<Ch,Tr,Alloc>::size_type size_type; | 
|---|
| 42 |         res.resize(0); | 
|---|
| 43 |         if(w<=0 || static_cast<size_type>(w) <=size) { | 
|---|
| 44 |             // no need to pad. | 
|---|
| 45 |             res.reserve(size + !!prefix_space); | 
|---|
| 46 |             if(prefix_space)  | 
|---|
| 47 |               res.append(1, prefix_space); | 
|---|
| 48 |             if (size) | 
|---|
| 49 |               res.append(beg, size); | 
|---|
| 50 |         } | 
|---|
| 51 |         else {  | 
|---|
| 52 |             std::streamsize n=static_cast<std::streamsize>(w-size-!!prefix_space); | 
|---|
| 53 |             std::streamsize n_after = 0, n_before = 0;  | 
|---|
| 54 |             res.reserve(w); // allocate once for the 2 inserts | 
|---|
| 55 |             if(center)  | 
|---|
| 56 |                 n_after = n/2, n_before = n - n_after;  | 
|---|
| 57 |             else  | 
|---|
| 58 |                 if(f & std::ios_base::left) | 
|---|
| 59 |                     n_after = n; | 
|---|
| 60 |                 else | 
|---|
| 61 |                     n_before = n; | 
|---|
| 62 |             // now make the res string : | 
|---|
| 63 |             if(n_before) res.append(n_before, fill_char); | 
|---|
| 64 |             if(prefix_space)  | 
|---|
| 65 |               res.append(1, prefix_space); | 
|---|
| 66 |             if (size)   | 
|---|
| 67 |               res.append(beg, size); | 
|---|
| 68 |             if(n_after) res.append(n_after, fill_char); | 
|---|
| 69 |         } | 
|---|
| 70 |     } // -mk_str(..)  | 
|---|
| 71 |  | 
|---|
| 72 |  | 
|---|
| 73 | #if BOOST_WORKAROUND( BOOST_MSVC, <= 1300) || \ | 
|---|
| 74 |     BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042)) | 
|---|
| 75 | // MSVC needs to be tricked to disambiguate this simple overload.. | 
|---|
| 76 | // the trick is in "boost/format/msvc_disambiguater.hpp" | 
|---|
| 77 |    | 
|---|
| 78 |     template< class Ch, class Tr, class T> inline | 
|---|
| 79 |     void put_head (BOOST_IO_STD basic_ostream<Ch, Tr> & os, const T& x ) { | 
|---|
| 80 |         disambiguater<Ch, Tr, T>::put_head(os, x, 1L); | 
|---|
| 81 |     } | 
|---|
| 82 |     template< class Ch, class Tr, class T> inline | 
|---|
| 83 |     void put_last (BOOST_IO_STD basic_ostream<Ch, Tr> & os, const T& x ) { | 
|---|
| 84 |         disambiguater<Ch, Tr, T>::put_last(os, x, 1L); | 
|---|
| 85 |     } | 
|---|
| 86 |  | 
|---|
| 87 | #else   | 
|---|
| 88 |  | 
|---|
| 89 |     template< class Ch, class Tr, class T> inline | 
|---|
| 90 |     void put_head (BOOST_IO_STD basic_ostream<Ch, Tr> &, const T& ) { | 
|---|
| 91 |     } | 
|---|
| 92 |  | 
|---|
| 93 |     template< class Ch, class Tr, class T> inline | 
|---|
| 94 |     void put_head( BOOST_IO_STD basic_ostream<Ch, Tr> & os, const group1<T>& x ) { | 
|---|
| 95 |         os << group_head(x.a1_); // send the first N-1 items, not the last | 
|---|
| 96 |     } | 
|---|
| 97 |  | 
|---|
| 98 |     template< class Ch, class Tr, class T> inline | 
|---|
| 99 |     void put_last( BOOST_IO_STD basic_ostream<Ch, Tr> & os, const T& x ) { | 
|---|
| 100 |         os << x ; | 
|---|
| 101 |     } | 
|---|
| 102 |  | 
|---|
| 103 |     template< class Ch, class Tr, class T> inline | 
|---|
| 104 |     void put_last( BOOST_IO_STD basic_ostream<Ch, Tr> & os, const group1<T>& x ) { | 
|---|
| 105 |         os << group_last(x.a1_); // this selects the last element | 
|---|
| 106 |     } | 
|---|
| 107 |  | 
|---|
| 108 | #ifndef BOOST_NO_OVERLOAD_FOR_NON_CONST  | 
|---|
| 109 |     template< class Ch, class Tr, class T> inline | 
|---|
| 110 |     void put_head( BOOST_IO_STD basic_ostream<Ch, Tr> &, T& ) { | 
|---|
| 111 |     } | 
|---|
| 112 |  | 
|---|
| 113 |     template< class Ch, class Tr, class T> inline | 
|---|
| 114 |     void put_last( BOOST_IO_STD basic_ostream<Ch, Tr> & os, T& x) { | 
|---|
| 115 |         os << x ; | 
|---|
| 116 |     } | 
|---|
| 117 | #endif | 
|---|
| 118 | #endif  // -msvc workaround | 
|---|
| 119 |  | 
|---|
| 120 |  | 
|---|
| 121 |     template< class Ch, class Tr, class Alloc, class T>  | 
|---|
| 122 |     void put( T x,  | 
|---|
| 123 |               const format_item<Ch, Tr, Alloc>& specs,  | 
|---|
| 124 |               typename basic_format<Ch, Tr, Alloc>::string_type& res,  | 
|---|
| 125 |               typename basic_format<Ch, Tr, Alloc>::internal_streambuf_t & buf, | 
|---|
| 126 |               io::detail::locale_t *loc_p = NULL) | 
|---|
| 127 |     { | 
|---|
| 128 |         // does the actual conversion of x, with given params, into a string | 
|---|
| 129 |         // using the supplied stringbuf. | 
|---|
| 130 |  | 
|---|
| 131 |         typedef typename basic_format<Ch, Tr, Alloc>::string_type   string_type; | 
|---|
| 132 |         typedef typename basic_format<Ch, Tr, Alloc>::format_item_t format_item_t; | 
|---|
| 133 |         typedef typename string_type::size_type size_type; | 
|---|
| 134 |  | 
|---|
| 135 |         basic_oaltstringstream<Ch, Tr, Alloc>  oss( &buf); | 
|---|
| 136 |         specs.fmtstate_.apply_on(oss, loc_p); | 
|---|
| 137 |  | 
|---|
| 138 |         // the stream format state can be modified by manipulators in the argument : | 
|---|
| 139 |         put_head( oss, x ); | 
|---|
| 140 |         // in case x is a group, apply the manip part of it,  | 
|---|
| 141 |         // in order to find width | 
|---|
| 142 |  | 
|---|
| 143 |         const std::ios_base::fmtflags fl=oss.flags(); | 
|---|
| 144 |         const bool internal = (fl & std::ios_base::internal) != 0; | 
|---|
| 145 |         const std::streamsize w = oss.width(); | 
|---|
| 146 |         const bool two_stepped_padding= internal && (w!=0); | 
|---|
| 147 |        | 
|---|
| 148 |         res.resize(0); | 
|---|
| 149 |         if(! two_stepped_padding) { | 
|---|
| 150 |             if(w>0) // handle padding via mk_str, not natively in stream  | 
|---|
| 151 |                 oss.width(0); | 
|---|
| 152 |             put_last( oss, x); | 
|---|
| 153 |             const Ch * res_beg = buf.pbase(); | 
|---|
| 154 |             Ch prefix_space = 0; | 
|---|
| 155 |             if(specs.pad_scheme_ & format_item_t::spacepad) | 
|---|
| 156 |                 if(buf.pcount()== 0 ||  | 
|---|
| 157 |                    (res_beg[0] !=oss.widen('+') && res_beg[0] !=oss.widen('-')  )) | 
|---|
| 158 |                     prefix_space = oss.widen(' '); | 
|---|
| 159 |             size_type res_size = (std::min)( | 
|---|
| 160 |                 static_cast<size_type>(specs.truncate_ - !!prefix_space),  | 
|---|
| 161 |                 buf.pcount() ); | 
|---|
| 162 |             mk_str(res, res_beg, res_size, w, oss.fill(), fl,  | 
|---|
| 163 |                    prefix_space, (specs.pad_scheme_ & format_item_t::centered) !=0 ); | 
|---|
| 164 |         } | 
|---|
| 165 |         else  { // 2-stepped padding | 
|---|
| 166 |             // internal can be implied by zeropad, or user-set. | 
|---|
| 167 |             // left, right, and centered alignment overrule internal, | 
|---|
| 168 |             // but spacepad or truncate might be mixed with internal (using manipulator) | 
|---|
| 169 |             put_last( oss, x); // may pad | 
|---|
| 170 |             const Ch * res_beg = buf.pbase(); | 
|---|
| 171 |             size_type res_size = buf.pcount(); | 
|---|
| 172 |             bool prefix_space=false; | 
|---|
| 173 |             if(specs.pad_scheme_ & format_item_t::spacepad) | 
|---|
| 174 |                 if(buf.pcount()== 0 ||  | 
|---|
| 175 |                    (res_beg[0] !=oss.widen('+') && res_beg[0] !=oss.widen('-')  )) | 
|---|
| 176 |                     prefix_space = true; | 
|---|
| 177 |             if(res_size == static_cast<size_type>(w) && w<=specs.truncate_ && !prefix_space) { | 
|---|
| 178 |                 // okay, only one thing was printed and padded, so res is fine | 
|---|
| 179 |                 res.assign(res_beg, res_size); | 
|---|
| 180 |             } | 
|---|
| 181 |             else { //   length w exceeded | 
|---|
| 182 |                 // either it was multi-output with first output padding up all width.. | 
|---|
| 183 |                 // either it was one big arg and we are fine. | 
|---|
| 184 |                 // Note that res_size<w is possible  (in case of bad user-defined formatting) | 
|---|
| 185 |                 res.assign(res_beg, res_size); | 
|---|
| 186 |                 res_beg=NULL;  // invalidate pointers. | 
|---|
| 187 |                  | 
|---|
| 188 |                 // make a new stream, to start re-formatting from scratch : | 
|---|
| 189 |                 buf.clear_buffer(); | 
|---|
| 190 |                 basic_oaltstringstream<Ch, Tr, Alloc>  oss2( &buf); | 
|---|
| 191 |                 specs.fmtstate_.apply_on(oss2, loc_p); | 
|---|
| 192 |                 put_head( oss2, x ); | 
|---|
| 193 |  | 
|---|
| 194 |                 oss2.width(0); | 
|---|
| 195 |                 if(prefix_space) | 
|---|
| 196 |                     oss2 << ' '; | 
|---|
| 197 |                 put_last(oss2, x ); | 
|---|
| 198 |                 if(buf.pcount()==0 && specs.pad_scheme_ & format_item_t::spacepad) { | 
|---|
| 199 |                     prefix_space =true; | 
|---|
| 200 |                     oss2 << ' '; | 
|---|
| 201 |                 } | 
|---|
| 202 |                 // we now have the minimal-length output | 
|---|
| 203 |                 const Ch * tmp_beg = buf.pbase(); | 
|---|
| 204 |                 size_type tmp_size = (std::min)(static_cast<size_type>(specs.truncate_), | 
|---|
| 205 |                                                 buf.pcount() ); | 
|---|
| 206 |                                                      | 
|---|
| 207 |                  | 
|---|
| 208 |                 if(static_cast<size_type>(w) <= tmp_size) {  | 
|---|
| 209 |                     // minimal length is already >= w, so no padding (cool!) | 
|---|
| 210 |                         res.assign(tmp_beg, tmp_size); | 
|---|
| 211 |                 } | 
|---|
| 212 |                 else { // hum..  we need to pad (multi_output, or spacepad present) | 
|---|
| 213 |                     //find where we should pad | 
|---|
| 214 |                     size_type sz = (std::min)(res_size+prefix_space, tmp_size); | 
|---|
| 215 |                     size_type i = prefix_space; | 
|---|
| 216 |                     for(; i<sz && tmp_beg[i] == res[i-prefix_space]; ++i) {} | 
|---|
| 217 |                     if(i>=tmp_size) i=prefix_space; | 
|---|
| 218 |                     res.assign(tmp_beg, i); | 
|---|
| 219 |                                         std::streamsize d = w - static_cast<std::streamsize>(tmp_size); | 
|---|
| 220 |                                         BOOST_ASSERT(d>0); | 
|---|
| 221 |                     res.append(static_cast<size_type>( d ), oss2.fill()); | 
|---|
| 222 |                     res.append(tmp_beg+i, tmp_size-i); | 
|---|
| 223 |                     BOOST_ASSERT(i+(tmp_size-i)+(std::max)(d,(std::streamsize)0)  | 
|---|
| 224 |                                  == static_cast<size_type>(w)); | 
|---|
| 225 |                     BOOST_ASSERT(res.size() == static_cast<size_type>(w)); | 
|---|
| 226 |                 } | 
|---|
| 227 |             } | 
|---|
| 228 |         } | 
|---|
| 229 |         buf.clear_buffer(); | 
|---|
| 230 |     } // end- put(..) | 
|---|
| 231 |  | 
|---|
| 232 |  | 
|---|
| 233 |     template< class Ch, class Tr, class Alloc, class T>  | 
|---|
| 234 |     void distribute (basic_format<Ch,Tr, Alloc>& self, T x) { | 
|---|
| 235 |         // call put(x, ..) on every occurence of the current argument : | 
|---|
| 236 |         if(self.cur_arg_ >= self.num_args_)  { | 
|---|
| 237 |             if( self.exceptions() & too_many_args_bit ) | 
|---|
| 238 |                 boost::throw_exception(too_many_args(self.cur_arg_, self.num_args_));  | 
|---|
| 239 |             else return; | 
|---|
| 240 |         } | 
|---|
| 241 |         for(unsigned long i=0; i < self.items_.size(); ++i) { | 
|---|
| 242 |             if(self.items_[i].argN_ == self.cur_arg_) { | 
|---|
| 243 |                 put<Ch, Tr, Alloc, T> (x, self.items_[i], self.items_[i].res_,  | 
|---|
| 244 |                                 self.buf_, boost::get_pointer(self.loc_) ); | 
|---|
| 245 |             } | 
|---|
| 246 |         } | 
|---|
| 247 |     } | 
|---|
| 248 |  | 
|---|
| 249 |     template<class Ch, class Tr, class Alloc, class T>  | 
|---|
| 250 |     basic_format<Ch, Tr, Alloc>&   | 
|---|
| 251 |     feed (basic_format<Ch,Tr, Alloc>& self, T x) { | 
|---|
| 252 |         if(self.dumped_) self.clear(); | 
|---|
| 253 |         distribute<Ch, Tr, Alloc, T> (self, x); | 
|---|
| 254 |         ++self.cur_arg_; | 
|---|
| 255 |         if(self.bound_.size() != 0) { | 
|---|
| 256 |                 while( self.cur_arg_ < self.num_args_ && self.bound_[self.cur_arg_] ) | 
|---|
| 257 |                     ++self.cur_arg_; | 
|---|
| 258 |         } | 
|---|
| 259 |         return self; | 
|---|
| 260 |     } | 
|---|
| 261 |      | 
|---|
| 262 | } // namespace detail | 
|---|
| 263 | } // namespace io | 
|---|
| 264 | } // namespace boost | 
|---|
| 265 |  | 
|---|
| 266 |  | 
|---|
| 267 | #endif //  BOOST_FORMAT_FEED_ARGS_HPP | 
|---|