Internacionalizace a lokalizace v C++

╙dy ₧lutΘho kon∞ 4.

╙dy ₧lutΘho kon∞ poΦtvrtΘ a naposledy: Formßtovßnφ Φφsel

V p°edchozφch dφlech Φlßnku v∞novanΘho problematice lokalizace, resp. internacionalizace program∙ v C++ jsme se seznßmili s t°φdou std::locale a s fazetami, nauΦili jsme se Φφst a zapisovat Φesk² text v r∙zn²ch k≤dovßnφch, p°evßd∞t malß pφsmena na velkß a naopak a °adit znakovΘ °et∞zce podle abecedy. V poslednφm dφlu se podφvßme na formßtovßnφ Φφsel.

Nßrodnφ zvyklosti

Ka₧d² zaΦφnajφcφ programßtor se uΦφ, ₧e v reßln²ch Φφslech musφ v programech psßt desetinnou teΦku, nikoli Φßrku, a ₧e nesmφ pou₧φvat mezery pro odd∞lovßnφ tisφc∙. Jen₧e to je vlastn∞ nep°irozenΘ: celß kontinentßlnφ Evropa pou₧φvß desetinnou Φßrku a n∞jakΘ odd∞lovaΦe tisφc∙. Podφvßme-li se za hranice Evropy, zjistφme, ₧e jinΘ nßrody seskupujφ Φφslice ve vφcecifern²ch Φφslech i ji.nak ne₧ po t°ech (tak₧e vlastn∞ ani nelze hovo°it o odd∞lovaΦφch tisφc∙).

Pro nßs je p°irozenΘ napsat Φφslo mili≤n 1 000 000,00, zatφmco N∞mec napφÜe 1.000.000,00 a AngliΦan pro zm∞nu 1,000,000.00. V Nepßlu pr² totΘ₧ Φφslo zapφÜφ jako 10.00.000,00.
Navφc se tuto zvyklosti mohou v pr∙b∞hu Φasu vyvφjet, tak₧e je jasnΘ, ₧e i tyto odliÜnosti nelze dost dob°e naprogramovat jednou pro v₧dy, ale ₧e je t°eba je naΦerpat stejn∞ jako jinΘ souΦßsti nßrodnφho nastavenφ nap°. z dat ulo₧en²ch v operaΦnφho systΘmu û tak, jako to d∞lß zpravidla t°φda locale.

Fazety pro formßtovßnφ Φφsel

O formßtovßnφ Φφsel se starajφ fazety numpunct, num_get a num_put.

- Fazeta numpunct definuje znak pro desetinnou Φßrku (nebo teΦku), odd∞lovaΦe tisφc∙ a jmΘna pro logickΘ hodnoty.
- Fazeta num_get se starß o anal²zu vstupnφho °et∞zce, kter² obsahuje Φφslo.
- Fazeta num_put mß na starosti formßtovßnφ vystupujφcφho °et∞zce, kter² p°edstavuje Φφselnou hodnotu.

Fazety num_get a num_put pou₧φvajφ fazetu numpunct.

Implicitnφ nastavenφ
Podφvejme se, jak dopadne v²stup celΘho a reßlnΘho Φφsla v ΦeskΘm nßrodnφm nastavenφ tak, jak je k dispozici ve Visual C++ .NET pod Vindows 2000. Vezm∞me nßsledujφcφ jednoduch² program:

// Test ΦeskΘho nßrodnφho nastavenφ
// pro celß a reßlnß Φφsla
#include <locale>
#include <iostream>
using namespace std;
int main()
{
          locale L("Czech_Czech Republic.852");
          double d = 123456.7;
          int i = 100000;
          wcout << fixed << boolalpha;
          wcout << L"implicitni nastaveni: " << d << endl
                        << i << endl << true << endl;
          wcout.imbue(L);
          wcout << L"ΦeskΘ nastavenφ: " << d << endl << i << endl
                       << true << endl;
          return 0;
}

Zde nejprve vypφÜeme reßlnΘ a celΘ Φφslo s implicitnφm nastavenφm a pak s Φesk²m nastavenφm. V²stup tohoto programu bude

implicitni nastaveni: 123456.700000
1000000
true
ΦeskΘ nastavenφ: 123456,700000
1000000
true

