2
0
mirror of https://github.com/boostorg/spirit.git synced 2026-01-19 04:42:11 +00:00
Files
spirit/example/karma/printf_style_double_format.cpp
Nikita Kniazev f44479bcd3 Remove boost/config/warning_disable.hpp usage
It is better to manage warnings on our side to know what warnings we need to fix or suppress, and the only thing that header does is disabling deprecation warnings on MSVC and ICC which we would prefer to not show to users.
2021-08-24 03:14:12 +03:00

282 lines
10 KiB
C++

// 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/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;
}