16.3. Personnalisation des m�canismes de localisation

Les m�canismes de localisation ont �t� con�us de telle sorte que le programmeur peut, s'il le d�sire (et s'il en a r�ellement le besoin), personnaliser leur fonctionnement. Ainsi, il est parfaitement possible de d�finir de nouvelles facettes, par exemple pour permettre la localisation des types de donn�es compl�mentaires d�finis par le programme. De m�me, il est possible de red�finir les m�thodes virtuelles des classes de gestion des facettes standards de la biblioth�que et de remplacer les facettes originales par des facettes personnalis�es. Cependant, il faut bien reconna�tre que la mani�re de proc�der n'est pas tr�s pratique, et en fait les m�canismes internes de gestion des facettes semblent �tre r�serv�s aux classes et aux m�thodes de la biblioth�que standard elle-m�me.

16.3.1. Cr�ation et int�gration d'une nouvelle facette

Comme il l'a �t� expliqu� dans la Section 16.1, une facette n'est rien d'autre qu'une classe d�rivant de la classe locale::facet et contenant une donn�e membre statique id. Cette donn�e membre est utilis�e par les classes de locale pour identifier le type de la facette et pour l'int�grer dans le m�canisme de gestion des facettes standards.

L'exemple suivant montre comment on peut r�aliser deux facettes permettant d'encapsuler les sp�cificit�s d'un type de donn�e d�fini par le programme, le type answer_t. Ce type est suppos� permettre la cr�ation de variables contenant la r�ponse de l'utilisateur � une question. Ce n'est rien d'autre qu'une �num�ration contenant les valeurs no (pour la r�ponse n�gative), yes (pour l'affirmative), all (pour r�pondre par l'affirmative pour tout un ensemble d'�l�ments) et none (pour r�pondre par la n�gative pour tout un ensemble d'�l�ments).

Dans cet exemple, deux facettes sont d�finies : la facette answerpunct, qui prend en charge la localisation des noms des diff�rentes valeurs de l'�num�ration answer_t, et la facette answer_put, qui prend en charge le formatage des valeurs de cette �num�ration dans un flux standard. L'op�rateur operator<< est �galement d�fini, afin de pr�senter la mani�re dont ces facettes peuvent �tre utilis�es. La facette answer_get et l'op�rateur correspondant operator>> n'ont pas �t� d�finis et sont laiss�s en exercice pour le lecteur int�ress�.

Exemple 16-6. D�finition de nouvelles facettes

#include <iostream>
#include <locale>

using namespace std;

// Nouveau type de donn�e permettant de g�rer les r�ponses
// aux questions (yes / no / all / none) :
enum answer_t
{
    no, yes, all, none
};

// Facette prenant d�finissant les noms des r�ponses :
template <class charT>
class answerpunct : public locale::facet
{
public:
    // Les types de donn�es :
    typedef charT char_type;
    typedef basic_string<charT> string_type;

    // L'identifiant de la facette :
    static locale::id id;

    // Le constructeur :
    answerpunct(size_t refs = 0) : locale::facet(refs)
    {
    }

    // Les m�thodes permettant d'obtenir les noms des valeurs :
    string_type yesname() const
    {
        return do_yesname();
    }

    string_type noname() const
    {
        return do_noname();
    }

    string_type allname() const
    {
        return do_allname();
    }

    string_type nonename() const
    {
        return do_nonename();
    }

protected:
    // Le destructeur :
    virtual ~answerpunct()
    {
    }

    // Les m�thodes virtuelles :
    virtual string_type do_yesname() const
    {
        return "yes";
    }

    virtual string_type do_noname() const
    {
        return "no";
    }

    virtual string_type do_allname() const
    {
        return "all";
    }

    virtual string_type do_nonename() const
    {
        return "none";
    }
};

// Instanciation de l'identifiant de la facette answerpunct :
template <class charT>
locale::id answerpunct<charT>::id;

// Facette prenant en charge le formatage des r�ponses :
template <class charT,
    class OutputIterator = ostreambuf_iterator<charT> >
class answer_put : public locale::facet
public:
    // Les types de donn�es :
    typedef charT char_type;
    typedef OutputIterator      iter_type;
    typedef basic_string<charT> string_type;

    // L'identifiant de la facette :
    static locale::id id;

    // Le constructeur :
    answer_put(size_t refs = 0) : locale::facet(refs)
    {
    }

    // La m�thode de formatage publique :
    iter_type put(iter_type i, ios_base &flux,
        char_type remplissage, answer_t valeur) const
    {
        return do_put(i, flux, remplissage, valeur);
    }

protected:
    // Le destructeur :
    virtual ~answer_put()
    {
    }

    // L'impl�mentation de la m�thode de formatage :
    virtual iter_type do_put(iter_type i, ios_base &flux,
        char_type remplissage, answer_t valeur) const
    {
        // R�cup�re la facette d�crivant les noms de types :
        const answerpunct<charT> &facet =
            use_facet<answerpunct<charT> >(flux.getloc());
        // R�cup�ration du nom qui sera �crit :
        string_type result;
        switch (valeur)
        {
        case yes:
            result = facet.yesname();
            break;
        case no:
            result = facet.noname();
            break;
        case all:
            result = facet.allname();
            break;
        case none:
            result = facet.nonename();
            break;
        }
        // �criture de la valeur :
        const char *p = result.c_str();
        while (*p != 0)
        {
            *i = *p;
            ++i; ++p;
        }
        return i;
    }
};

