Notice that Unicode support has changed radically in wxWidgets 3.0 and a lot of existing material pertaining to the previous versions of the library is not correct any more. Please see Unicode-related Changes for the details of these changes.
You can skip the first two sections if you're already familiar with Unicode and wish to jump directly in the details of its support in the library:
From a practical point of view, using Unicode is almost a requirement when writing applications for international audience. Moreover, any application reading files which it didn't produce or receiving data from the network from other services should be ready to deal with Unicode.
From the C/C++ programmer perspective the situation is further complicated by the fact that the standard type wchar_t which is used to represent the Unicode ("wide") strings in C/C++ doesn't have the same size on all platforms. It is 4 bytes under Unix systems, corresponding to the tradition of using UTF-32, but only 2 bytes under Windows which is required by compatibility with the OS which uses UTF-16.
However, unlike Unicode build mode in the previous versions of wxWidgets, this support is mostly transparent: you can still continue to work with the narrow (i.e. char*) strings even if wide (i.e. wchar_t*) strings are also supported. Any wxWidgets function accepts arguments of either type as both kinds of strings are implicitly converted to wxString, so both
wxMessageBox("Hello, world!");
wxMessageBox(L"Salut \u00e0 toi!"); // 00E0 is "Latin Small Letter a with Grave"
Notice that the narrow strings used with wxWidgets are always assumed to be in the current locale encoding, so writing
wxMessageBox("Salut à toi!");
wxMessageBox(wxString::FromUTF8("Salut \xc3\xa0 toi!"));
In a similar way, wxString provides access to its contents as either wchar_t or char character buffer. Of course, the latter only works if the string contains data representable in the current locale encoding. This will always be the case if the string had been initially constructed from a narrow string or if it contains only 7-bit ASCII data but otherwise this conversion is not guaranteed to succeed. And as with FromUTF8() example above, you can always use ToUTF8() to retrieve the string contents in UTF-8 encoding -- this, unlike converting to char* using the current locale, never fails
To summarize, Unicode support in wxWidgets is mostly transparent for the application and if you use wxString objects for storing all the character data in your program there is really nothing special to do. However you should be aware of the potential problems covered by the following section.
char and wchar_t, wxString implementation is rather involved and many of its operators don't return the types which they could be naively expected to return. For example, the operator[] doesn't return neither a char nor a wchar_t but an object of a helper class wxUniChar or wxUniCharRef which is implicitly convertible to either. Usually you don't need to worry about this as the conversions do their work behind the scenes however in some cases it doesn't work. Here are some examples, using a wxString object s and some integer n:
switch ( s[n] )
s[n] with s[n].GetValue()
(wxChar)s[n]
&s[n]
char* or wchar_t*. Consider using string iterators instead if possible or replace this expression with s.c_str() + n
Another class of problems is related to the fact that the value returned by c_str() itself is also not just a pointer to a buffer but a value of helper class wxCStrData which is implicitly convertible to both narrow and wide strings. Again, this mostly will be unnoticeable but can result in some problems:
c_str() result to vararg functions such as standard printf(). Some compilers (notably g++) warn about this but even if they don't, this printf("Hello, %s", s.c_str())
wxPrintf("Hello, %s", s)
c_str(), it is not needed at all with wxWidgets functions)wxPrintf("Hello, %s", s.c_str())
printf("Hello, %s", (const char *)s.mb_str())
printf("Hello, %s", (const char *)s.c_str())
c_str() can not be cast to char* but only to const char*. Of course, modifying the string via the pointer returned by this method has never been possible but unfortunately it was occasionally useful to use a const_cast here to pass the value to const-incorrect functions. This can be done either using new wxString::char_str() (and matching wchar_str()) method or by writing a double cast: (char *)(const char *)s.c_str()
wxPrintf() without using c_str() is that it is now impossible to pass the elements of unnamed enumerations to wxPrintf() and other similar vararg functions, i.e. enum { Red, Green, Blue }; wxPrintf("Red is %d", Red);
Other unexpected compilation errors may arise but they should happen even more rarely than the above-mentioned ones and the solution should usually be quite simple: just use the explicit methods of wxUniChar and wxCStrData classes instead of relying on their implicit conversions if the compiler can't choose among them.
To be precise, the conversion will always succeed if the string was created from a narrow string initially. It will also succeed if the current encoding is UTF-8 as all Unicode strings are representable in this encoding. However initializing the string using FromUTF8() method and then accessing it as a char string via its c_str() method is a recipe for disaster as the program may work perfectly well during testing on Unix systems using UTF-8 locale but completely fail under Windows where UTF-8 locales are never used because c_str() would return an empty string.
The simplest way to ensure that this doesn't happen is to avoid conversions to char* completely by using wxString throughout your program. However if the program never manipulates 8 bit strings internally, using char* pointers is safe as well. So the existing code needs to be reviewed when upgrading to wxWidgets 3.0 and the new code should be used with this in mind and ideally avoiding implicit conversions to char*.
Even despite caching the index, indexed access should be replaced with sequential access using string iterators. For example a typical loop:
wxString s("hello"); for ( size_t i = 0; i < s.length(); i++ ) { wchar_t ch = s[i]; // do something with it }
wxString s("hello"); for ( wxString::const_iterator i = s.begin(); i != s.end(); ++i ) { wchar_t ch = *i // do something with it }
Another, similar, alternative is to use pointer arithmetic:
wxString s("hello"); for ( const wchar_t *p = s.wc_str(); *p; p++ ) { wchar_t ch = *i // do something with it }
NUL characters and the use of iterators is generally preferred as they provide some run-time checks (at least in debug build) unlike the raw pointers. But if you do use them, it is better to use wchar_t pointers rather than char ones to avoid the data loss problems due to conversion as discussed in the previous section.
wxString also provides two convenience functions: From8BitData() and To8BitData(). They can be used to create wxString from arbitrary binary data without supposing that it is in current locale encoding, and then get it back, again, without any conversion or, rather, undoing the conversion used by From8BitData(). Because of this you should only use From8BitData() for the strings created using To8BitData(). Also notice that in spite of the availability of these functions, wxString is not the ideal class for storing arbitrary binary data as they can take up to 4 times more space than needed (when using wchar_t internal representation on the systems where size of wide characters is 4 bytes) and you should consider using wxMemoryBuffer instead.
Final word of caution: most of these functions may return either directly the pointer to internal string buffer or a temporary wxCharBuffer or wxWCharBuffer object. Such objects are implicitly convertible to char and wchar_t pointers, respectively, and so the result of, for example, ToUTF8() can always be passed directly to a function taking const char*. However code such as
const char *p = s.ToUTF8(); ... puts(p); // or call any other function taking const char *
p is left pointing nowhere. To correct this you may use wxCharBuffer p(s.ToUTF8()); puts(p);
const wxUTF8Buf p(s.ToUTF8()); puts(p);
wxUTF8Buf is the type corresponding to the real return type of ToUTF8(). Similarly, wxWX2WCbuf can be used for the return type of wc_str(). But, once again, none of these cryptic types is really needed if you just pass the return value of any of the functions mentioned in this section to another function directly.wxUSE_UNICODE is now defined as 1 by default to indicate Unicode support. If UTF-8 is used for the internal storage in wxString, wxUSE_UNICODE_UTF8 is also defined, otherwise wxUSE_UNICODE_WCHAR is.
|
[ top ] |