Actually std::put_money
can and will do exactly what you're asking if you use it correctly.
There are two parts to that. First of all, you have to select a locale. The default "C" locale simply doesn't define a currency symbol to use. Typically you'll want to use the nameless locale, something like this:
std::cout.imbue(std::locale(""));
This chooses (and uses) the locale for which the user has configured their environment, so (for example) a typical US user will get some US English locale with a dollar sign as the currency symbol, but a typical German user will get a German locale with a euro sign as the currency symbol1. This will also affect the formatting of the numbers. The example output below shows it using a comma as a thousands separator and a period (full-stop) as a decimal separator. Users in many European countries (for one example) should expect those two to be reversed.
Then you need to set the showbase
flag for the stream. For other numbers, this tells the stream to show the base of the number as converted, so (for example) if you print out something in hexadecimal, it'll be preceded by a 0x
(or 0X
). When you write money, that gets re-purposed to saying whether to show the currency symbol or not.
You normally want to combine that with the advice you've already received about creating a class (or struct) to store the data about an item. Rather than a function named display
, I'd create an overload of operator<<
to display an item though.
Note that money values are stored as long double
s. In a typical case, it will be stored as an integral number of the smallest denomination of currency in normal use (e.g., in the US, an integral number of pennies). Therefore, initialization to 100.0
would actually mean a value of $1.00
2.
With that (and leaving out the code for your header), creating and displaying the table might look something like this:
#include <iostream>
#include <iomanip>
#include <string>
#include <vector>
unsigned short int width=10;
unsigned short int prec=6;
class item {
std::string name;
long double price;
public:
item(std::string const &name, double price): name(name), price(price) {}
friend std::ostream &operator<<(std::ostream &os, item const &i) {
return os<<std::setw(width) << i.name
<<std::setw(width) << std::showbase << std::put_money(i.price);
}
};
int main(void) {
std::vector<item> items {
{"water", 10000},
{"Milk", 20000},
{"Juice", 30000},
{"Wine", 40000},
{"Bread", 60000},
{"Apple", 70000},
{"Tuna", 80000},
{"Steak", 90000},
{"Bandage", 100000},
{"Med-Kit", 110000},
{"Splint", 120000},
{"Thread", 130000}
};
std::cout.imbue(std::locale(""));
unsigned count=0;
for (auto &&item:items) {
std::cout<<item;
if (++count==3) {
std::cout<<"\n";
count=0;
}
}
}
Result:
water $100.00 Milk $200.00 Juice $300.00
Wine $400.00 Bread $600.00 Apple $700.00
Tuna $800.00 Steak $900.00 Bandage $1,000.00
Med-Kit $1,100.00 Splint $1,200.00 Thread $1,300.00
If you decide to do the formatting on your own, be aware that it won't be quite as simple as just inserting a $
before the item to be written. The problem is that you pretty much need to explicitly set a width and precision when you write out the value. When you do that, you'll quickly find that the $
gets inserted before the padding for the number, so (for example) your first entry will end up looking something like this:
water$ 100.00
...instead of putting the currency symbol next to the number where you almost certainly want it. It is possible to prevent that with enough extra work:
std::ostringstream temp;
temp << '$' << std::setprecision(your_precision) << value;
std::cout << std::setw(your_width) << temp.str();
Although it may be open to argument that usingstd::put_money
is a little more work than you'd like, by the time you get the formatting even reasonably close to correct on your own, it's quite a bit more work still (and adding even minimal support for internationalization is quite a bit more work again).
- Do note, however, that while the symbol is automatically chosen based on the user's locale, no attempt is made to convert the value, so if I enter 1 dollar, and that value is displayed in a German locale, it'll show up as 1 Euro (and vice versa).
- Technically, I don't believe the standard actually requires this, but it's how all the compilers I've seen actually do things. The standard does require that
get_money
and put_money
are symmetrical, so if a user enters $100.00
and you read it with get_money
, store whatever that produces, and then write it back out with put_money
, you should get $100.00
as the result.