Boost logo

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