Je tedy z°ejmΘ, ₧e vÜe, Φeho jsme dosßhli, je, ₧e se ve v²stupu reßlnΘho Φφsla objevila desetinnß Φßrka. (Tu nynφ musφme pou₧φt i p°i vstupu.) Implicitnφ nastavenφ neobsahuje odd∞lovaΦe tisφc∙ a Φeskß jmΘna pro logickΘ hodnoty. Pokud opravdu trvßme na odd∞lovaΦφch tisφc∙, musφme si je naprogramovat, a podobn∞ pokud chceme pou₧φvat jinß jmΘna pro hodnoty logick²ch konstant true a false.

Fazeta numpunct
Cesta ke zm∞n∞ formßtovßnφ p°i vstupnφch a v²stupnφch operacφch v datov²ch proudech vede p°es fazetu std::numpunct<>. Ve standardnφ knihovn∞ jazyka C++ je definovßna takto:

namespace std {
          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);
                    char_type decimal_point() const;
                    char_type thousands_sep() const;
                    string grouping() const;
                    string_type truename() const;
                    string_type falsename() const;
                    static locale::id id;
          protected:
                    ~numpunct(); //virtußlnφ
                    virtual char_type do_decimal_point() const;
                    virtual char_type do_thousands_sep() const;
                    virtual string do_grouping() const;
                    virtual string_type do_truename() const; // pro typ bool
                    virtual string_type do_falsename() const; // pro typ bool
          };
}

èablonov² parametr charT znamenß jako obvykle znakov² typ, tedy char nebo wchar_t. Metody decimal_point(), thousands_sep(), truename() a falsename() vracejφ po °ad∞ znak pro desetinnou Φßrku, odd∞lovaΦ tisφc∙, znakov² °et∞zec pro hodnotu true a znakov² °et∞zec pro hodnotu false. Prvnφ dv∞ metody vracejφ hodnotu typu charT, poslednφ dv∞ vracejφ metodu typu basic_string<charT>. (To znamenß, ₧e typ vracenΘ hodnoty se tedy liÜφ podle toho, zda pracujeme s ·zk²mi nebo se Üirok²mi znaky.)

Metoda grouping() vracφ znakov² °et∞zec typu string (v₧dy ·zkΘ znaky, tedy typ basic_string<char>) kter² urΦuje rozd∞lenφ Φφslic v cel²ch Φφslech do skupin. Vrßtφme se k nφ v nßsledujφcφm odstavci.

Tyto metody ned∞lajφ nic jinΘho, ne₧ ₧e volajφ odpovφdajφcφ virtußlnφ metody, pojmenovanΘ do_decimal_point(), do_thousands_sep(), do_grouping(), do_truename() a do_falsename(). To umo₧≥uje pou₧φt na mφst∞ fazety numpunct û tedy na mφst∞ instance t°φdy numpunct û instanci jakΘkoli odvozenΘ t°φdy.

Statickß slo₧ka id typu std::locale::id slou₧φ k identifikaci fazety; pou₧φvß se intern∞ jako klφΦ p°i vyhledßvßnφ fazety v instanci t°φdy locale. V potomkovi t°φdy std::numpunct ji nenφ t°eba definovat. (P°esn∞ji, nesmφme ji definovat znovu, jinak by ji t°φda locale chßpala jako jin² typ fazety.)

Skupiny Φφslic
U₧ vφme, ₧e °et∞zec vrßcen² metodou grouping() urΦuje zp∙sob seskupovßnφ Φφslic. Jestli₧e nechceme Φφslice nijak seskupovat, zadßme prßzdn² °et∞zec "" nebo "\0". Pro nejb∞₧n∞jÜφ zp∙sob, seskupovßnφ po t°ech, zadßme "\3". Pokud bychom si p°ßli nejprve skupinu 4 Φφslic, pak skupinu dvou a nakonec skupinu 3 Φφslic, zadali bychom °et∞zec "\4\2\3".

P°esnΘ pravidlo je jednoduchΘ: zadan² znakov² °et∞zec se bere jako pole Φφsel. ╚φselnß hodnota prvnφho prvku udßvß velikost prvnφ skupiny (skupiny nejni₧Üφch Φφslic), Φφselnß hodnota druhΘho prvku udßvß velikost druhΘ skupiny atd. Je-li Φφslo p°φliÜ velkΘ a odpovφdajφcφ skupina nenφ °et∞zcem vrßcen²m funkcφ grouping() popsßna, pou₧ije se pro ni velikost poslednφ skupiny. To znamenß, ₧e poslednφ velikost skupiny se stßle opakuje.