// Instanciation de l'identifiant de la facette answer_put :
template <class charT,
    class OutputIterator = ostreambuf_iterator<charT> >
locale::id answer_put<charT, OutputIterator>::id;

// Op�rateur permettant de formater une valeur
// de type answer_t dans un flux de sortie :
template <class charT, class Traits>
basic_ostream<charT, Traits> &operator<<(
    basic_ostream<charT, Traits> &flux,
    answer_t valeur)
{
    // Initialisation du flux de sortie :
    typename basic_ostream<charT, Traits>::sentry init(flux);
    if (init)
    {
        // R�cup�ration de la facette de gestion de ce type :
        const answer_put<charT> &facet =
            use_facet<answer_put<charT> >(flux.getloc());
        // �criture des donn�es :
        facet.put(flux, flux, ' ', valeur);
    }
    return flux;
}

int main(void)
{
    // Cr�e une nouvelle locale utilisant nos deux facettes :
    locale temp(locale(""), new answerpunct<char>);
    locale loc(temp, new answer_put<char>);
    // Installe cette locale dans le flux de sortie :
    cout.imbue(loc);
    // Affiche quelques valeurs de type answer_t :
    cout << yes << endl;
    cout << no << endl;
    cout << all << endl;
    cout << none << endl;
    return 0;
}

Note�: Cet exemple, bien que d�j� compliqu�, passe sous silence un certain nombre de points qu'il faudrait th�oriquement prendre en compte pour r�aliser une impl�mentation correcte des facettes et des op�rateurs d'insertion et d'extraction des donn�es de type answer_t dans les flux standards. Il faudrait en effet traiter les cas d'erreurs lors des �critures sur le flux de sortie dans la m�thode do_put de la facette answer_put, capter les exceptions qui peuvent se produire, corriger l'�tat du flux d'entr�e / sortie au sein de l'op�rateur operator<< et relancer ces exceptions.

De m�me, les param�tres de la locale ne sont absolument pas pris en compte dans la facette answerpunct, alors qu'une impl�mentation compl�te devrait s'en soucier. Pour cela, il faudrait r�cup�rer le nom de la locale incluse dans les flux d'entr�e / sortie d'une part, et d�finir une facette sp�cialis�e answerpunct_byname, en fonction du nom de laquelle les m�thodes do_yesname, do_noname, do_allname et do_nonename devraient s'adapter. La section suivante donne un exemple de red�finition d'une facette existante.

