]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/spirit/example/karma/printf_style_double_format.cpp
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / libs / spirit / example / karma / printf_style_double_format.cpp
CommitLineData
7c673cae
FG
1// Copyright (c) 2001-2010 Hartmut Kaiser
2//
3// Distributed under the Boost Software License, Version 1.0. (See accompanying
4// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
5
6// The main purpose of this example is to show how a single container type can
7// be formatted using different output grammars.
8
7c673cae
FG
9#include <boost/spirit/include/qi.hpp>
10#include <boost/spirit/include/karma.hpp>
11#include <boost/fusion/include/adapt_struct.hpp>
12
13#include <cmath>
14
15using namespace boost::spirit;
16
17///////////////////////////////////////////////////////////////////////////////
18// This policy allows to use printf style formatting specifiers for Karma
19// floating point generators. This policy understands the following format:
20//
21// The format string must conform to the following format, otherwise a
22// std::runtime_error will be thrown:
23//
24// %[flags][fill][width][.precision]type
25//
26// where:
27// flags (only one possible):
28// +: Always denote the sign '+' or '-' of a number
29// -: Left-align the output
30// fill:
31// 0: Uses 0 instead of spaces to left-fill a fixed-length field
32// width:
33// number: Left-pad the output with spaces until it is at least number
34// characters wide. if number has a leading '0', that is
35// interpreted as a 'fill', the padding is done with '0'
36// characters instead of spaces.
37// precision:
38// number: Causes the decimal portion of the output to be expressed
39// in at least number digits
40// type (only one possible):
41// e: force scientific notation, with a lowercase "e"
42// E: force scientific notation, with a uppercase "E"
43// f: floating point format
44// g: use %e or %f, whichever is shorter
45// G: use %E or %f, whichever is shorter
46//
47
48///////////////////////////////////////////////////////////////////////////////
49// define a data structure and a corresponding parser to hold the formatting
50// information extracted from the format specification string
51namespace client
52{
53 struct format_data
54 {
55 char flag;
56 char fill;
57 int width;
58 int precision;
59 char type;
60 };
61}
62
63// We need to tell fusion about our format_data struct
64// to make it a first-class fusion citizen
65BOOST_FUSION_ADAPT_STRUCT(
66 client::format_data,
67 (char, flag)
68 (char, fill)
69 (int, width)
70 (int, precision)
71 (char, type)
72)
73
74namespace client
75{
76 ///////////////////////////////////////////////////////////////////////////
77 // Grammar for format specification string as described above
78 template <typename Iterator>
79 struct format_grammar : qi::grammar<Iterator, format_data()>
80 {
81 format_grammar() : format_grammar::base_type(format)
82 {
83 using qi::uint_;
84 using qi::attr;
85 using ascii::char_;
86 using ascii::no_case;
87
88 format %= '%' >> flags >> fill >> width >> prec >> type;
89
90 // default flags is right aligned
91 flags = char_('+') | char_('-') | attr(' ');
92 fill = char_('0') | attr(' '); // default fill is space
93 width = uint_ | attr(-1);
94 prec = '.' >> uint_ | attr(3); // default is 3 digits
95 type = no_case[char_('e')] | char_('f') | no_case[char_('g')];
96 };
97
98 qi::rule<Iterator, format_data()> format;
99 qi::rule<Iterator, char()> flags;
100 qi::rule<Iterator, char()> fill;
101 qi::rule<Iterator, int()> width;
102 qi::rule<Iterator, int()> prec;
103 qi::rule<Iterator, char()> type;
104 };
105}
106
107///////////////////////////////////////////////////////////////////////////////
108// real_policies implementation allowing to use a printf style format
109// specification for Karma floating pointing number generators
110template <typename T>
111struct format_policies : karma::real_policies<T>
112{
113 typedef karma::real_policies<T> base_policy_type;
114
115 ///////////////////////////////////////////////////////////////////////////
116 // This real_policies implementation requires the output_iterator to
117 // implement buffering and character counting. This needs to be reflected
118 // in the properties exposed by the generator
119 typedef boost::mpl::int_<
120 karma::generator_properties::countingbuffer
121 > properties;
122
123 ///////////////////////////////////////////////////////////////////////////
124 format_policies(char const* fmt = "%f")
125 {
126 char const* last = fmt;
127 while (*last)
128 last++;
129
130 client::format_grammar<char const*> g;
131 if (!qi::parse(fmt, last, g, format_))
132 throw std::runtime_error("bad format string");
133 }
134
135 ///////////////////////////////////////////////////////////////////////////
136 // returns the overall format: scientific or fixed
137 int floatfield(T n) const
138 {
139 if (format_.type == 'e' || format_.type == 'E')
140 return base_policy_type::fmtflags::scientific;
141
142 if (format_.type == 'f')
143 return base_policy_type::fmtflags::fixed;
144
145 BOOST_ASSERT(format_.type == 'g' || format_.type == 'G');
146 return this->base_policy_type::floatfield(n);
147 }
148
149 ///////////////////////////////////////////////////////////////////////////
150 // returns whether to emit a sign even for non-negative numbers
151 bool const force_sign(T) const
152 {
153 return format_.flag == '+';
154 }
155
156 ///////////////////////////////////////////////////////////////////////////
157 // returns the number of required digits for the fractional part
158 unsigned precision(T) const
159 {
160 return format_.precision;
161 }
162
163 ///////////////////////////////////////////////////////////////////////////
164 // emit the decimal dot
165 template <typename OutputIterator>
166 static bool dot (OutputIterator& sink, T n, unsigned precision)
167 {
168 // don't print the dot if no fractional digits are to be emitted
169 if (precision == 0)
170 return true;
171 return base_policy_type::dot(sink, n, precision);
172 }
173
174 template <typename CharEncoding, typename Tag, typename OutputIterator>
175 bool exponent (OutputIterator& sink, long n) const
176 {
177 if (format_.type == 'E' || format_.type == 'G') {
178 // print exponent symbol in upper case
179 return this->base_policy_type::
180 template exponent<char_encoding::ascii, tag::upper>(sink, n);
181 }
182 return this->base_policy_type::
183 template exponent<CharEncoding, Tag>(sink, n);
184 }
185
186 ///////////////////////////////////////////////////////////////////////////
187 // this gets called by the numeric generators at the top level, it allows
188 // to do alignment and other high level things
189 template <typename Inserter, typename OutputIterator, typename Policies>
190 bool call (OutputIterator& sink, T n, Policies const& p) const
191 {
192 bool r = false;
193 if (format_.flag == '-') { // left align
194 // wrap the given output iterator to allow counting
195 karma::detail::enable_counting<OutputIterator> counting(sink);
196
197 // first generate the actual floating point number
198 r = Inserter::call_n(sink, n, p);
199
200 // pad the output until the max width is reached
201 while(r && int(counting.count()) < format_.width)
202 r = karma::generate(sink, ' ');
203 }
204 else { // right align
205 // wrap the given output iterator to allow left padding
206 karma::detail::enable_buffering<OutputIterator> buffering(
207 sink, format_.width);
208
209 // first generate the actual floating point number
210 {
211 karma::detail::disable_counting<OutputIterator> nocounting(sink);
212 r = Inserter::call_n(sink, n, p);
213 }
214
215 buffering.disable(); // do not perform buffering any more
216
217 // generate the left padding
218 karma::detail::enable_counting<OutputIterator> counting(
219 sink, buffering.buffer_size());
220 while(r && int(counting.count()) < format_.width)
221 r = karma::generate(sink, format_.fill);
222
223 // copy the buffered output to the target output iterator
224 if (r)
225 buffering.buffer_copy();
226 }
227 return r;
228 }
229
230 client::format_data format_;
231};
232
233///////////////////////////////////////////////////////////////////////////////
234// This is the generator usable in any Karma output format expression, it needs
235// to be utilized as
236//
237// generate(sink, real("%6.3f"), 3.1415926536); // prints: ' 3.142'
238//
239// and it supports the format specification as described above.
240typedef karma::real_generator<double, format_policies<double> > real;
241
242///////////////////////////////////////////////////////////////////////////////
243int main()
244{
245 std::cout << "/////////////////////////////////////////////////////////////\n\n";
246 std::cout << "A format driven floating point number generator for Spirit...\n\n";
247 std::cout << "/////////////////////////////////////////////////////////////\n\n";
248
249 std::cout << "Give me a printf style format\n";
250 std::cout << "Type [enter] to quit\n\n";
251
252 std::string str;
253 while (getline(std::cin, str))
254 {
255 if (str.empty())
256 break;
257
258 try {
259 std::string generated;
260 std::back_insert_iterator<std::string> sink(generated);
261 if (!karma::generate(sink, real(str.c_str()), 4*std::atan(1.0)))
262 {
263 std::cout << "-------------------------\n";
264 std::cout << "Generating failed\n";
265 std::cout << "-------------------------\n";
266 }
267 else
268 {
269 std::cout << ">" << generated << "<\n";
270 }
271 }
272 catch (std::runtime_error const&) {
273 std::cout << "-------------------------\n";
274 std::cout << "Invalid format specified!\n";
275 std::cout << "-------------------------\n";
276 }
277 }
278
279 return 0;
280}
281