Back to TOC Columns


Standard C/C++: The Facet money_put

P. J. Plauger

Standard C++ gives you a license to print money, or at least monetary values, if only you can figure out how.


Introduction

You've seen those checks, printed by the government or some big bank, where the amount is printed as something like $***1,234.56. The comma is a nice touch, aiding readability. But those stars are a bit of an insult. They suggest that, unless they stop you, you're going to add a leading digit or three to give yourself an unauthorized bonus. As if that's all it takes to seduce you into a life of crime.

If you're like me, you may have given some thought to how to generate a display of this sort. Certainly, you can't do the job just by calling printf. You can generate the dollar sign, all the digits, and maybe the decimal point by a judicious choice of format strings. You can even get printf to pad with spaces, if not stars, by specifying a field width. But you'll have to replace those spaces and insert commas yourself, after printf has done its limited magic, with a bit of postprocessing logic.

You may not know it, but all that machinery is built right into the Standard C++ library. Well, it's mostly built-in, to be more precise. Or at least it will be, once all libraries catch up with the C++ Standard. Perhaps I should be a bit more specific.

Listing 1 shows a working example program. I've tested it under Microsoft Visual C++ v5.0. As far as I know, it should also work with the earlier VC++ v4.2. And it works with the Dinkum C++ Library shipped with other C++ compilers. It demonstrates how you can take the long double value 123456789.0, convert it to an object of type Money (also defined in Listing 1) , and insert it into cout. The payoff is that the expression statement:

cout << showbase << setw(20)
    << internal << setfill('*')
    << Money(123456789.0) << endl;

generates the output:

$*******1,234,567.89

Neat, huh?

There's a bit more to Listing 1 than just this expression statement, however. Let's look at the pieces one at a time.

First, you have to decide on a way to represent monetary values. Your choices are either as an object of type long double or as an optionally signed sequence of digits stored in an object of class string (defined in the header <string>). Either way, you're counting pennies, not dollars. In Listing 1, I introduce the typedef MoneyVal to provide a single point of definition. It takes only a small amount of work to make the example work with string instead of long double monetary values.

You can't simply insert a MoneyVal value into cout, at least not if you want it to be recognized as a monetary value. It will print as a long double or a string, of course. So I introduce class Money as a wrapper for a MoneyVal object. It is now possible to overload operator<< to act as an inserter for objects of type Money.

Listing 1 includes the full-blown template definition of operator<< for Money objects. It works with any kind of output stream you care to contrive. I basically cribbed an existing template inserter from the header <ostream> and reworked it as needed. Given all the magic that's now required to write a heavy-duty inserter, it's hard to imagine any other way to get the darned thing right.

The macro _USEFAC(loc, fac) is peculiar to my implementation of the Standard C++ library, by the way. It paves over the dialect differences between compilers that support different generations of template processing. The macro expands either to use_facet<fac>(loc) or use_facet(loc, (fac *)0), accordingly. If you don't know why this might be true, don't worry. All that matters for this essay is that the macro obtains a reference to a locale facet which is a specialization of the template class money_put. That template class is the guest of honor this month.

You now have enough machinery to support the expression statement I showed earlier. It will indeed insert a monetary amount into cout. Try it, however, and you will be disappointed in the result. The generated output should look like:

***********123456789

The reason is simple. Left to its own devices, a Standard C++ library uses only locale objects that reflect the "C" locale. As I described last month, this locale has next to nothing interesting to say about monetary formatting. (See "Standard C/C++: The Facet moneypunct," CUJ, March 1998.) The C Standards committee was determined to make ISO C as culturally neutral as possible. Hence, the newly minted LC_MONETARY locale category is so nebbish, by default, as to be equally unusable by all cultures around the world. Talk about your political correctness.

Maybe your C library has nontrivial support for locales, and maybe the Standard C++ library can make good use of it. (This is certainly true for VC++.) If so, then maybe you know the name of a locale that says nice things about monetary formatting. And maybe you know how to use that name to whomp up a suitable locale object. But you don't want to have to count on all those things.

What you can do instead is provide your own parameter values just for monetary formatting. You do so by deriving a new class from class moneypunct<char, false>, which I described last month, then overriding the virtual member functions to behave as you wish. That's what's going on with class Mymoneypunct in Listing 1.

There's one last piece of the puzzle. You have to convince cout to use the newly minted locale facet in place of the one that normally comes with the "C" locale. I did so by creating a new locale object loc and "imbuing" it into cout, with the code:

locale loc = _ADDFAC(locale::classic(),
new Mymoneypunct);
cout.imbue(loc);

The macro _ADDFAC(loc, pfac) is also peculiar to my implementation of the Standard C++ library, just like _USEFAC above. It expands either to the constructor locale(loc, pfac) or to the special template function _Addfac(loc, pfac). In either case, it returns a new locale object that is a copy of the object loc with the facet pointed to by pfac inserted into it — possibly in place of a facet of the same basic type. Once again, I gloss over a considerable amount of magic that you don't have to understand to appreciate the rest of this essay. Suffice it to say that the newly imbued locale object tells the appropriate money_put facet how to generate the display you desire.

