A facet is an object which can be stored in a locale and retrieved from the locale based on its type. Facets encapsulate data about cultural and language dependencies. They also contain services (functions) that use the encapsulated data to help you internationalize your programs.
The C++ Standard Library defines a number of facet classes, which we call standard facets. These facets were reviewed in the previous chapter; they are present in every locale. You can derive your own facet classes from the standard facets, to modify their behavior. You can also create entirely new facet classes, to handle aspects of internationalization that the standard facets don't cover. Both of these processes are described later in this chapter.
In talking about facet classes, we need to distinguish between base facets and derived facets. A base facet is a class with the following properties:
It is derived from class std::locale::facet.
It contains a static data member named id, of type std::locale::id.
These properties make it possible to store the facet in a locale, and to retrieve it from the locale based on its type. A locale contains at most one facet of each base facet type.
A derived facet is a class that is derived from a base facet, but does not contain its own static std::locale::id member. Instead, it inherits this member from the base facet it derives from. Like any facet, a derived facet can be stored in a locale, but it occupies the same slot in the locale as its base facet, displacing any other facet that was in that slot. For example, the following skeletal code defines a mythical base facet and a facet derived from it, and shows some of the ways these mythical facets can be stored in and retrieved from locales:
class mythical: public std::locale::facet { //1 public: static std::locale::id id; ... // etc }; std::locale::id mythical::id; class mythical_byname: public mythical { //2 public: mythical_byname (char *name,/*etc*/); // Constructor ... // etc }; int main (void) { std::locale loc0(std::locale::classic()); //3 std::locale loc1(loc0,new mythical); //4 std::locale loc2(loc1,new mythical_byname("he_DO")); //5 const mythical& m2=std::use_facet<mythical>(loc2); //6 const mythical& m1=std::use_facet<mythical>(loc1); //7 const mythical& m0=std::use_facet<mythical>(loc0); //8 }
//1 | An example of a base facet class, derived from std::locale::facet and containing a static std::locale::id member name id. |
//2 | A derived facet class. |
//3 | A copy of the classic locale. Like all locales, it contains many facets. The facets are kept in slots indexed by the set of possible base facet types. Thus there is one slot for the base facet type mythical. In the classic locale, this mythical slot is empty. |
//4 | A copy of loc0, the classic locale, with its mythical slot now occupied by a newly-created mythical facet. |
//5 | A copy of loc1, but with the mythical slot now occupied by a newly-created mythical_byname facet. |
//6 | This returns a reference to the mythical_byname facet constructed in //5. However, note that you no longer know that it is a mythical_byname facet (short of using RTTI, which is cheating in this context). All you know is that it is a mythical facet, because it came from the mythical slot in the locale. (The std::use_facet function template is described in more detail later in this chapter.) |
//7 | This returns a reference to the mythical facet constructed in step //4. |
//8 | This causes a std::bad_cast exception to be thrown, because the mythical slot in loc0 is empty. |
The standard facets that come with the library are all defined as class templates. The first template parameter is always the character type the facet will operate on, which is usually char or wchar_t. Some of the facets have additional template parameters. As another example of the distinction between base facets and derived facets, here is a stripped-down version of the Standard facet template std::numpunct, which takes a single template parameter.
template <class charT> class numpunct: public std::locale::facet { public: static std::locale::id id; ... // etc }; template <class charT> class numpunct_byname: public numpunct<charT> { ... // etc };
Typically, these templates are instantiated on char and wchar_t, so the following are base facet types:
std::numpunct<char> std::numpunct<wchar_t>
and the following are derived facet types:
std::numpunct_byname<char> std::numpunct_byname<wchar_t>
If your application created its own character type my_char_t, then std::numpunct<my_char_t> would be a base facet type and std::numpunct_byname<my_char_t> would be a derived facet type.