A C++ program may define facets to be added to a locale and used identically as the built-in facets. To create a new facet interface, C++ programs simply derive from locale::facet a class containing a static member: static locale::id id.
[ Note: The locale member function templates verify its type and storage class. — end note ]
[ Example: Traditional global localization is still easy:
#include <iostream> #include <locale> int main(int argc, char** argv) { using namespace std; locale::global(locale("")); // set the global locale // imbue it on all the std streams cin.imbue(locale()); cout.imbue(locale()); cerr.imbue(locale()); wcin.imbue(locale()); wcout.imbue(locale()); wcerr.imbue(locale()); return MyObject(argc, argv).doit(); }
— end example ]
[ Example: Greater flexibility is possible:
#include <iostream>
#include <locale>
int main() {
using namespace std;
cin.imbue(locale("")); // the user's preferred locale
cout.imbue(locale::classic());
double f;
while (cin >> f) cout << f << endl;
return (cin.fail() != 0);
}
In a European locale, with input 3.456,78, output is 3456.78. — end example ]
This can be important even for simple programs, which may need to write a data file in a fixed format, regardless of a user's preference.
[ Example: Here is an example of the use of locales in a library interface.
// file: Date.h
#include <iosfwd>
#include <string>
#include <locale>
class Date {
public:
Date(unsigned day, unsigned month, unsigned year);
std::string asString(const std::locale& = std::locale());
};
std::istream& operator>>(std::istream& s, Date& d);
std::ostream& operator<<(std::ostream& s, Date d);
This example illustrates two architectural uses of class locale.
The first is as a default argument in Date::asString(), where the default is the global (presumably user-preferred) locale.
The second is in the operators << and >>, where a locale “hitchhikes” on another object, in this case a stream, to the point where it is needed.
// file: Date.C #include "Date" // includes <ctime> #include <sstream> std::string Date::asString(const std::locale& l) { using namespace std; ostringstream s; s.imbue(l); s << *this; return s.str(); } std::istream& operator>>(std::istream& s, Date& d) { using namespace std; istream::sentry cerberos(s); if (cerberos) { ios_base::iostate err = goodbit; struct tm t; use_facet< time_get<char> >(s.getloc()).get_date(s, 0, s, err, &t); if (!err) d = Date(t.tm_day, t.tm_mon + 1, t.tm_year + 1900); s.setstate(err); } return s; }
— end example ]
A locale object may be extended with a new facet simply by constructing it with an instance of a class derived from locale::facet. The only member a C++ program must define is the static member id, which identifies your class interface as a new facet.
[ Example: Classifying Japanese characters:
// file: <jctype> #include <locale> namespace My { using namespace std; class JCtype : public locale::facet { public: static locale::id id; // required for use as a new locale facet bool is_kanji (wchar_t c) const; JCtype() { } protected: ~JCtype() { } }; } // file: filt.C #include <iostream> #include <locale> #include "jctype" // above std::locale::id My::JCtype::id; // the static JCtype member declared above. int main() { using namespace std; typedef ctype<wchar_t> wctype; locale loc(locale(""), // the user's preferred locale ... new My::JCtype); // and a new feature ... wchar_t c = use_facet<wctype>(loc).widen('!'); if (!use_facet<My::JCtype>(loc).is_kanji(c)) cout << "no it isn't!" << endl; return 0; }
The new facet is used exactly like the built-in facets. — end example ]
[ Example: Replacing an existing facet is even easier. The code does not define a member id because it is reusing the numpunct<charT> facet interface:
// file: my_bool.C #include <iostream> #include <locale> #include <string> namespace My { using namespace std; typedef numpunct_byname<char> cnumpunct; class BoolNames : public cnumpunct { protected: string do_truename() const { return "Oui Oui!"; } string do_falsename() const { return "Mais Non!"; } ~BoolNames() { } public: BoolNames(const char* name) : cnumpunct(name) { } }; } int main(int argc, char** argv) { using namespace std; // make the user's preferred locale, except for... locale loc(locale(""), new My::BoolNames("")); cout.imbue(loc); cout << boolalpha << "Any arguments today? " << (argc > 1) << endl; return 0; }
— end example ]