P°φklad
NapφÜeme si vlastnφ fazetu pro formßtovßnφ Φφsel. Nßsledujφcφ program ukazuje, ₧e formßtovßnφ se uplatnφ nejen p°i v²stupu, ale i p°i Φtenφ:

// Definice a pou₧itφ vlastnφ fazety
// pro formßtovßnφ Φφsel a logick²ch hodnot
#include <iostream>
#include <stdexcept>
#include <fstream>
#include <locale>
using namespace std;
class punkt_w: public std::numpunct<wchar_t> {
public:
          typedef wchar_t char_type;
          typedef wstring string_type;
          explicit punkt_w(size_t r = 0): std::numpunct<wchar_t>(r){}
protected:
          char_type do_decimal_point() const {return ',';}
          char_type do_thousands_sep() const {return ' ';}
          string do_grouping() const {return "\003";}
          string_type do_truename() const {return L"pravda";};
          string_type do_falsename() const {return L"nepravda";};
};
wchar_t cw[] = L"Älu¥ouΦk² k∙≥ p°φÜern∞ ·p∞l ∩ßbelskΘ ≤dy.";
int main()
{
          try{
                    locale Cr("Czech_Czech Republic.852");
                    locale Kon(Cr, new punkt_w(0));
                    wcout.imbue(Kon);
                    wcin.imbue(Kon);
                    wcout << cw << endl;
                    bool b = true;
                    int n = 12345;
                    double d = 12.345678;
                    wcin >> boolalpha;
                    wcout << boolalpha << b << endl;
                    wcout << n << endl;
                    wcout << d << endl;
                    wcout << L"zadej reßlnΘ Φφslo: ";
                    wcin >> d;
                    wcout << L"dvojnßsobek je "<< d*2 << endl;
                    wcout << L"zadej celΘ Φφslo: ";
                    wcin >> n;
                    wcout << L"dvojnßsobek je "<< 2*n << endl;
                    wcout << L"zadej logickou hodnotu: ";
                    wcin >> b;
                    wcout << L"Opak je " << !b << endl;
          }
          catch(runtime_error &e)
          {
                    cerr << e.what() << endl;
          }
          return 0;
}

Fazetu punct jsme odvodili jako potomka fazety std::numpunct<wchar_t> (pouze pro ÜirokΘ znaky). V nφ jsme definovali mezeru jako odd∞lovaΦ tisφc∙, Φßrku jako desetinnou teΦku a °et∞zce pravda, resp. nepravda jako slovnφ reprezentaci hodnot true, resp. false.

Na poΦßtku funkce main() vytvo°φme instanci Cr t°φdy locale reprezentujφcφ ΦeskΘ nßrodnφ nastavenφ; pak vytvo°φme instanci Kon, kterß p°evezme z instance Cr vÜechny fazety krom∞ numpunct; tu nahradφ instancφ fazety punct.

Abychom se p°esv∞dΦili, ₧e instance Kon opravdu p°evzala ostatnφ fazety z instance Cr, vypφÜeme nejprve oblφbenou v∞tu o ₧lu¥ouΦkΘm koni.

Pak pomocφ manipulßtoru boolalpha p°edepφÜeme, ₧e po₧adujeme vstup a v²stup logick²ch hodnot ve form∞ znakovΘho °et∞zce, nikoli v podob∞ Φφsel 0 nebo 1.

Konverzace s tφmto programem m∙₧e vypadat takto:

Älu¥ouΦk² k∙≥ p°φÜern∞ ·p∞l ∩ßbelskΘ ≤dy.
pravda
12 345
12,3457
zadej reßlnΘ Φφslo: 3,1415926
dvojnßsobek je 6,28319
zadej celΘ Φφslo: 1 000 000 000
dvojnßsobek je 2 000 000 000
zadej logickou hodnotu: nepravda
Opak je pravda

Dopl≥me, ₧e p°i zadßvßnφ cel²ch Φφsel m∙₧eme mezery, p°edstavujφcφ odd∞lovaΦe tisφc∙, vynechat. (Musφme ale bu∩ vÜechny vynechat, nebo vÜechny uvΘst, jinak se Φφslo sprßvn∞ nep°eΦte. Nesmφme takΘ nikde pou₧φt vφce ne₧ jednu mezeru.)

