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> mk_str(std::basic_string<Ch,Tr,Alloc> & res,const Ch * beg,typename std::basic_string<Ch,Tr,Alloc>::size_type size,std::streamsize w,const Ch fill_char,std::ios_base::fmtflags f,const Ch prefix_space,bool center)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(static_cast<size_type>(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(static_cast<size_type>(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(static_cast<size_type>(n_after), fill_char); 69 } 70 } // -mk_str(..) 71 72 73 #if BOOST_WORKAROUND(__DECCXX_VER, BOOST_TESTED_AT(60590042)) 74 // __DECCXX needs to be tricked to disambiguate this simple overload.. 75 // the trick is in "boost/format/msvc_disambiguater.hpp" 76 77 template< class Ch, class Tr, class T> inline put_head(BOOST_IO_STD basic_ostream<Ch,Tr> & os,const T & x)78 void put_head (BOOST_IO_STD basic_ostream<Ch, Tr> & os, const T& x ) { 79 disambiguater<Ch, Tr, T>::put_head(os, x, 1L); 80 } 81 template< class Ch, class Tr, class T> inline put_last(BOOST_IO_STD basic_ostream<Ch,Tr> & os,const T & x)82 void put_last (BOOST_IO_STD basic_ostream<Ch, Tr> & os, const T& x ) { 83 disambiguater<Ch, Tr, T>::put_last(os, x, 1L); 84 } 85 86 #else 87 88 template< class Ch, class Tr, class T> inline put_head(BOOST_IO_STD basic_ostream<Ch,Tr> &,const T &)89 void put_head (BOOST_IO_STD basic_ostream<Ch, Tr> &, const T& ) { 90 } 91 92 template< class Ch, class Tr, class T> inline put_head(BOOST_IO_STD basic_ostream<Ch,Tr> & os,const group1<T> & x)93 void put_head( BOOST_IO_STD basic_ostream<Ch, Tr> & os, const group1<T>& x ) { 94 os << group_head(x.a1_); // send the first N-1 items, not the last 95 } 96 97 template< class Ch, class Tr, class T> inline put_last(BOOST_IO_STD basic_ostream<Ch,Tr> & os,const T & x)98 void put_last( BOOST_IO_STD basic_ostream<Ch, Tr> & os, const T& x ) { 99 os << x ; 100 } 101 102 template< class Ch, class Tr, class T> inline put_last(BOOST_IO_STD basic_ostream<Ch,Tr> & os,const group1<T> & x)103 void put_last( BOOST_IO_STD basic_ostream<Ch, Tr> & os, const group1<T>& x ) { 104 os << group_last(x.a1_); // this selects the last element 105 } 106 107 #ifndef BOOST_NO_OVERLOAD_FOR_NON_CONST 108 template< class Ch, class Tr, class T> inline put_head(BOOST_IO_STD basic_ostream<Ch,Tr> &,T &)109 void put_head( BOOST_IO_STD basic_ostream<Ch, Tr> &, T& ) { 110 } 111 112 template< class Ch, class Tr, class T> inline put_last(BOOST_IO_STD basic_ostream<Ch,Tr> & os,T & x)113 void put_last( BOOST_IO_STD basic_ostream<Ch, Tr> & os, T& x) { 114 os << x ; 115 } 116 #endif 117 #endif // -__DECCXX workaround 118 119 template< class Ch, class Tr, class T> call_put_head(BOOST_IO_STD basic_ostream<Ch,Tr> & os,const void * x)120 void call_put_head(BOOST_IO_STD basic_ostream<Ch, Tr> & os, const void* x) { 121 put_head(os, *(static_cast<T const *>(x))); 122 } 123 124 template< class Ch, class Tr, class T> call_put_last(BOOST_IO_STD basic_ostream<Ch,Tr> & os,const void * x)125 void call_put_last(BOOST_IO_STD basic_ostream<Ch, Tr> & os, const void* x) { 126 put_last(os, *(static_cast<T const *>(x))); 127 } 128 129 template< class Ch, class Tr> 130 struct put_holder { 131 template<class T> put_holderboost::io::detail::put_holder132 put_holder(T& t) 133 : arg(&t), 134 put_head(&call_put_head<Ch, Tr, T>), 135 put_last(&call_put_last<Ch, Tr, T>) 136 {} 137 const void* arg; 138 void (*put_head)(BOOST_IO_STD basic_ostream<Ch, Tr> & os, const void* x); 139 void (*put_last)(BOOST_IO_STD basic_ostream<Ch, Tr> & os, const void* x); 140 }; 141 142 template< class Ch, class Tr> inline put_head(BOOST_IO_STD basic_ostream<Ch,Tr> & os,const put_holder<Ch,Tr> & t)143 void put_head( BOOST_IO_STD basic_ostream<Ch, Tr> & os, const put_holder<Ch, Tr>& t) { 144 t.put_head(os, t.arg); 145 } 146 147 template< class Ch, class Tr> inline put_last(BOOST_IO_STD basic_ostream<Ch,Tr> & os,const put_holder<Ch,Tr> & t)148 void put_last( BOOST_IO_STD basic_ostream<Ch, Tr> & os, const put_holder<Ch, Tr>& t) { 149 t.put_last(os, t.arg); 150 } 151 152 153 template< class Ch, class Tr, class Alloc, class T> put(T x,const format_item<Ch,Tr,Alloc> & specs,typename basic_format<Ch,Tr,Alloc>::string_type & res,typename basic_format<Ch,Tr,Alloc>::internal_streambuf_t & buf,io::detail::locale_t * loc_p=NULL)154 void put( T x, 155 const format_item<Ch, Tr, Alloc>& specs, 156 typename basic_format<Ch, Tr, Alloc>::string_type& res, 157 typename basic_format<Ch, Tr, Alloc>::internal_streambuf_t & buf, 158 io::detail::locale_t *loc_p = NULL) 159 { 160 #ifdef BOOST_MSVC 161 // If std::min<unsigned> or std::max<unsigned> are already instantiated 162 // at this point then we get a blizzard of warning messages when we call 163 // those templates with std::size_t as arguments. Weird and very annoyning... 164 #pragma warning(push) 165 #pragma warning(disable:4267) 166 #endif 167 // does the actual conversion of x, with given params, into a string 168 // using the supplied stringbuf. 169 170 typedef typename basic_format<Ch, Tr, Alloc>::string_type string_type; 171 typedef typename basic_format<Ch, Tr, Alloc>::format_item_t format_item_t; 172 typedef typename string_type::size_type size_type; 173 174 basic_oaltstringstream<Ch, Tr, Alloc> oss( &buf); 175 176 #if !defined(BOOST_NO_STD_LOCALE) 177 if(loc_p != NULL) 178 oss.imbue(*loc_p); 179 #endif 180 181 specs.fmtstate_.apply_on(oss, loc_p); 182 183 // the stream format state can be modified by manipulators in the argument : 184 put_head( oss, x ); 185 // in case x is a group, apply the manip part of it, 186 // in order to find width 187 188 const std::ios_base::fmtflags fl=oss.flags(); 189 const bool internal = (fl & std::ios_base::internal) != 0; 190 const std::streamsize w = oss.width(); 191 const bool two_stepped_padding= internal && (w!=0); 192 193 res.resize(0); 194 if(! two_stepped_padding) { 195 if(w>0) // handle padding via mk_str, not natively in stream 196 oss.width(0); 197 put_last( oss, x); 198 const Ch * res_beg = buf.pbase(); 199 Ch prefix_space = 0; 200 if(specs.pad_scheme_ & format_item_t::spacepad) 201 if(buf.pcount()== 0 || 202 (res_beg[0] !=oss.widen('+') && res_beg[0] !=oss.widen('-') )) 203 prefix_space = oss.widen(' '); 204 size_type res_size = (std::min)( 205 (static_cast<size_type>((specs.truncate_ & (std::numeric_limits<size_type>::max)())) - !!prefix_space), 206 buf.pcount() ); 207 mk_str(res, res_beg, res_size, w, oss.fill(), fl, 208 prefix_space, (specs.pad_scheme_ & format_item_t::centered) !=0 ); 209 } 210 else { // 2-stepped padding 211 // internal can be implied by zeropad, or user-set. 212 // left, right, and centered alignment overrule internal, 213 // but spacepad or truncate might be mixed with internal (using manipulator) 214 put_last( oss, x); // may pad 215 const Ch * res_beg = buf.pbase(); 216 size_type res_size = buf.pcount(); 217 bool prefix_space=false; 218 if(specs.pad_scheme_ & format_item_t::spacepad) 219 if(buf.pcount()== 0 || 220 (res_beg[0] !=oss.widen('+') && res_beg[0] !=oss.widen('-') )) 221 prefix_space = true; 222 if(res_size == static_cast<size_type>(w) && w<=specs.truncate_ && !prefix_space) { 223 // okay, only one thing was printed and padded, so res is fine 224 res.assign(res_beg, res_size); 225 } 226 else { // length w exceeded 227 // either it was multi-output with first output padding up all width.. 228 // either it was one big arg and we are fine. 229 // Note that res_size<w is possible (in case of bad user-defined formatting) 230 res.assign(res_beg, res_size); 231 res_beg=NULL; // invalidate pointers. 232 233 // make a new stream, to start re-formatting from scratch : 234 buf.clear_buffer(); 235 basic_oaltstringstream<Ch, Tr, Alloc> oss2( &buf); 236 specs.fmtstate_.apply_on(oss2, loc_p); 237 put_head( oss2, x ); 238 239 oss2.width(0); 240 if(prefix_space) 241 oss2 << ' '; 242 put_last(oss2, x ); 243 if(buf.pcount()==0 && specs.pad_scheme_ & format_item_t::spacepad) { 244 prefix_space =true; 245 oss2 << ' '; 246 } 247 // we now have the minimal-length output 248 const Ch * tmp_beg = buf.pbase(); 249 size_type tmp_size = (std::min)( 250 (static_cast<size_type>(specs.truncate_ & (std::numeric_limits<size_type>::max)())), 251 buf.pcount()); 252 253 if(static_cast<size_type>(w) <= tmp_size) { 254 // minimal length is already >= w, so no padding (cool!) 255 res.assign(tmp_beg, tmp_size); 256 } 257 else { // hum.. we need to pad (multi_output, or spacepad present) 258 //find where we should pad 259 size_type sz = (std::min)(res_size + (prefix_space ? 1 : 0), tmp_size); 260 size_type i = prefix_space; 261 for(; i<sz && tmp_beg[i] == res[i - (prefix_space ? 1 : 0)]; ++i) {} 262 if(i>=tmp_size) i=prefix_space; 263 res.assign(tmp_beg, i); 264 std::streamsize d = w - static_cast<std::streamsize>(tmp_size); 265 BOOST_ASSERT(d>0); 266 res.append(static_cast<size_type>( d ), oss2.fill()); 267 res.append(tmp_beg+i, tmp_size-i); 268 BOOST_ASSERT(i+(tmp_size-i)+(std::max)(d,(std::streamsize)0) 269 == static_cast<size_type>(w)); 270 BOOST_ASSERT(res.size() == static_cast<size_type>(w)); 271 } 272 } 273 } 274 buf.clear_buffer(); 275 #ifdef BOOST_MSVC 276 #pragma warning(pop) 277 #endif 278 } // end- put(..) 279 280 281 template< class Ch, class Tr, class Alloc, class T> distribute(basic_format<Ch,Tr,Alloc> & self,T x)282 void distribute (basic_format<Ch,Tr, Alloc>& self, T x) { 283 // call put(x, ..) on every occurrence of the current argument : 284 if(self.cur_arg_ >= self.num_args_) { 285 if( self.exceptions() & too_many_args_bit ) 286 boost::throw_exception(too_many_args(self.cur_arg_, self.num_args_)); 287 else return; 288 } 289 for(unsigned long i=0; i < self.items_.size(); ++i) { 290 if(self.items_[i].argN_ == self.cur_arg_) { 291 put<Ch, Tr, Alloc, T> (x, self.items_[i], self.items_[i].res_, 292 self.buf_, boost::get_pointer(self.loc_) ); 293 } 294 } 295 } 296 297 template<class Ch, class Tr, class Alloc, class T> 298 basic_format<Ch, Tr, Alloc>& feed_impl(basic_format<Ch,Tr,Alloc> & self,T x)299 feed_impl (basic_format<Ch,Tr, Alloc>& self, T x) { 300 if(self.dumped_) self.clear(); 301 distribute<Ch, Tr, Alloc, T> (self, x); 302 ++self.cur_arg_; 303 if(self.bound_.size() != 0) { 304 while( self.cur_arg_ < self.num_args_ && self.bound_[self.cur_arg_] ) 305 ++self.cur_arg_; 306 } 307 return self; 308 } 309 310 template<class Ch, class Tr, class Alloc, class T> inline 311 basic_format<Ch, Tr, Alloc>& feed(basic_format<Ch,Tr,Alloc> & self,T x)312 feed (basic_format<Ch,Tr, Alloc>& self, T x) { 313 return feed_impl<Ch, Tr, Alloc, const put_holder<Ch, Tr>&>(self, put_holder<Ch, Tr>(x)); 314 } 315 316 } // namespace detail 317 } // namespace io 318 } // namespace boost 319 320 321 #endif // BOOST_FORMAT_FEED_ARGS_HPP 322