/* toupper.cpp
*
* COMMON="-ansi -pedantic -Wall -Wextra -o toupper toupper.cpp"
*
* If you saved this file with UTF-8 encoding:
* g++ $COMMON -finput-charset=UTF-8
*
* If you saved this file with latin2 encoding:
* g++ $COMMON -finput-charset=latin2
*
* Run the binary:
* ./toupper
*/
#define GPP_BUG_35353 1 /* http://gcc.gnu.org/bugzilla/show_bug.cgi?id=35353 */
#if GPP_BUG_35353
# include <clocale> /* ::setlocale() */
#endif
#include <functional> /* std::bind1st() */
#include <string> /* std::wstring */
#include <locale> /* std::locale */
#include <algorithm> /* std::for_each() */
#include <iostream> /* std::wcout */
/* Function object composition. Takes (= copies) two function objects that
* conform to C++03 20.3 Function objects [lib.function.objects] paragraph 5,
* and returns a function object that implements their composition, and also
* conforms to said paragraph.
*/
template <class Op1, class Op2>
class Compose : public std::unary_function<typename Op1::argument_type,
typename Op2::result_type>
{
public:
Compose(const Op1& op1, const Op2& op2) : op1(op1), op2(op2) {}
typename Op2::result_type operator()(const typename Op1::argument_type &in)
const { return op2(op1(in)); }
private:
const Op1 op1;
const Op2 op2;
};
template <class Op1, class Op2> Compose<Op1, Op2>
compose(const Op1& op1, const Op2& op2)
{
return Compose<Op1, Op2>(op1, op2);
}
int
main()
{
#if GPP_BUG_35353
::setlocale(LC_ALL, "");
#endif
/* Phase 1.
*
* Dependent on the "-finput-charset" g++ option, wide characters of this
* string that fall outside the basic source character set are mapped to
* universal characters correctly. C++03 2.1 Phases of translation
* [lex.phases] paragraph 1.
*
* A universal-character-name is then encoded into the execution character
* set, see C++03 2.13.2 Character literals [lex.ccon] paragraph 5. See also
* the "-fwide-exec-charset" g++ option -- the default should be OK (UCS-2
* (UTF-16) or UCS-4 (UTF-32), based on the size of "wchar_t").
*/
const std::wstring str(L"árvíztűrő tükörfúrógép");
/* Get the user's preferred locale from the environment. See C++03 22.1.1.2
* locale constructors and destructor [lib.locale.cons] paragraph 8, and
* C++03 22.2.8 Program-defined facets [lib.facets.examples] paragraph 5.
*/
const std::locale cur_loc("");
#if !GPP_BUG_35353
/* Ensure wide cout formats / converts according to user preference. This is
* a mess. See C++03 27.4.2.3 ios_base locale functions
* [lib.ios.base.locales] and C++03 22.2.8 Program-defined facets
* [lib.facets.examples].
*/
std::wcout.imbue(cur_loc);
#endif
/* Thus this prints str to stdout, according to the users LC_CTYPE. */
std::wcout << str << std::endl;
/* Phase 2.
*
* This typedef basically means "LC_CTYPE for wchar_t", for any given given
* locale. See C++03 22.2.1.1 Class template ctype [lib.locale.ctype].
*/
typedef std::ctype<wchar_t> lc_ctype;
/* Capture the ctype category of the user's preferred locale. See C++03
* 22.1.1 Class locale [lib.locale] paragraph 4.
*/
const lc_ctype& lc_ctype_cur = std::use_facet<lc_ctype>(cur_loc);
/* "Program C++ in Haskell".
*
* See C++03 20.3 Function objects [lib.function.objects] and C++03 25.1.1
* For each [lib.alg.foreach].
*
* lc_ctype::toupper() is a member function. For any given "lc_ctype" locale
* category object, it implements "toupper". It takes a "wchar_t".
*
* std::mem_fun() as used here takes a pointer to a member function, and
* returns a function object whose operator() takes two parameters: (1)
* pointer to the object on which we'd like to invoke the member function,
* (2) the argument to pass to the member function.
*
* std::bind1st() takes a function object whose operator() takes two
* parameters, and binds the first. The returned function object only takes
* one parameter (the unbound, second one).
*
* We create the following function objects here:
*
* (a) A functor that runs the lc_ctype::toupper() member function on an
* "lc_ctype" instance, and passes a "wchar_t" to it. This functor
* upper-cases a wide character in some locale.
*
* (b) A functor that binds functor (a) to the users preferred locale's
* LC_CTYPE category, so that functor (b) only takes a "wchar_t". This
* functor implements the traditional "toupper" in the user's preferred
* locale.
*
* (c) A functor thar runs the std::wostream::put() member function on an
* "std::wostream" instance, and passes a "wchar_t" to it. This functor
* prints a wide character to some wide output stream.
*
* (d) A functor that binds functor (c) to "std::wcout", so that functor (d)
* only takes a "wchar_t". This functor prints a wide character to
* standard output.
*
* (e) A functor that is the composition of (b) and (d): it upper-cases a
* wide character in the user's preferred locale, then prints it to
* standard output.
*
* Then we run functor (e) on all elements of "str".
*/
std::for_each(str.begin(), str.end(), compose(
std::bind1st(std::mem_fun(&lc_ctype::toupper), &lc_ctype_cur),
std::bind1st(std::mem_fun(&std::wostream::put), &std::wcout)
));
std::wcout << std::endl;
}
- uid_2716 blogja
- A hozzászóláshoz be kell jelentkezni
- 80254 megtekintés
Hozzászólások
A fájl UTF-8-ban mentve, majd clang-2.8 és g++ 4.2.1 eredménye FreeBSD-n (ugyenzt kapom, ha UTF-8-at használó locale-lal futtatom UTF-8-as xterm-ben)
$ ./toupper.clang
terminate called without an active exception
Abort trap
$ ./toupper.gcc
terminate called after throwing an instance of 'std::runtime_error'
what(): locale::facet::_S_create_c_locale name not valid
Abort trap
$ locale
LANG=hu_HU.ISO8859-2
LC_CTYPE="hu_HU.ISO8859-2"
LC_COLLATE="hu_HU.ISO8859-2"
LC_TIME="hu_HU.ISO8859-2"
LC_NUMERIC="hu_HU.ISO8859-2"
LC_MONETARY="hu_HU.ISO8859-2"
LC_MESSAGES="hu_HU.ISO8859-2"
LC_ALL=
$
- A hozzászóláshoz be kell jelentkezni
Könnyen reprodukálható nálam is, ha valamelyik LC_XXX változóban érvénytelen értéket adok meg. A "locale" parancs (paraméterek nélkül) csak annyit jelez vissza, hogy a felhasználó milyen beállításokat szeretne érvényesíteni; arról nem szól, hogy ebből mi elégíthető ki.
Ez a sor hajít el egy std::runtime_error-t (a [lib.locale.cons] p7 által dokumentáltan):
const std::locale cur_loc("");
Biztosat persze csak úgy fogsz tudni, ha engedélyezed a core dump írását, és belenézel gdb-vel (... és ha a forrást -g3-mal fordítod).
Az a parancs, hogy "locale -k", nem jelez hibát? Pl. LC_MESSAGES hiányára nem panaszkodik?
... Arra akarok kilyukadni, hogy a hu_HU.ISO8859-2 nincs 100%-osan telepítve. Egy "rendes" programnak természetesen az ilyen hibákat is kezelnie kell, de most ezzel nem foglalkoztam. Pl. a ::setlocale() visszatérési értékét sem ellenőrzöm.
Az LC_MESSAGES legegyszerűbb ellenőrzése (a fenti LANG beállítás mellett):
touch /tmp/dummy
rm -i /tmp/dummy
Ha az "rm" kérdésére "i"-vel (és nem "y"-nal) válaszolsz, letörli a file-t?
- A hozzászóláshoz be kell jelentkezni
Hm, kösz a tippet. A locale -k FreeBSD-n paramétert vár. Ellenben az rm -es kérdésed talált. Már csak az a furcsa, hogy vagy 15 éve én csináltam meg a FreeBSD hu locale-ját, és tudom, hogy akkor még volt benne yesstr és nostr :-) Persze az is lehet, hogy simán az rm nincs felkészítve a locale kezelésére. Erre utal ez is:
$ locale -k yesstr
yesstr="igen"
$ locale -k nostr
nostr="nem"
$
Már csak arra nem emlékszem, mi a neve a rövid yesstr/nostr változóknak. (Pár locale-t lekérdezve, elég kevésben van ez a kettő implementálva. Szégyen.)
- A hozzászóláshoz be kell jelentkezni
Ha jól látom, újabban yesexpr és noexpr szerepel a szabványban, és ezek kiterjesztett reguláris kifejezések.
- A hozzászóláshoz be kell jelentkezni
Kosz, valoban. Es mit ad isten, most megneztem ezt az egeszet 9.0-s FreeBSD-n. Immar hu_HU.UTF-8 a locale, a magyaritas jo is, yesstr/nostr/yesexpr/noexpr mind gyonyoru; g++ ugyanaz, de immar 3.0-as clang/llvm. Es a hibauzenet is ugyanaz, szoval valami mast hianyolunk abbol a locale-bol, nem az igen es nem szavakat.
- A hozzászóláshoz be kell jelentkezni
Ha van kedved vacakolni vele, töröld a LANG és LC_ALL környezeti változókat, az LC_CTYPE-ot állítsd be magyarra, a maradékot pedig egyesével próbálgasd.
- A hozzászóláshoz be kell jelentkezni