123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282 |
- // Copyright (c) 2001-2010 Hartmut Kaiser
- //
- // Distributed under the Boost Software License, Version 1.0. (See accompanying
- // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- // The main purpose of this example is to show how a single container type can
- // be formatted using different output grammars.
- #include <boost/config/warning_disable.hpp>
- #include <boost/spirit/include/qi.hpp>
- #include <boost/spirit/include/karma.hpp>
- #include <boost/fusion/include/adapt_struct.hpp>
- #include <cmath>
- using namespace boost::spirit;
- ///////////////////////////////////////////////////////////////////////////////
- // This policy allows to use printf style formatting specifiers for Karma
- // floating point generators. This policy understands the following format:
- //
- // The format string must conform to the following format, otherwise a
- // std::runtime_error will be thrown:
- //
- // %[flags][fill][width][.precision]type
- //
- // where:
- // flags (only one possible):
- // +: Always denote the sign '+' or '-' of a number
- // -: Left-align the output
- // fill:
- // 0: Uses 0 instead of spaces to left-fill a fixed-length field
- // width:
- // number: Left-pad the output with spaces until it is at least number
- // characters wide. if number has a leading '0', that is
- // interpreted as a 'fill', the padding is done with '0'
- // characters instead of spaces.
- // precision:
- // number: Causes the decimal portion of the output to be expressed
- // in at least number digits
- // type (only one possible):
- // e: force scientific notation, with a lowercase "e"
- // E: force scientific notation, with a uppercase "E"
- // f: floating point format
- // g: use %e or %f, whichever is shorter
- // G: use %E or %f, whichever is shorter
- //
- ///////////////////////////////////////////////////////////////////////////////
- // define a data structure and a corresponding parser to hold the formatting
- // information extracted from the format specification string
- namespace client
- {
- struct format_data
- {
- char flag;
- char fill;
- int width;
- int precision;
- char type;
- };
- }
- // We need to tell fusion about our format_data struct
- // to make it a first-class fusion citizen
- BOOST_FUSION_ADAPT_STRUCT(
- client::format_data,
- (char, flag)
- (char, fill)
- (int, width)
- (int, precision)
- (char, type)
- )
- namespace client
- {
- ///////////////////////////////////////////////////////////////////////////
- // Grammar for format specification string as described above
- template <typename Iterator>
- struct format_grammar : qi::grammar<Iterator, format_data()>
- {
- format_grammar() : format_grammar::base_type(format)
- {
- using qi::uint_;
- using qi::attr;
- using ascii::char_;
- using ascii::no_case;
- format %= '%' >> flags >> fill >> width >> prec >> type;
- // default flags is right aligned
- flags = char_('+') | char_('-') | attr(' ');
- fill = char_('0') | attr(' '); // default fill is space
- width = uint_ | attr(-1);
- prec = '.' >> uint_ | attr(3); // default is 3 digits
- type = no_case[char_('e')] | char_('f') | no_case[char_('g')];
- };
- qi::rule<Iterator, format_data()> format;
- qi::rule<Iterator, char()> flags;
- qi::rule<Iterator, char()> fill;
- qi::rule<Iterator, int()> width;
- qi::rule<Iterator, int()> prec;
- qi::rule<Iterator, char()> type;
- };
- }
- ///////////////////////////////////////////////////////////////////////////////
- // real_policies implementation allowing to use a printf style format
- // specification for Karma floating pointing number generators
- template <typename T>
- struct format_policies : karma::real_policies<T>
- {
- typedef karma::real_policies<T> base_policy_type;
- ///////////////////////////////////////////////////////////////////////////
- // This real_policies implementation requires the output_iterator to
- // implement buffering and character counting. This needs to be reflected
- // in the properties exposed by the generator
- typedef boost::mpl::int_<
- karma::generator_properties::countingbuffer
- > properties;
- ///////////////////////////////////////////////////////////////////////////
- format_policies(char const* fmt = "%f")
- {
- char const* last = fmt;
- while (*last)
- last++;
- client::format_grammar<char const*> g;
- if (!qi::parse(fmt, last, g, format_))
- throw std::runtime_error("bad format string");
- }
- ///////////////////////////////////////////////////////////////////////////
- // returns the overall format: scientific or fixed
- int floatfield(T n) const
- {
- if (format_.type == 'e' || format_.type == 'E')
- return base_policy_type::fmtflags::scientific;
- if (format_.type == 'f')
- return base_policy_type::fmtflags::fixed;
- BOOST_ASSERT(format_.type == 'g' || format_.type == 'G');
- return this->base_policy_type::floatfield(n);
- }
- ///////////////////////////////////////////////////////////////////////////
- // returns whether to emit a sign even for non-negative numbers
- bool const force_sign(T) const
- {
- return format_.flag == '+';
- }
- ///////////////////////////////////////////////////////////////////////////
- // returns the number of required digits for the fractional part
- unsigned precision(T) const
- {
- return format_.precision;
- }
- ///////////////////////////////////////////////////////////////////////////
- // emit the decimal dot
- template <typename OutputIterator>
- static bool dot (OutputIterator& sink, T n, unsigned precision)
- {
- // don't print the dot if no fractional digits are to be emitted
- if (precision == 0)
- return true;
- return base_policy_type::dot(sink, n, precision);
- }
- template <typename CharEncoding, typename Tag, typename OutputIterator>
- bool exponent (OutputIterator& sink, long n) const
- {
- if (format_.type == 'E' || format_.type == 'G') {
- // print exponent symbol in upper case
- return this->base_policy_type::
- template exponent<char_encoding::ascii, tag::upper>(sink, n);
- }
- return this->base_policy_type::
- template exponent<CharEncoding, Tag>(sink, n);
- }
- ///////////////////////////////////////////////////////////////////////////
- // this gets called by the numeric generators at the top level, it allows
- // to do alignment and other high level things
- template <typename Inserter, typename OutputIterator, typename Policies>
- bool call (OutputIterator& sink, T n, Policies const& p) const
- {
- bool r = false;
- if (format_.flag == '-') { // left align
- // wrap the given output iterator to allow counting
- karma::detail::enable_counting<OutputIterator> counting(sink);
- // first generate the actual floating point number
- r = Inserter::call_n(sink, n, p);
- // pad the output until the max width is reached
- while(r && int(counting.count()) < format_.width)
- r = karma::generate(sink, ' ');
- }
- else { // right align
- // wrap the given output iterator to allow left padding
- karma::detail::enable_buffering<OutputIterator> buffering(
- sink, format_.width);
- // first generate the actual floating point number
- {
- karma::detail::disable_counting<OutputIterator> nocounting(sink);
- r = Inserter::call_n(sink, n, p);
- }
- buffering.disable(); // do not perform buffering any more
- // generate the left padding
- karma::detail::enable_counting<OutputIterator> counting(
- sink, buffering.buffer_size());
- while(r && int(counting.count()) < format_.width)
- r = karma::generate(sink, format_.fill);
- // copy the buffered output to the target output iterator
- if (r)
- buffering.buffer_copy();
- }
- return r;
- }
- client::format_data format_;
- };
- ///////////////////////////////////////////////////////////////////////////////
- // This is the generator usable in any Karma output format expression, it needs
- // to be utilized as
- //
- // generate(sink, real("%6.3f"), 3.1415926536); // prints: ' 3.142'
- //
- // and it supports the format specification as described above.
- typedef karma::real_generator<double, format_policies<double> > real;
- ///////////////////////////////////////////////////////////////////////////////
- int main()
- {
- std::cout << "/////////////////////////////////////////////////////////////\n\n";
- std::cout << "A format driven floating point number generator for Spirit...\n\n";
- std::cout << "/////////////////////////////////////////////////////////////\n\n";
- std::cout << "Give me a printf style format\n";
- std::cout << "Type [enter] to quit\n\n";
- std::string str;
- while (getline(std::cin, str))
- {
- if (str.empty())
- break;
- try {
- std::string generated;
- std::back_insert_iterator<std::string> sink(generated);
- if (!karma::generate(sink, real(str.c_str()), 4*std::atan(1.0)))
- {
- std::cout << "-------------------------\n";
- std::cout << "Generating failed\n";
- std::cout << "-------------------------\n";
- }
- else
- {
- std::cout << ">" << generated << "<\n";
- }
- }
- catch (std::runtime_error const&) {
- std::cout << "-------------------------\n";
- std::cout << "Invalid format specified!\n";
- std::cout << "-------------------------\n";
- }
- }
- return 0;
- }
|