|
Boost : |
From: Howard Hinnant (hinnant_at_[hidden])
Date: 2001-05-09 14:54:32
On Wednesday, May 9, 2001, at 03:06 PM, Beman Dawes wrote:
> Do make sure that it is easy to do the formatted output needed for
> business
> use, including internationalization. Options for fills, floating
> currency
> indicator, significance starter, various ways to indicate negative
> (included parentheses), separators, etc. All that stuff accountants
> care
> about. If it turns out the standard library's iostream and locale
> mechanisms are strong enough, then just document that. But otherwise
> some
> such facility is bound to be a frequent user need.
Below is sample I/O code for a skeleton Money class that uses
std::locale for parsing and formatting. The Money class is almost
completely ignorant of all formatting and parsing (the exception is that
Money provides a manipulator for choosing between local and
international formats). The manipulator is styled after Matt Austern's
excellent article at
http://www.cuj.com/experts/1902/austern.htm?topic=experts . All of the
other I/O details are in the lib or client supplied money facets (not
shown below):
#include <ios>
#include <istream>
#include <ostream>
#include <locale>
struct Money
{
enum format {local, international};
static void set_format(std::ios_base& s, format f)
{flag(s) = f;}
static format get_format(std::ios_base& s)
{return static_cast<format>(flag(s));}
static long& flag(std::ios_base& s);
long double amount_; // just for example
};
long&
Money::flag(std::ios_base& s)
{
static int n = std::ios_base::xalloc();
return s.iword(n);
}
template<class charT, class traits>
std::basic_ios<charT, traits>&
local(std::basic_ios<charT, traits>& s)
{
Money::set_format(s, Money::local);
return s;
}
template<class charT, class traits>
std::basic_ios<charT, traits>&
international(std::basic_ios<charT, traits>& s)
{
Money::set_format(s, Money::international);
return s;
}
template<class charT, class traits>
std::basic_istream<charT,traits>&
operator >>(std::basic_istream<charT,traits>& is, Money& item)
{
typename std::basic_istream<charT,traits>::sentry ok(is);
if (ok)
{
std::ios_base::iostate err = std::ios_base::goodbit;
try
{
const std::money_get<charT>& mg =
std::use_facet<std::money_get<charT> >
(is.getloc());
mg.get(is, 0, Money::get_format(is) == Money::international,
is, err, item.amount_);
}
catch (...)
{
err |= std::ios_base::badbit | std::ios_base::failbit;
}
is.setstate(err);
}
return is;
}
template<class charT, class traits>
std::basic_ostream<charT, traits>&
operator <<(std::basic_ostream<charT, traits>& os, const Money& item)
{
std::basic_ostream<charT, traits>::sentry ok(os);
if (ok)
{
bool failed;
try
{
const std::money_put<charT>& mp =
std::use_facet<std::money_put<charT> >
(os.getloc());
failed = mp.put(os, Money::get_format(os) == Money::international,
os, os.fill(), item.amount_).failed();
}
catch (...)
{
failed = true;
}
if (failed)
os.setstate(std::ios_base::failbit | std::ios_base::badbit);
}
return os;
}
With this code Money can now parse and format all of the details Beman
brought up, plus a few more. This example had Money hold a long double
data member (for simplicity). What is actually needed is either
conversion to and from long double, or conversion to and from
basic_string<charT>.
Example use:
Money m;
std::cin >> m;
std::cout << international << m;
The default mode is "local" in this example.
-Howard
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk