mirror of
https://github.com/boostorg/spirit.git
synced 2026-01-19 04:42:11 +00:00
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.
282 lines
10 KiB
C++
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;
|
|
}
|
|
|