And that's all there is to it.

Template Class money_put

Template class money_put behaves much like template class num_put. (See "Standard C/C++: The Facets num_put and numpunct," CUJ, January 1998.) Regular readers of this column should know how facets work by now; intermittent readers probably don't care. I confine my repetition to a description of how the inserter in Listing 1 makes use of money_put:

Listing 2 shows one way to implement (most of) template class money_put. As usual, I have omitted most of the implementation-specific magic code, particularly the stuff that initializes a new object. It's a distraction. Most of the interesting action, as usual, occurs in the two overloads for do_put. (You override these virtual member functions, as needed, in a derived class to produce your own flavor of a num_put facet.)

Both result in a call to the private member function _Putmfld, which formats a digit sequence as desired. _Putmfld queries the associated moneypunct facet from the same locale object to obtain values for the various monetary parameters. In particular, it chooses a formatting pattern, based on the sign of the monetary value, to determine what elements to display in what order.

I remind you once more that the macro _WIDEN converts a member of the basic C character set, as type char, to the element type. Typically, this involves little or no work.

To really understand template class money_put, you have to understand the two versions of do_put, and how they interpret a formatting pattern:

virtual iter_type do_put(iter_type next, bool intl,
    ios_base& x, E fill, string_type& val) const;
virtual iter_type do_put(iter_type next, bool intl,
    ios_base& x, E fill, long double& val) const;

The first virtual protected member function generates sequential elements beginning at next to produce a monetary output field from the string_type object val. The sequence controlled by val must begin with one or more decimal digits, optionally preceded by a minus sign (-), which represents the amount. The function returns an iterator designating the first element beyond the generated monetary output field.

The second virtual protected member function behaves the same as the first, except that it effectively first converts val to a sequence of decimal digits, optionally preceded by a minus sign, then converts that sequence as above.

The format of a monetary output field is determined by the locale facet fac returned by the (effective) call use_facet<moneypunct<E, intl>(x.getloc()). Specifically:

If the sign string (fac.negative_sign or fac.positive_sign) has more than one element, only the first element is generated where the element equal to money_base::sign appears in the format pattern (fac.neg_format or fac.pos_format). Any remaining elements are generated at the end of the monetary output field.

If x.flags() & showbase is nonzero, the string fac.curr_symbol is generated where the element equal to money_base::symbol appears in the format pattern. Otherwise, no currency symbol is generated.

If no grouping constraints are imposed by fac.grouping() (its first element has the value CHAR_MAX), then no instances of fac.thousands_sep() are generated in the value portion of the monetary output field (where the element equal to money_base::value appears in the format pattern). If fac.frac_digits() is zero, then no instance of fac.decimal_point() is generated after the decimal digits. Otherwise, the resulting monetary output field places the low-order fac.frac_digits() decimal digits to the right of the decimal point.

Padding occurs as for any numeric output field, except that if x.flags() & x.internal is nonzero, any internal padding is generated where the element equal to money_base::space appears in the format pattern, if it does appear. Otherwise, internal padding occurs before the generated sequence. The padding character is fill.

The function calls x.width(0) to reset the field width to zero.

And that's all there is to it. If you can work your way through this description, you can contrive a whole host of ways to display monetary values. All it takes is patience and a willingness to experiment.

Conclusion

Printing checks is, of course, only one possible use for template class money_put. It is presumably helpful in any program that needs to display monetary values — particularly if it wants to adapt well to multiple locales. Is it really useful enough in practice to warrant being a part of the Standard C++ library? I can't say. This feature is much too new to have generated much data from the field. I can say that I've received few if any questions on it in over two years of maintenance.

Template class money_put has one significant drawback. It describes two facets that are supposed to be part of every locale object:

money_put<char,
    ostreambuf_iterator<char,
	char_traits<char> > >
money_put<wchar_t,
    ostreambuf_iterator<wchar_t,
	char_traits<wchar_t> > >

Almost all the action, in both cases, is in virtual member functions and the private member functions they call. A typical implementation must thus load most of the code for both these facets regardless of whether the program ever makes use of them. (My implementation doesn't load all this stuff, but only thanks to some heroic measures.) That's how you can end up with a trivial program that occupies upwards of a megabyte of storage. It may be nice to print pretty checks from a C++ program with little added code, but I doubt the benefit is worth a tax on every C++ program that uses the standard input or output streams.

On the other hand, you can take this as an argument for using the money_put facets as often as possible. You're paying for them anyway, so why not use them? o

P.J. Plauger is Senior Editor of C/C++ Users Journal and President of Dinkumware, Ltd. He is the author of the Standard C++ Library shipped with Microsoft's Visual C++, v5.0. For eight years, he served as convener of the ISO C standards committee, WG14. He remains active on the C++ committee, J16. His latest books are The Draft Standard C++ Library, Programming on Purpose (three volumes), and Standard C (with Jim Brodie), all published by Prentice-Hall. You can reach him at pjp@plauger.com.