Zm∞nφme-li °et∞zec, vracen² metodou do_grouping(), na "\4\2\3", vypφÜe se Φφslo 2000000000 ve tvaru

2 000 00 0000

VÜimn∞te si, ₧e seskupovßnφ a odd∞lovaΦe tisφc∙ se nepou₧ily pro reßlnß Φφsla. To je obecnΘ pravidlo: fazeta numpunct urΦuje pouze desetinnou Φßrku (teΦku), nikoli seskupovßnφ a odd∞lovaΦe Φφslic. Pokud bychom n∞co takovΘho cht∞li, musφme si to naprogramovat sami.

Poznamenejme, ₧e kdybychom jako °et∞zec, vracen² funkcφ do_grouping(), mφsto "\3"pou₧ili "3", seskupovßnφ Φφslic v cel²ch Φφslech by se neuplatnilo. Znak '3' toti₧ p°edstavuje Φφselnou hodnotu 51, tak₧e by se program pokusil vytvo°it skupiny po 51 Φφslicφch.

Poznßmka
Formßtovßnφ cel²ch Φφsel, kterΘ jsme zde definovali, se uplatnφ pouze pro proudy pou₧φvajφcφ ÜirokΘ znaky. Budeme-li chtφt podobn²m zp∙sobem formßtovat vstup a v²stup Φφsel a logick²ch hodnot pro ·zkΘ znaky, budeme si muset napsat podobnou fazetu i pro typ char.

Fazetu punct se tedy vyplatφ deklarovat jako Üablonu s parametrem, kter² bude urΦovat pou₧it² znakov² typ. P°itom ovÜem narazφme na problΘm se znakov²mi literßly; souΦasnΘ C++ toti₧ nenabφzφ elegantnφ zp∙sob, jak definovat °et∞zcov² literßl, kter² by byl v zßvislosti na parametru Üablony typu char nebo wchar_t. Jedno z mo₧n²ch °eÜenφ je pou₧φt statickß znakovß pole a inicializovat je prvek po prvku, nebo¥ pro typ char je definovßna implicitnφ konverze na wchar_t:

template<typename charT>
class punkt: public std::numpunct<charT> {
public:
          typedef charT char_type;
          typedef basic_string<charT> string_type;
          explicit punkt(size_t r = 0): std::numpunct<charT>(r){}
protected:
          char_type do_decimal_point() const {return ',';}
          char_type do_thousands_sep() const {return ' ';}
          string do_grouping() const {return "\003";}
          string_type do_truename() const {return string_type(true_name);};
          string_type do_falsename() const {return string_type(false_name);};
private:
          static char_type true_name[];
          static char_type false_name[];
};
template<class charT>
punkt<charT>::char_type punkt<charT>::true_name[] =
                              {'p','r','a','v','d','a','\0'};
template<class charT>
punkt<charT>::char_type punkt<charT>::false_name[] =
                              {'n','e','p','r','a','v','d','a','\0'};

ZaΦßtek funkce main() pak upravφme nßsledujφcφm zp∙sobem:

