1 // ----------------------------------------------------------------------------
2 // feed_args.hpp : functions for processing each argument
3 // (feed, feed_manip, and distribute)
4 // ----------------------------------------------------------------------------
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)
10 // See http://www.boost.org/libs/format for library home page
12 // ----------------------------------------------------------------------------
14 #ifndef BOOST_FORMAT_FEED_ARGS_HPP
15 #define BOOST_FORMAT_FEED_ARGS_HPP
17 #include <boost/config.hpp>
18 #include <boost/assert.hpp>
19 #include <boost/throw_exception.hpp>
21 #include <boost/format/format_class.hpp>
22 #include <boost/format/group.hpp>
23 #include <boost/format/detail/msvc_disambiguater.hpp>
29 template<class Ch
, class Tr
, class Alloc
>
30 void mk_str( std::basic_string
<Ch
,Tr
, Alloc
> & res
,
32 typename
std::basic_string
<Ch
,Tr
,Alloc
>::size_type size
,
35 std::ios_base::fmtflags f
,
36 const Ch prefix_space
, // 0 if no space-padding
38 // applies centered/left/right padding to the string [beg, beg+size[
39 // Effects : the result is placed in res.
41 typedef typename
std::basic_string
<Ch
,Tr
,Alloc
>::size_type size_type
;
43 if(w
<=0 || static_cast<size_type
>(w
) <=size
) {
45 res
.reserve(size
+ !!prefix_space
);
47 res
.append(1, prefix_space
);
49 res
.append(beg
, size
);
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
56 n_after
= n
/2, n_before
= n
- n_after
;
58 if(f
& std::ios_base::left
)
62 // now make the res string :
63 if(n_before
) res
.append(static_cast<size_type
>(n_before
), fill_char
);
65 res
.append(1, prefix_space
);
67 res
.append(beg
, size
);
68 if(n_after
) res
.append(static_cast<size_type
>(n_after
), fill_char
);
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"
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);
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);
89 template< class Ch
, class Tr
, class T
> inline
90 void put_head (BOOST_IO_STD basic_ostream
<Ch
, Tr
> &, const T
& ) {
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
98 template< class Ch
, class Tr
, class T
> inline
99 void put_last( BOOST_IO_STD basic_ostream
<Ch
, Tr
> & os
, const T
& x
) {
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
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
& ) {
113 template< class Ch
, class Tr
, class T
> inline
114 void put_last( BOOST_IO_STD basic_ostream
<Ch
, Tr
> & os
, T
& x
) {
118 #endif // -msvc workaround
121 template< class Ch
, class Tr
, class Alloc
, class T
>
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
)
129 // If std::min<unsigned> or std::max<unsigned> are already instantiated
130 // at this point then we get a blizzard of warning messages when we call
131 // those templates with std::size_t as arguments. Weird and very annoyning...
132 #pragma warning(push)
133 #pragma warning(disable:4267)
135 // does the actual conversion of x, with given params, into a string
136 // using the supplied stringbuf.
138 typedef typename basic_format
<Ch
, Tr
, Alloc
>::string_type string_type
;
139 typedef typename basic_format
<Ch
, Tr
, Alloc
>::format_item_t format_item_t
;
140 typedef typename
string_type::size_type size_type
;
142 basic_oaltstringstream
<Ch
, Tr
, Alloc
> oss( &buf
);
143 specs
.fmtstate_
.apply_on(oss
, loc_p
);
145 // the stream format state can be modified by manipulators in the argument :
147 // in case x is a group, apply the manip part of it,
148 // in order to find width
150 const std::ios_base::fmtflags fl
=oss
.flags();
151 const bool internal
= (fl
& std::ios_base::internal
) != 0;
152 const std::streamsize w
= oss
.width();
153 const bool two_stepped_padding
= internal
&& (w
!=0);
156 if(! two_stepped_padding
) {
157 if(w
>0) // handle padding via mk_str, not natively in stream
160 const Ch
* res_beg
= buf
.pbase();
162 if(specs
.pad_scheme_
& format_item_t::spacepad
)
163 if(buf
.pcount()== 0 ||
164 (res_beg
[0] !=oss
.widen('+') && res_beg
[0] !=oss
.widen('-') ))
165 prefix_space
= oss
.widen(' ');
166 size_type res_size
= (std::min
)(
167 static_cast<size_type
>(specs
.truncate_
- !!prefix_space
),
169 mk_str(res
, res_beg
, res_size
, w
, oss
.fill(), fl
,
170 prefix_space
, (specs
.pad_scheme_
& format_item_t::centered
) !=0 );
172 else { // 2-stepped padding
173 // internal can be implied by zeropad, or user-set.
174 // left, right, and centered alignment overrule internal,
175 // but spacepad or truncate might be mixed with internal (using manipulator)
176 put_last( oss
, x
); // may pad
177 const Ch
* res_beg
= buf
.pbase();
178 size_type res_size
= buf
.pcount();
179 bool prefix_space
=false;
180 if(specs
.pad_scheme_
& format_item_t::spacepad
)
181 if(buf
.pcount()== 0 ||
182 (res_beg
[0] !=oss
.widen('+') && res_beg
[0] !=oss
.widen('-') ))
184 if(res_size
== static_cast<size_type
>(w
) && w
<=specs
.truncate_
&& !prefix_space
) {
185 // okay, only one thing was printed and padded, so res is fine
186 res
.assign(res_beg
, res_size
);
188 else { // length w exceeded
189 // either it was multi-output with first output padding up all width..
190 // either it was one big arg and we are fine.
191 // Note that res_size<w is possible (in case of bad user-defined formatting)
192 res
.assign(res_beg
, res_size
);
193 res_beg
=NULL
; // invalidate pointers.
195 // make a new stream, to start re-formatting from scratch :
197 basic_oaltstringstream
<Ch
, Tr
, Alloc
> oss2( &buf
);
198 specs
.fmtstate_
.apply_on(oss2
, loc_p
);
205 if(buf
.pcount()==0 && specs
.pad_scheme_
& format_item_t::spacepad
) {
209 // we now have the minimal-length output
210 const Ch
* tmp_beg
= buf
.pbase();
211 size_type tmp_size
= (std::min
)(static_cast<size_type
>(specs
.truncate_
),
215 if(static_cast<size_type
>(w
) <= tmp_size
) {
216 // minimal length is already >= w, so no padding (cool!)
217 res
.assign(tmp_beg
, tmp_size
);
219 else { // hum.. we need to pad (multi_output, or spacepad present)
220 //find where we should pad
221 size_type sz
= (std::min
)(res_size
+ (prefix_space
? 1 : 0), tmp_size
);
222 size_type i
= prefix_space
;
223 for(; i
<sz
&& tmp_beg
[i
] == res
[i
- (prefix_space
? 1 : 0)]; ++i
) {}
224 if(i
>=tmp_size
) i
=prefix_space
;
225 res
.assign(tmp_beg
, i
);
226 std::streamsize d
= w
- static_cast<std::streamsize
>(tmp_size
);
228 res
.append(static_cast<size_type
>( d
), oss2
.fill());
229 res
.append(tmp_beg
+i
, tmp_size
-i
);
230 BOOST_ASSERT(i
+(tmp_size
-i
)+(std::max
)(d
,(std::streamsize
)0)
231 == static_cast<size_type
>(w
));
232 BOOST_ASSERT(res
.size() == static_cast<size_type
>(w
));
243 template< class Ch
, class Tr
, class Alloc
, class T
>
244 void distribute (basic_format
<Ch
,Tr
, Alloc
>& self
, T x
) {
245 // call put(x, ..) on every occurence of the current argument :
246 if(self
.cur_arg_
>= self
.num_args_
) {
247 if( self
.exceptions() & too_many_args_bit
)
248 boost::throw_exception(too_many_args(self
.cur_arg_
, self
.num_args_
));
251 for(unsigned long i
=0; i
< self
.items_
.size(); ++i
) {
252 if(self
.items_
[i
].argN_
== self
.cur_arg_
) {
253 put
<Ch
, Tr
, Alloc
, T
> (x
, self
.items_
[i
], self
.items_
[i
].res_
,
254 self
.buf_
, boost::get_pointer(self
.loc_
) );
259 template<class Ch
, class Tr
, class Alloc
, class T
>
260 basic_format
<Ch
, Tr
, Alloc
>&
261 feed (basic_format
<Ch
,Tr
, Alloc
>& self
, T x
) {
262 if(self
.dumped_
) self
.clear();
263 distribute
<Ch
, Tr
, Alloc
, T
> (self
, x
);
265 if(self
.bound_
.size() != 0) {
266 while( self
.cur_arg_
< self
.num_args_
&& self
.bound_
[self
.cur_arg_
] )
272 } // namespace detail
277 #endif // BOOST_FORMAT_FEED_ARGS_HPP