16.3.2. Remplacement d'une facette existante

La red�finition des m�thodes de facettes d�j� existantes est l�g�rement plus simple que l'�criture d'une nouvelle facette. En effet, il n'est plus n�cessaire de d�finir la donn�e membre statique id. De plus, seules les m�thodes qui doivent r�ellement �tre red�finies doivent �tre r�crites.

L'exemple suivant pr�sente comment un programme peut red�finir les m�thodes do_truename et do_falsename de la facette standard numpunct_byname afin d'en fournir une version localis�e en fran�ais. Cela permet d'utiliser ces noms fran�ais dans les op�rations de formatage des flux d'entr�e / sortie standards, lorsque le manipulateur boolalpha a �t� utilis�.

Exemple 16-7. Sp�cialisation d'une facette existante

#include <iostream>
#include <locale>
#include <clocale>
#include <cstring>

using namespace std;

// Facette destin�e � remplacer numpunct_byname :
class MyNumpunct_byname :
    public numpunct_byname<char>
{
    // Les noms des valeurs true et false :
    const char *m_truename;
    const char *m_falsename;

public:
    MyNumpunct_byname(const char* nom) :
        numpunct_byname<char>(nom)
    {
        // D�termine le nom de la locale active :
        const char *loc = nom;
        if (strcmp(nom, "") == 0)
         {
            // R�cup�re le nom de la locale globale active :
            loc = setlocale(0, NULL);
        }
        // Prend en charge les noms fran�ais :
        if (strcmp(loc, "fr_FR") == 0)
        {
            m_truename = "vrai";
            m_falsename = "faux";
        }
        else
        {
            // Pour les autres locales, utilise les noms anglais :
            m_truename = "true";
            m_falsename = "false";
        }
    }

protected:
    ~MyNumpunct_byname()
    {
    }

    string do_truename() const
    {
        return m_truename;
    }

    string do_falsename() const
    {
        return m_falsename;
    }
};

int main(void)
{
    // Fixe la locale globale du programme :
    locale::global(locale(""));
    // Cr�e une nouvelle locale utilisant notre facette :
       locale l(locale(""), new MyNumpunct_byname(""));
    // Installe cette locale dans le flux de sortie :
    cout.imbue(l);
    // Affiche deux bool�ens :
    cout << boolalpha << true << endl;
    cout << false << endl;
    return 0;
}

Note�: La classe de base de la facette MyNumpunct_byname est la classe numpunct_byname parce que la facette a besoin de conna�tre le nom de la locale pour laquelle elle est construite. En effet, aucun autre m�canisme standard ne permet � une facette de r�cup�rer ce nom et donc de s'adapter aux diff�rentes locales existantes. Vous remarquerez que les facettes de formatage n'ont pas besoin de conna�tre ce nom puisqu'elles peuvent le r�cup�rer gr�ce � la m�thode name de la locale du flux sur lequel elles travaillent.

La facette MyNumpunct_byname utilise la fonction setlocale de la biblioth�que C pour r�cup�rer le nom de la locale courante si elle est initialis�e avec un nom vide. En r�alit�, elle devrait r�cup�rer ce nom par ses propres moyens et effectuer les traductions des noms des valeurs true et false par elle-m�me, car cela suppose que la locale globale du programme est initialis�e avec le m�me nom. C'est pour cela que le programme principal commence par appeler la m�thode global de la classe local avec comme param�tre une locale anonmyme. Cela dit, les m�canismes permettant � un programme de r�cup�rer les param�tres de la locale d�finie dans l'environnement d'ex�cution du programme sont sp�cifiques � chaque syst�me et ne peuvent donc pas �tre d�crits ici.

Bien entendu, si d'autres langues que le fran�ais devaient �tre prises en compte, d'autre m�canismes plus g�n�riques devraient �galement �tre mis en place pour d�finir les noms des valeurs true et false afin d'�viter de compliquer exag�r�ment le code de la facette.