If you can print monetary values, it's nice to be able to read them back in as well. And you can, thanks to the Standard C++ library.
Introduction
Last month, I showed how you can display monetary amounts using a little-known facility built into the Standard C++ library. Template class money_put is a facet much like num_put, except that it converts an internal value to a monetary field (such as $1,234.56) instead of a numeric field (such as 123456). (See "Standard C/C++: The Facet money_put," CUJ, April 1998.) With a little bit of setup, you can exercise considerable control over how a monetary value gets converted. You can even define your own inserter for printing monetary values through cout or some other output stream.
I continue the story this month by showing the other side of the coin, as it were. Any time you can generate formatted output, it's usually handy to be able to scan much the same sort of thing you generate and recover the original value. Just as the C library function printf has its companion scanf, and the C++ locale facet num_put has its num_get, so too does money_put have its buddy money_get.
Listing 1 shows one way to use money_get. It is an augmented form of the example I showed last month. All the previous example did was construct a Money object with the value 123456789.0 (in pennies) and insert it into cout. I chose formatting options such that the output was:
$*******1,234,567.89To that example, I've now added a template extractor overloaded on the class Money. And the insertion expression is replaced by a loop that extracts a monetary value from cin and inserts it into cout. The example in Listing 1 thus echoes each value you type in "check" format, as in:
1 $***************1.00 -123 $************-123.00 1234567 $*******1,234,567.00I've tested the example under Microsoft Visual C++ v5.0. It should also work on any compiler that includes the Dinkum C++ Library. Other implementations of the Standard C++ library are beginning to support locale facets as well, but many do not.
I described the operation of most of the example last month. I wrote the template extractor for Money much the same way I wrote the inserter before, by cribbing code from an existing extractor in the header <istream> and changing it as needed. As you can see, both inserters and extractors are nontrivial critters these days. It's hard to get either completely correct when writing from scratch.
It's a tiresome amount of setup to perform, but once you get it right you can, of course, read lots of input with the same machinery.
Template Class money_get
Template class money_get behaves much like template class num_get. (See "Standard C/C++: The Facet num_get," CUJ, February 1998.) It is a facet like many of the others I've described these past several months. (For the basics of locales and facets, see "Standard C/C++: Introduction to Locales," CUJ, October 1997.)
I repeat here just the description of how the extractor in Listing 1 makes use of money_get:
- The member function ios_base::getloc obtains a copy of the locale object "imbued" into the object of class basic_istreambuf<_E, _Tr>.
- The template function use_facet<_Mget> obtains from its locale object argument a const reference to the facet _Mget (a typedef for money_get<_E, _Iter> here), which should always be present.
- The facet reference _Fac can be used to call either of the two versions of _Fac.get to extract a field (sequence of elements) from the input stream and convert it.
- _Fac.get extracts elements from the stream buffer (obtained by calling _Myios::rdbuf) using an input iterator of type _Iter (a typedef for istreambuf_iterator<_E, _Tr>) to assemble the input field.
- _Fac.get obtains stored formatting information from the object of class basic_istream<_E, _Tr> (_I).
- _Fac.get stores the converted field value in the object _V (of type long double in this case). It can also throw an exception if anything goes wrong along the way.
- The extractor ensures that the converted value was obtained successfully, then stores the converted value in _X as an object of type Money.
Listing 2 shows one way to implement (most of) template class money_get. As usual, I omit most of the implementation-specific magic code. Most of the interesting action, also as usual, occurs in the two overloads for do_get. (You override these virtual member functions, as needed, in a derived class to produce your own flavor of a money_get facet.)
Both result in a call to the private member function _Getmfld, which extracts and converts a monetary field as desired. _Getmfld 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 extract 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. Similarly, the macro _NARROW converts an element of an arbitrary character type E to a char value in the basic C character set, or to the null character if that is not possible.
To really understand template class money_get, you have to understand the two versions of do_get, and how they interpret a formatting pattern:
virtual iter_type do_get(iter_type first, iter_type last, bool intl, ios_base& x, ios_base::iostate& st, string_type& val) const; virtual iter_type do_get(iter_type first, iter_type last, bool intl, ios_base& x, ios_base::iostate& st, long double& val) const;The first virtual protected member function endeavors to match sequential elements beginning at first in the sequence [first, last) until it has recognized a complete, nonempty monetary input field. If successful, it converts this field to a sequence of one or more decimal digits, optionally preceded by a minus sign (-), to represent the amount and stores the result in the string_type object val. It returns an iterator designating the first element beyond the monetary input field. Otherwise, the function stores an empty sequence in val and sets ios_base::failbit in st. It returns an iterator designating the first element beyond any prefix of a valid monetary input field. In either case, if the return value equals last, the function sets ios_base::eofbit in st.
The second virtual protected member function behaves the same as the first, except that if successful it converts the optionally signed digit sequence to a value of type long double and stores that value in val.
The format of a monetary input field is determined by the locale facet fac returned by the (effective) call use_facet<moneypunct<E, intl> >(x.getloc()). Specifically:
- fac.neg_format determines the order in which components of the field occur.
- fac.curr_symbol determines the sequence of elements that constitutes a currency symbol.
- fac.positive_sign determines the sequence of elements that constitutes a positive sign.
- fac.negative_sign determines the sequence of elements that constitutes a negative sign.
- fac.grouping determines how digits are grouped to the left of any decimal point.
- fac.thousands_sep determines the element that separates groups of digits to the left of any decimal point.
- fac.decimal_point determines the element that separates the integer digits from the fraction digits.
- fac.frac_digits determines the number of significant fraction digits to the right of any decimal point.
If the sign string (fac.negative_sign or fac.positive_sign) has more than one element, only the first element is matched where the element equal to money_base::sign appears in the format pattern (fac.neg_format). Any remaining elements are matched at the end of the monetary input field. If neither string has a first element that matches the next element in the monetary input field, the sign string is taken as empty and the sign is positive.
If x.flags() & showbase is nonzero, the string fac.curr_symbol must match where the element equal to money_base::symbol appears in the format pattern. Otherwise, if money_base::symbol occurs at the end of the format pattern, and if no elements of the sign string remain to be matched, the currency symbol is not matched. Otherwise, the currency symbol is optionally matched.
If no instances of fac.thousands_sep() occur in the value portion of the monetary input field (where the element equal to money_base::value appears in the format pattern), no grouping constraint is imposed. Otherwise, any grouping constraints imposed by fac.grouping() is enforced. Note that the resulting digit sequence represents an integer whose low-order fac.frac_digits() decimal digits are considered to the right of the decimal point.
Arbitrary white space is matched where the element equal to money_base::space appears in the format pattern, if it appears other than at the end of the format pattern. Otherwise, no internal white space is matched. An element c is considered white space if use_facet<ctype<E> >(x.getloc()). is(ctype_base::space, c) is true.
Conclusion
Template class money_get shares the same drawback that money_put and moneypunct have. It describes two facets that are supposed to be part of every locale object:
money_get<char, istreambuf_iterator<char, char_traits<char> > > money_get<wchar_t, istreambuf_iterator<wchar_t, char_traits<wchar_t> > >Both facets define virtual member functions, which in turn call nontrivial private member functions. A typical implementation (but not mine) must thus load most of the code for both these facets regardless of whether the program ever makes use of them. So you get all this code whether you like it or not. If I were you, I wouldn't like it.
You can't extract every possible monetary value that you can insert, of course, any more than scanf can be made to eat anything that printf emits. In fact, the display I show above is an example of such a one-way conversion. There's no provision in money_get for skipping over all those stars you can only skip white space. But it's easy enough to contrive output formats that can later be read back in, or input formats that are reasonably tolerant of the sort of things human beings want to type. The trick is contriving a moneypunct facet that has all the right properties.
You might also want to take whatever a given locale object has to offer in the way of a moneypunct facet. After all, a principal design goal of locales, in both C and C++, is to help a program adapt to the needs of a particular culture, whose properties may not be known when the program is written. I suspect that adaptable code of this sort is more suitable for generating output than recognizing input, however. I have seen more than one monetary format that is hardly agreeable to type.
But then, maybe I'm spoiled by the fairly simple formats I grew up with. Who am I to judge? 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.