• Advertisement
Sign in to follow this  

C++ locales and facets

This topic is 3823 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Does anybody use these obscure things? They're part of the standard library, but are apparently so bizarre that even the Josuttis book doesn't give a single working example of any of them. There's an example in MSDN but it may as well be written in Ancient Greek. All I want is to display a time in a locale-dependent way. I have the C functions that generate a struct tm with the time in, I have the line to generate a std::time_put facet, but then I have no idea how to use it. Ideas? :)

Share this post


Link to post
Share on other sites
Advertisement
No, not really. :) I saw that on Google. It's still largely inpenetrable to me. If I just want a string back from it, what do I do? Use an ostringstream? Some sort of back_inserter trick? It amazes me that they expect people to use this stuff.

Share this post


Link to post
Share on other sites
For the record, I hate and despise the C++ standard library localization components and strongly recommend using a third-party library for localization like ICU.

That being said, in order to use time_put::put(), you need a few things:

1) an output iterator where you want to write the time to. This can be anything from a std::ostream_iterator<CharType>, std::ostreambuf_iterator<CharType> or even a CharType *.

2) a CharType used for spacing. When inserting into an ostream os, this is typically os.fill(), which in turn is typically ' '.

3) a tm object containing the time to be outputted, which sounds like you've got covered.

4) a time format string. These are like printf() format strings, except time-specific. Time format string documentation usually comes under the strftime() documentation for your compiler's standard library. For just producing the a time string, you can use "%X". For a date-time string, "%c".

At this point you've got two choices of put() members. If your format string only consists of a single format character such as the "%X" or "%c" examples above, you can use either put() version.

The first is:
put(iter_type out, ios_base & str, char_type fill, const tm * t, const CharType * begin, const CharType * end)

Here out is the output iterator in (1). str is any stream object. It should be imbued with the same locale as the one you got your time_put facet from. It's used for the implementation to get a ctype<> facet from, but in many implementations this argument is ignored. fill is the fill character described in (2), t is your time in (3) and [begin, end) is the const CharType * range for your time format string in (4).

The second, the version for the single character time format string, is:
put(iter_type out, ios_base & str, char_type fill, const tm * t, char format, char modifier = 0)
If modifier is 0, then this form is equivalent to the first form with a format string of "%[format]". i.e. if format is 'X' then it's equivalent to "%X". If modifier is non-zero, them this form is equivalent to the first form with a format string of "%[modifier][format]". i.e. if format is 'X' and modifier is '#", then it's equivalent to a format string of "%#X". The C++ standard does not actually state what the effect of the modifier characters are; any modifiers characters are vendor-specific.

To string that all together into a form to get a std::string from a const std::tm * and a std::locale:

std::string format_time(const std::tm * time, const std::locale & loc) {
std::stringstream sstr;
sstr.imbue(loc);
typedef std::time_put<char> TimeFacet;
const TimeFacet & time_facet = std::use_facet<TimeFacet>(loc);
char format[] = "%x %X";
time_facet.put(sstr,
sstr,
sstr.fill(),
time,
format,
format + sizeof(format));
return sstr.str();
}

The one thing to note here, is that the type of output iterator is embedded in the type of the time_put<> facet, which defaults to std::ostreambuf_iterator<CharType>. If you wanted to use a different output iterator type, you'll need a different time_put facet.

Share this post


Link to post
Share on other sites
Ok, just one question - how does the stringstream get converted to an ostream_iterator for that first parameter? I thought you'd have to explicitly construct one yourself, but it builds fine without doing so, I find.

Share this post


Link to post
Share on other sites
As mentioned, the type of the output iterator is embedded in the time_put facet, so the type of the first argument to the put() function is set by the type of the time_put facet. For time_put<char> this is a std::ostreambuf_iterator<char>, which has a non-explicit single argument constructor of type std::ostream &. If time_put didn't embed the output iterator as part of the type, and had a templated output iterator argument like the vast majority of functions that take output iterators in the standard library, you would need to explicitly construct the ostreambuf_iterator (or ostream_iterator).

Share this post


Link to post
Share on other sites
Yeah, I was just having trouble following it because it says it takes an iterator, but every example I've seen supplies a stream, and it's been so long since I used a streambuf_iterator that it didn't occur to me that you could construct one from an ostream. Thanks.

Share this post


Link to post
Share on other sites
If it's any consolation, you aren't the only one who's gotten tripped up over this. As far as I can tell, the fact that the iterator type is embedded in the time_put type (and other facet types) is for the specific reason to enable this facility of just passing the stream as an argument to the put() and get() functions. However, the locale facets are the only place where this kind of shortcut is built into the standard library, so it represents a relatively frequent point of failure for people trying to understand locale code.

Unfortunately, the internationalization facilities of the C++ standard library seems to be an example of the worst aspects of design by committee: it's a result of what the members hated the least rather than liked the most. See previous comment about despising the C++ localization facilities.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement