Skip to content. Skip to navigation

ICTP Portal

Sections
You are here: Home Manuals on-line PGI Compiler pgC_lib stdlibug 1.5 The Facets
Personal tools
Document Actions

1.5 The Facets



Click on the banner to return to the user guide home page.

1.5 The Facets

A facet is a nested class inside class locale; it is called locale::facet. Facet objects encapsulate internationalization services, and represent culture and language dependencies.

1.5.1 Creating a Facet Object

There are several ways to create facet objects:

  • Buy a facet library, which provides you with facet classes and objects.

  • Build your own facet classes and construct facet objects.

  • Build facet objects from the external representation of a C locale. This is done via the constructor of one of the byname[9] facet classes from the Standard C++ Library, as shown in Figure 11:

Figure 11. Creating facet objects


Facets are interdependent. For example, the num_get and num_put facet objects rely on a numpunct facet object. In most cases, facet objects are not used independently of each other, but grouped together in a locale object. For this reason, facet objects are usually constructed along with the locale object that maintains them.

On rare occasions, however, you may need to construct a single facet for stand-alone use. You will then find that you cannot construct a facet directly, because the facet class has a protected destructor. [10] The example below demonstrates how to write the code to construct and use a single facet object. This code demonstrates a locale-sensitive string comparison, which you would perform in C using the strcoll() function.

template <class charT>
class Collate : public collate_byname<charT>
{
public:
   Collate(const char* name, size_t refs=0)
   : collate_byname<charT>(name,refs) {}
   ~Collate() {}
};

string name1("Peter Gartner");
string name2 ("Peter G_rtner");
Collate<char> collFacet("De_DE");                             \\1
if ( collFacet.compare                                        \\2
   (name1.begin(), name1.end(), name2.begin(), name2.end()) 
   == -1)
{ _ }
//1A collation facet object is constructed from a German C locale's external representation.
//2The member function compare() of this facet object is used for string comparison.

The string class in the Standard C++ Library does not provide any service for locale-sensitive string comparisons. Hence, you will generally use a collate facet's compare service, as demonstrated above, or the locale's function call operator instead:

string name1("Peter Gartner");
string name2 ("Peter G_rtner");
locale loc("De_DE");
if ( loc(name1, name2) )
{ _ }

1.5.2 Accessing a Locale's Facets

A locale object is like a container--or a map, to be more precise--but it is indexed by type at compile time. The indexing operator, therefore, is not operator[], but rather the template operator <>. Access to the facet objects of a locale object is via two member function templates, use_facet and has_facet:

template <class Facet> const Facet&     use_facet(const locale&);
template <class Facet> bool             has_facet(const locale&);

The code below demonstrates how they are used. It is an example of the ctype facet's usage; all upper case letters of a string read from the standard input stream are converted to lower case letters and written to the standard output stream.

string in;
cin >> in;
if (has_facet< ctype<char> >(locale::locale()))               \\1
{  cout << use_facet< ctype<char> >(locale::locale())         \\2
               .tolower(in.begin(),in.end());                 \\3
}
//1In the call to has_facet<...>(), the template argument chooses a facet class. If no object of the named facet class is present in a locale object, has_facet returns false.
//2The function template use_facet<_>() returns a reference to a locale's facet object. As locale objects are immutable, the reference to a facet object obtained via use_facet() stays valid throughout the lifetime of the locale object.
//3The facet object's member function tolower() is called. It has the functionality of the C function tolower(); it converts all upper case letters into lower case letters.

In most situations, you do not have to check whether a locale has a standard facet object like ctype. Most locale objects are created by composition, starting with a locale object constructed from a C locale's external representation. Locale objects created this way, that is, via a byname constructor, always have all of the standard facet objects. Because you can only add or replace facet objects in a locale object, you cannot compose a locale that misses one of the standard facets.

A call to has_facet() is useful, however, when you expect that a certain non-standard facet object should be present in a locale object.

1.5.3 Using a Stream's Facet

Here is a more advanced example that uses a time facet for printing a date. Let us assume we have a date and want to print it this way:

struct tm aDate;                                              //1
aDate.tm_year = 1989;
aDate.tm_mon = 9;
aDate.tm_mday = 1;                                            //2

cout.imbue(locale::locale("De_CH"));                          //3
cout << aDate;                                                //4
//1A date object is created. It is of type tm, which is the time structure defined in the standard C library.
//2The date object is initialized with a particular date, September 1, 1989.
//3Let's assume our program is supposed to run in a German-speaking canton of Switzerland. Hence, a Swiss locale is attached to the standard output stream.
//4The date is printed in German to the standard output stream.

The output will be: 1. September 1989

As there is no operator<<() defined in the Standard C++ Library for the time structure tm from the C library, we have to provide this inserter ourselves. The following code suggests a way this can be done. If you are not familiar with iostreams, you will want to refer to the iostreams section of this User's Guide for more information.

To keep it simple, the handling of exceptions thrown during the formatting is omitted.

template<class charT, class traits>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT,traits>& os, const tm& date)   \\1
{
 locale loc = os.getloc();                                    \\2
 typedef ostreambuf_iterator<charT,traits> outIter_t;         \\3
 const time_put<charT,outIter_t>& fac;                        \\4
 fac = use_facet < time_put<charT, bufIter_t > > (loc);       \\5
 outIter_t nxtpos;                                            \\6
 nextpos = fac.put(os,os,os.fill(),&date,'x');                \\7
 if (nxtpos.failed())                                         \\8
    os.setstate(ios_base::badbit);                            \\9
 return os;
}
//1This is a typical signature of a stream inserter; it takes a reference to an output stream and a constant reference to the object to be printed, and returns a reference to the same stream.
//2The stream's locale object is obtained via the stream's member function getloc(). This is the locale object where we expect to find a time-formatting facet object.
//3We define a type for an output iterator to a stream buffer.

Time formatting facet objects write the formatted output via an iterator into an output container (see the sections on containers and iterators in the User's Guide). In principle, this can be an arbitrary container that has an output iterator, such as a string or a C++ array.

Here we want the time-formatting facet object to bypass the stream's formatting layer and write directly to the output stream's underlying stream buffer. Therefore, the output container shall be a stream buffer.
//4We define a variable that will hold a reference to the locale object's time_put facet object. The time formatting facet class time_put has two template parameters:

The first template parameter is the character type used for output. Here we provide the stream's character type as the template argument.

The second template parameter is the output iterator type. Here we provide the stream buffer iterator type outIter_t that we had defined as before.
//5Here we get the time-formatting facet object from the stream's locale via use_facet().
//6We define a variable to hold the output iterator returned by the facet object's formatting service.
//7The facet object's formatting service put()is called. Let us see what arguments it takes. Here is the function's interface:
     iter_type put     iter_type      (a)
                       ,ios_base&     (b)
                       ,char_type     (c)
                       ,const tm*     (d)
                       ,char)         (e)
    The types iter_type and char_type stand for the types that were provided as template arguments when the facet class was instantiated. In this case, they are ostreambuf_iterator<charT,traits> and charT, where charT and traits are the respective streams template arguments.

    Here is the actual call:

    nextpos = fac.put(os,os,os.fill(),&date,'x');

    Now let's see what the arguments mean:

    1. The first parameter is supposed to be an output iterator. We provide an iterator to the stream's underlying stream buffer. The reference os to the output stream is converted to an output iterator, because output stream buffer iterators have a constructor taking an output stream, that is, basic_ostream<charT,traits>&.

    2. The second parameter is of type ios_base&, which is one of the stream base classes. The class ios_base contains data for format control (see the section on iostreams for details). The facet object uses this formatting information. We provide the output stream's ios_base part here, using the automatic cast from a reference to an output stream, to a reference to its base class.

    3. The third parameter is the fill character. It is used when the output has to be adjusted and blank characters have to be filled in. We provide the stream's fill character, which one can get by calling the stream's fill() function.

    4. The fourth parameter is a pointer to a time structure tm from the C library.

    5. The fifth parameter is a format character as in the C function strftime(); the x stands for the locale's appropriate date representation.

    6. The value returned is an output iterator that points to the position immediately after the last inserted character.

//8As we work with output stream buffer iterators, we can even check for errors happening during the time formatting. Output stream buffer iterators are the only iterators that have a member function failed() for error indication. [11]
//9If there was an error, we set the stream's state accordingly. See the section on iostreams for details on the setstate() function and the state bits.

1.5.4 Creating a Facet Class for Replacement in a Locale

At times you may need to replace a facet object in a locale by another kind of facet object. In the following example, let us derive from one of the standard facet classes, numpunct, and create a locale object in which the standard numpunct facet object is replaced by an instance of our new, derived facet class.

Here is the problem we want to solve. When you print boolean values, you can choose between the numeric representation of the values "true" and "false", or their alphanumeric representation.

int main(int argc, char** argv)
{
   bool any_arguments = (argc > 1);                           \\1
   cout.setf(ios_base::boolalpha);                            \\2
   cout << any_arguments << '\n';                             \\3
   // _
}
//1A variable of type bool is defined. Its initial value is the boolean value of the logical expression (argc > 1), so the variable any_arguments contains the information, whether the program was called with or without arguments.
//2The format flag ios_base:: boolalpha is set in the predefined output stream cout. The effect is that the string representation of boolean values is printed, instead of their numerical representation 0 or 1, which is the default representation.
//3Here either the string "true" or the string "false" will be printed.

Of course, the string representation depends on the language. Hence, the alphanumeric representation of boolean values is provided by a locale. It is the numpunct facet of a locale that describes the cultural conventions for numerical formatting. It has a service that provides the string representation of the boolean values true and false.[12]

This is the interface of facet numpunct:

template <class charT>
    class numpunct : public locale::facet {
    public:
      typedef charT               char_type;
      typedef basic_string<charT> string_type;
      explicit numpunct(size_t refs = 0);
      string_type  decimal_point()   const;
      string_type  thousands_sep()   const;
      vector<char> grouping()        const;
      string_type  truename()        const;
      string_type  falsename()       const;
      static locale::id id;
};

Now let us replace this facet. To make it more exciting, let's use not only a different language, but also different words for true and false, such as Yes! and No!. For just using another language, we would not need a new facet; we would simply use the right native locale, and it would contain the right facet.

template <class charT, charT* True, charT* False>             //1
class CustomizedBooleanNames 
: public numpunct_byname<charT> {                             //2
    typedef basic_string<charT> string;
  protected:
    string do_truename()  {return True;}                      //3
    string do_falsename() {return False;}
    ~CustomizedBooleanNames() {}
  public:
    explicit CustomizedBooleanNames(const char* LocName)      //4
         : numpunct_byname<charT>(LocName) {}
};
//1The new facet is a class template that takes the character type as a template parameter, and the string representation for true and false as non-type template parameters.
//2The new facet is derived from the numpunct_byname<charT> facet.

The byname facets read the respective locale information from the external representation of a C locale. The name provided to construct a byname facet is the name of a locale, as you would use it in a call to setlocale().
//3The virtual member functions do_truename() and do_falsename() are reimplemented. They are called by the public member functions truename() and falsename(). See the Class Reference for further details.
//4A constructor is provided that takes a locale name. This locale's numpunct facet will be the basis for our new facet.

Now let's replace the numpunct facet object in a given locale object, as shown in Figure 12:

Figure 12. Replacing the numpunct facet object


The code looks like this:

char Yes[] = "Ja.";
char No[] = "Nein.";

void main(int argc, char** argv)
{
  locale loc(locale("de_DE"),                                 \\1
       new CustomizedBooleanNames<char,Yes,No>("de_DE"));     \\2
  cout.imbue(loc);                                            \\3
  cout << "Argumente vorhanden? "  //Any arguments?
       << boolalpha << (argc > 1) << endl;                    \\4
}
//1A locale object is constructed with an instance of the new facet class. The locale object will have all facet objects from a German locale object, except that the new facet object CustomizedBooleanNames will substitute for the numpunct facet object.
//2The new facet object takes all information from a German numpunct facet object, and replaces the default native names true and false with the provided strings "Ja."("Yes.") and "Nein."("No.").

