8b91ef0999
[SVN r58772]
283 lines
10 KiB
C++
283 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/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;
|
|
}
|
|
|