int main()
{
          try{
                    locale Cr("Czech_Czech Republic.852");
                    locale Kon0(Cr, new punkt<wchar_t>(0));
                    locale Kon(Kon0, new punkt<char>(0));
                    wcout.imbue(Kon);
                    wcin.imbue(Kon);
                    cout.imbue(Kon);
                    // ... a dßle jako p°edtφm
}

Fazety p°idßvßme do instance t°φdy locale jednu po druhΘ.

Vlastnφ fazeta

Nynφ si ukß₧eme, jak lze definovat a pou₧φvat vlastnφ fazetu. Jde o upraven² p°φklad z dodatku D knihy [1].

Definujeme fazetu, kterß bude slou₧it ke vstupu a v²stupu nßzv∙ roΦnφch obdobφ a kterou p∙jde pou₧φt jen pro ·zkΘ znaky. Pro ÜirokΘ znaku je postup podobn².
Nejprve definujeme v²Φtov² typ popisujφcφ roΦnφ obdobφ:

enum obdobi {jaro, leto, podzim, zima};

Je-li s prom∞nnß typu obdobi obsahujφcφ hodnotu podzim, m∞l by nßÜ program po zadßnφ p°φkazu

cout << s;

vypsat °et∞zec podzim. Podobn∞ p°i vstupu dat tohoto typu chceme zadßvat °et∞zce p°erdstavujφcφ jmΘna roΦnφch obdobφ, nikoli Φφsla.

Fazeta Obd_vv
Na prvnφ pohled se m∙₧e zdßt, ₧e k tomu staΦφ p°etφ₧it v hodn²m zp∙sobem operßtory << a >>, nenφ t°eba zat∞₧ovat se s fazetami nßrodnφho nastavenφ.

To je pravda, pokud nßm jde o jeden program. Pokud bychom ovÜem cht∞li upravit pou₧φvßnφ typu obdobi v ÜirÜφm kontextu û nap°. ve vÜech p°ekladaΦφch, a pokud bychom cht∞li umo₧nit pou₧φvßnφ °et∞zc∙ k prßci s roΦnφmi obdobφmi i v jin²ch nßrodnφch prost°edφch, mß smysl definovat pro tento ·Φel fazetu.

Rozhodneme se tedy, ₧e definujeme fazetu Obd_vv (vstup a v²stup roΦnφho obdobφ). Abychom umo₧nili dalÜφm programßtor∙m vytvo°it odpovφdajφcφ fazety pro jinß nßrodnφ nastavenφ, bude rozumnΘ definovat Obd_vv jako abstraktnφ t°φdu a od nφ odvodit fazetu pro ΦeskΘ nßrodnφ nastavenφ:

class Obd_vv: public locale::facet {/* ... */ }; // abstraktnφ fazeta
class CZ_Obd_vv: public Obd_vv{/* ... */ }; // Φeskß verze

P°etφ₧enΘ operßtory << a >> budou se budou odvolßvat na abstraktnφ t°φdu Obd_vv. To znamenß, ₧e veÜkerΘ slu₧by, kterΘ tato t°φda bude poskytovat, musφ b²t implementovßny jako virtußlnφ metody.

Fazeta pro roΦnφ obdobφ musφ um∞t konvertovat hodnotu v²ΦtovΘho typu na znakov² °et∞zec a znakov² °et∞zec na hodnotu v²ΦtovΘho typu. P°esn∞ji, musφ um∞t konvertovat alespo≥ hodnoty odpovφdajφcφ n∞kterΘ z v²Φtov²ch konstant. Proto bude obsahovat metody, kterΘ vtipn∞ pojmenujeme to_str() a from_str():

class Obd_vv: public locale::facet {
public:
          Obd_vv(int i = 0): locale::facet(i){}
          ~Obd_vv(){}
          virtual const string& to_str(obdobi x) const = 0
          virtual bool from_str(const string& s, obdobi& x) const = 0;
          static locale::id id;
};
locale::id Obd_vv::id;

Metoda from_str() vracφ logickou hodnotu indikujφcφ, zda se zadanou hodnotu poda°ilo konvertovat, tj. zda x obsahuje hodnotu odpovφdajφcφ n∞kterΘ z konstant.
P°ipome≥me si, ₧e tato t°φda musφ obsahovat statickou slo₧ku id typu std::locale::id, kterou bude t°φda locale pou₧φvat k jejφ identifikaci.

Od t°φdy Obd_vv odvodφme implementaci pro ΦeskΘ nastavenφ, kterou nazveme nap°. CZ_Obd_vv:

class CZ_Obd_vv: public Obd_vv
{
          static const string nazvy[];
public:
          const string& to_str(obdobi) const;
          bool from_str(const string&, obdobi&) const;
};
const string CZ_Obd_vv::nazvy[] = {"jaro","leto","podzim","zima"};

v nφ₧ budou ji₧ definovßny metody to_str() a from_str(). Dßle ovÜem musφme p°etφ₧it operßtory << a >>, aby p°i v²stupu vyu₧φvaly slu₧eb tΘto fazety. Podφvejme se nynφ na cel² program:

// Definice a pou₧itφ vlastnφ fazety
// pro formßtovßnφ Φφsel a logick²ch hodnot
#include <iostream>
#include <stdexcept>
#include <fstream>
#include <locale>
#include <algorithm>
using namespace std;
enum obdobi {jaro, leto, podzim, zima};
class Obd_vv: public locale::facet { // Abstraktnφ p°edek
public:
          Obd_vv(int i = 0): locale::facet(i){}
          ~Obd_vv(){}
          virtual const string& to_str(obdobi x) const = 0;//{static const string s("");return s;};
          virtual bool from_str(const string& s, obdobi& x) const = 0; //{return true;}
          static locale::id id;
};
locale::id Obd_vv::id;
class CZ_Obd_vv: public Obd_vv // Implementace pro ΦeskΘ
{ // nastavenφ

          static const string nazvy[];
public:
          const string& to_str(obdobi) const;
          bool from_str(const string&, obdobi&) const;
          //CZ_Obd_vv(int i = 0): Obd_vv(i){}
};
const string CZ_Obd_vv::nazvy[] = {"jaro","lΘto","podzim","zima"};
// Implementace metod pro p°evod na °et∞zec a z n∞j
const string& CZ_Obd_vv::to_str(obdobi x) const
{
          static const string s("nesprßvnΘ obdobφ");
          if(x < jaro || x > zima) return s;
                    else return nazvy[x];
}
bool CZ_Obd_vv::from_str(const string& s, obdobi& x) const
{
          const string* p = find<const string*, string>(nazvy, &nazvy[zima+1], s);
          if(p == &nazvy[zima+1]) return false;
          x = obdobi(p - nazvy);
          return true;
}
// P°etφ₧enΘ operßtory pro vstup a v²stup,
// kterΘ vyu₧φvajφ slu₧eb fazety Obd_vv

ostream& operator<<(ostream& Proud, obdobi x)
{
          const locale& loc = Proud.getloc();
          if(has_facet<Obd_vv>(loc))
                    return Proud << use_facet<Obd_vv>(loc).to_str(x);
          else
                    return Proud << int(x);
};
istream& operator>>(istream& Proud, obdobi &x)
{
          const locale &loc = Proud.getloc();
          if(has_facet<Obd_vv>(loc)){
                    const Obd_vv& f = use_facet<Obd_vv>(loc);
                    string buf;
                    if(!(Proud >> buf && f.from_str(buf, x)))
                              Proud.setstate(ios_base::failbit);
                    return Proud;
                    }
                    int i;
                    Proud >> i;
                    x = obdobi(i);
                    return Proud;
}
int main()
{
          try{
                    locale Cr("Czech_Czech Republic.852");
                    locale Kon(Cr, new CZ_Obd_vv);
                    obdobi ob = leto;
                    cout.imbue(Kon);
                    cin.imbue(Kon);
                    cout << ob << endl;
                    cin >> ob;
                    cout << int(ob) << endl;
          }
          catch(runtime_error &e)
          {
                    cerr << e.what() << endl;
          }
          return 0;
}

Implementace metody to_str() pro p°evod hodnoty typu obdobi na znakov² °et∞zec je p°φmoΦarß a nevy₧aduje komentß°e. Implementace metody from_str() je jen nepatrn∞ slo₧it∞jÜφ. K vyhledßnφ nßzvu s v poli nßzv∙ roΦnφch obdobφ pou₧φvß funkce find<>(), jejφ₧ Üablona je v hlaviΦkovΘm souboru <algorithm>.

const string* p = find<const string*, string>(nazvy, &nazvy[zima+1], s);

Prvnφ parametr udßvß ukazatel na prvnφ prvek prohledßvanΘho ·seku, druh² udßvß ukazatel za poslednφ prvek a t°etφ je hledanß hodnota. Tato funkce vrßtφ ukazatel na nalezen² °et∞zec nebo ukazatel na poslednφ prvek, pokud dan² °et∞zec nenajde. P°φkaz

x = obdobi(p - nazvy);

zjistφ pomocφ adresovΘ aritmetiky index nalezenΘho prvku, p°etypuje ho na obdobi a ulo₧φ ho do prom∞nnΘ x, v nφ₧ se vracφ v²sledek.

Implementace operßtoru << pro v²stup je op∞t pom∞rn∞ p°φmoΦarß. Tato funkce nejprve zφskß odkaz na instanci t°φdy locale datovΘho proudu,

const locale& loc = Proud.getloc();

Pak si zjistφ, zda tato instance obsahuje fazetu Obd_vv. Pokud ano, pou₧ije jejφ metodu to_str() k p°evodu na hodnoty typu obdobi °et∞zec, jinak vypφÜe zadanou hodnotu jako Φφslo. P°etypovßnφ na int v p°φkazu

return Proud << int(x);

je nezbytnΘ, nebo¥ jinak by se rekurzivn∞ volal operßtor << pro typ obdobi.

Implementace operßtoru >> pro vstup je pon∞kud slo₧it∞jÜφ. TakΘ tato funkce nejprve zφskß odkaz na instanci t°φdy locale datovΘho proudu a zjistφ, zda obsahuje pot°ebnou fazetu. Pokud ne, p°eΦte prost∞ celΘ Φφslo, p°etypuje ho na obdobi a v²sledek ulo₧φ do parametru x. Pokud ovÜem instance t°φdy locale pot°ebnou fazetu obsahuje, p°eΦte z proudu znakov² °et∞zec a pokusφ se ho p°evΘst na typ obdobi pomocφ metody from_str() naÜφ fazety; v²sledek ulo₧φ do parametru x. Jestli₧e p°i Φtenφ nebo p°i p°evodu °et∞zce neusp∞je, nastavφ p°φznak chyby v proudu.

Poznßmka
Budete/li si tento p°φklad zkouÜet, m∙₧ete narazit na nßsledujφcφ problΘm: N∞kterΘ starÜφ implementace standardnφ knihovny jazyka C++ (nap°. Borland C++Builder 4 a 5 nebo Microsoft Visual C++ 6) si z n∞jak²ch zßhadn²ch d∙vod∙ vytvß°ejφ takΘ instanci p°edka pou₧itΘ fazety. V naÜem p°φpad∞ se tedy pokusφ vytvo°it instanci t°φdy Obd_vv; pochopiteln∞ ohlßsφ chybu, ₧e nemohou vytvo°it instanci abstraktnφ t°φdy. Pomoc je snadnß, staΦφ abstraktnφm dßt metodßm implementaci û staΦφ jakßkoli, nebo¥ nebudou pou₧ity.

class Obd_vv: public locale::facet { // Kdy₧ nesmφ b²t abstraktnφ
public:
          Obd_vv(int i = 0): locale::facet(i){}
          ~Obd_vv(){}
                    virtual const string& to_str(obdobi x) const {
                    static const string s("");
          return s;
          }
          virtual bool from_str(const string& s, obdobi& x){ return true; }

          static locale::id id;
};

Na zßv∞r je t°eba jeÜt∞ jednou zd∙raznit, ₧e definice vlastnφ fazety je n∞co, co do b∞₧nΘho programu nepat°φ; samotnΘ p°etφ₧enφ operßtor∙ >> a << obstarß totΘ₧ jednoduÜeji.

O Φem jsme nehovo°ili

I kdy₧ naÜe povφdßnφ o nßrodnφm prost°edφ m∞lo Φty°i dφly, nepokr²vß vÜe, co by o tΘto problematice stßlo za napsßnφ. Nehovo°ili jsme nap°φklad o formßtovßnφ datov²ch a Φasov²ch ·daj∙, o formßtovßnφ m∞nov²ch ·daj∙ nebo o prßci s katalogem zprßv. I kdy₧ jde o v∞ci na prvnφ pohled pot°ebnΘ, prßce s nimi nenφ tak snadnß jako prßce s °et∞zci nebo s Φφsly. Jazyk C++ toti₧ neobsahuje standardnφ t°φdu Date (nebo jinou, kterß by se hodila k prßci s Φasov²mi ·daji), stejn∞ jako neobsahuje nap°. t°φdu pro reprezentaci pen∞₧nφch Φßstek. Pokud n∞co takovΘho chceme, musφme si to naprogramovat sami. Nehovo°ili jsme takΘ o prßci s nßrodnφm prost°edφm v jazyce C.

Upozorn∞nφ

Podobn∞ jako v p°edchozφch dφlech, i zde musφm Φtenß°e upozornit, ₧e souΦasnΘ p°ekladaΦe s nßrodnφm nastavenφm nezachßzejφ p°φliÜ korektn∞. Nebu∩te tedy p°ekvapeni, kdy₧ uvedenΘ p°φkladu nebudou ve vaÜem p°ekladaΦi fungovat.

V mnoha starÜφch p°ekladaΦφch narazφme na problΘmy, je₧ pochßzejφ ze skuteΦnosti, ₧e tyto p°ekladaΦe neimplementujφ vno°enΘ Üablony, neum∞jφ pou₧φt jeden parametr Üablony jako implicitnφ hodnotu nßsledujφcφho parametru, nepodporujφ explicitnφ specifikaci parametry Üablony u ÜablonovΘ funkce atd. Mφsto funkcφ use_face<>() a has_face<>() pak musφme pou₧φvat r∙znß makra.

V∞tÜina souΦasn²ch b∞₧n∞ rozÜφ°en²ch p°ekladaΦ∙ si takΘ neporadφ s p°ekladem ·zk²ch znak∙ z jednoho k≤dovßnφ do jinΘho; proto jsem v n∞kter²ch p°φkladech pou₧φval pouze ÜirokΘ znaky. (Ostatn∞ n∞kterΘ si neporadφ ani s p°ekladem Üirok²ch znak∙, pokud obsahujφ znaky nßrodnφ abecedy û a je jedno, zda jde o abecedu Φeskou, polskou, n∞meckou nebo francouzskou).
N∞kterΘ p°ekladaΦe takΘ nekorektn∞ zachßzejφ s p°edky fazetov²ch t°φd (o tom jsme mluvili v tomto pokraΦovßnφ v oddφlu v∞novanΘm vlastnφ fazet∞).

VÜechny p°φklady zde uvedenΘ ve vÜech dφlech tohoto Φlßnku jsem p°elo₧il a vyzkouÜel ve Visual C++ .NET. Ani tento p°ekladaΦ se ovÜem nechovß zcela korektn∞ (nezvlßdß p°ek≤dovßnφ ·zk²ch znak∙, jestli₧e obsahujφ znaky nßrodnφch abeced).

Kde zφskat dalÜφ informace

Problematice nßrodnφho nastavenφ se v∞nujφ auto°i knih o C++ spφÜe v²jimeΦn∞. AmeriΦany a nangliΦany to p°φliÜ nezajφmß, nebo¥ poΦφtaΦe hovo°φ p°evß₧n∞ jejich jazykem, a t∞ch n∞kolik drobnostφ, v nich₧ se odchylujφ, v∞tÜinou nep∙sobφ problΘmu. Je p°φznaΦnΘ, ₧e prvnφ podrobn∞jÜφ zmφnky o t°φd∞ locale jsem naÜel v knize n∞meckΘho autora. (V mnoha ohledech Ülo ovÜem o knihu velmi Üpatnou, a proto ji zde neuvßdφm.)

Podrobn² v²klad lze najφt v anglicky psanΘ knize [2], jejφm₧ autorem je takΘ N∞mec.

Dobr²m zdrojem informacφ je takΘ kniha B. Stroustrupa [1]. (Autor je Dßn.) V dodatku D najdete rozsßhlou kapitolu v∞novanou tΘto problematice. Musφ ovÜem jφt o pozd∞jÜφ tisky t°etφho vydßnφ nebo o vydßnφ oznaΦenΘ Special Edition. DruhΘ vydßnφ, kterΘ bylo p°elo₧eno do ΦeÜtiny, tyto informace neobsahuje. Neobsahujφ je ani prvnφ tisku t°etφho vydßnφ z r. 1997; lze si je ale stßhnout ve formßtu PDF z adresy http://www.research.att.com/~bs/3rd_loc.pdf.

Miroslav Virius

 

Odkazy

[1] B. Stroustrup: The C++ Programming Language. Addison-Wesley 2000. Dodatek D. (Pouze v nov∞jÜφch tiscφch 3. vydßnφ a ve vydßnφ oznaΦenΘm Special Edition. Lze takΘ stßhnout ve formßtu PDF ze strßnky http://www.research.att.com/~bs/3rd_loc.pdf.
[2] N. M. Josuttis: The C++ Standard Library. A Tutorial and Reference. Addison-Wesley 2000.

 

Pokud vßs problematika Internacionalizace a lokalizace v C++ zaujala, napiÜte nßm na adresu chipcd@vogel.cz o jejφ pokraΦovßnφ. OΦekßvßme takΘ vaÜe nßm∞ty a p°ipomφnky.