Note that the facet object is created on the heap. That's because the locale class by default manages installation, reference-counting, and destruction of all its facet objects.
//3The standard output stream cout is imbued with the newly created locale object.
//4The expression (argc > 1) yields a boolean value, which indicates whether the program was called with arguments. This boolean value's alphanumeric representation is printed to the standard output stream. The output might be:

Argument vorhanden? Ja.

1.5.5 The Facet Id

In the example discussed above, we derived a new facet class from one of the standard facet classes, then replaced an object of base class type by one of derived class type. The inheritance relationship of the facet classes is essential if you plan on replacing facet objects. Let us see why this is true.

A locale object maintains a set of facet objects. Each facet object has an identification that serves as an index to the set of facet objects. This identification, called id, is a static data member of the respective facet class. Whether or not a facet object will replace another facet, or be an actual addition to the locale object's set of facet objects, solely depends on the facet's identification.

The base class of all facets, class locale::facet, does not have a facet identification. The class locale::facet performs the function of an abstract base class; there will never be any facet object of the base class type. However, all concrete facet classes have to define a facet identification. In the example above, we inherited the facet identification from the base class we derived from, that is, the standard facet class numpunct. Every object of our facet class CustomizedBooleanNames has a facet identification that identifies it as a numpunct facet. As the facet identification serves as an index to the locale object's set of facets, our facet object replaced the current numpunct facet object in the locale object's set of facet objects.

If you do not want to replace a facet object, but want to add a new kind of facet object, we have to provide it with a facet identification different from all existing facet identifications. The following example will demonstrate how this can be achieved.

1.5.6 Creating a Facet Class for Addition to a Locale

At times you may need to add a facet object to a locale. This facet object must have a facet identification that distinguishes it from all existing kinds of facets.

Here is an example of a new facet class like that. It is a facet that checks whether a given character is a German umlaut[13], that is, one of the special characters .

class Umlaut : public locale::facet {                         \\1
  public:
    static locale::id id;                                     \\2
    bool is_umlaut(char c);                                   \\3
    Umlaut() {}
  protected:
    ~Umlaut() {}
};
//1All facet classes have to be derived from class locale::facet.
//2Here we define the static data member id. It is of type locale::id. The default constructor of the facet identification class locale::id assigns the next unused identification to each object it creates. Hence, it is not necessary, nor even possible, to explicitly assign a value to the static facet id object. In other words, this definition does the whole trick; our facet class will have a facet identification that distinguishes it from all other facet classes.
//3A member function is_umlaut() is declared that returns true if the character is a German umlaut.

Now let's add the new facet object to a given locale object, as shown in Figure 13:

Figure 13. Adding a new facet to a locale


The code for this procedure is given below:

locale loc(locale(""),  // native locale
           new Umlaut); // the new facet                      //1
char c,d;
while (cin >> c){
 d = use_facet<ctype<char> >(loc).tolower(c);                 //2
 if (has_facet<Umlaut>(loc))                                  //3
 { if (use_facet<Umlaut>(loc).is_umlaut(d))                   //4
       cout << c << "belongs to the German alphabet!" << '\n';
 }
}
//1A locale object is constructed with an instance of the new facet class. The locale object will have all facet objects from the native locale object, plus an instance of the new facet class Umlaut.
//2Let's assume our new umlaut facet class is somewhat limited; it can handle only lower case characters. Thus we have to convert each character to a lower case character before we hand it over to the umlaut facet object. This is done by using a ctype facet object's service function tolower().
//3Before we use the umlaut facet object, we check whether such an object is present in the locale. In a toy example like this it is obvious, but in a real application it is advisable to check for the existence of a facet object, especially if it is a non-standard facet object we are looking for.
//4The umlaut facet object is used, and its member function is_umlaut() is called. Note that the syntax for using this newly contrived facet object is exactly like the syntax for using the standard ctype facet.

©Copyright 1996, Rogue Wave Software, Inc.


Powered by Plone This site conforms to the following standards: