Showing posts with label api. Show all posts
Showing posts with label api. Show all posts

Sunday, February 19, 2017

Safer S...

I want, of course, to talk about "Safer Strings" today.


TL;DR: Add /DwxNO_UNSAFE_WXSTRING_CONV=1 to your compiler options today.


wxWidgets has had implicit conversion of wxString to const char* since the dawn of time (or about 1992, at any rate). This was always dangerous, as it allowed someone to accidentally write:

void show_and_free(const char* p) { ...; free(p); }
wxString s("...");
show_and_free(s);
with catastrophic consequences, but such situations were relatively rare and it was thought that the convenience of having this implicit conversion overweighted the dangers. This is also why when we added Unicode support later, we also added implicit conversion to const wchar_t* and, when we added "STL" build mode, in which interoperability with the standard library is increased further even at the price of backwards compatibility, we added implicit conversions to std::string and std::wstring as well.

Unfortunately, with the merge of ANSI and Unicode build modes in wxWidgets 3, another, much more dangerous, problem has appeared because in the new combined mode we can now have a string containing Unicode characters not representable in the current locale encoding. And converting such strings to either char* or std::string inevitably results in a loss of data in this case, e.g.

double convert_temperature_to_celsius(const char* p) {
    const char* end;
    double t = strtod(p, &end);
    return 5.*(t - 32)/9.;
}

wxString s = wxGetTextFromUser("Enter temperature");
convert_temperature_to_celsius(s);
could, confusingly, result in always returning -17.77777, corresponding to 0°F, if the user decided to terminate the temperature entry with "°F" to explicitly indicate the scale used and the current encoding couldn't represent the degree symbol (which is the case of e.g. CP1250 under Microsoft Windows). In this case, conversion of wxString to char* would fail and p would be just empty.

Of course, this wouldn't happen if the code just used wxString::ToDouble() directly, or used wxChar and wxStrtod(), or used UTF-8, capable of representing any Unicode character, as encoding (which is practically always the case under Unix systems nowadays). So there are a lot of ways to write this code correctly, but, unfortunately, it was still too simple to write it wrongly accidentally lose the data entered by the user in this case. Clearly, implicit conversions potentially losing data are a bad idea, but we couldn't just turn them off in wxWidgets 3, as it would have broken almost all the existing programs which, empirically, all used these conversions in many places.

For the same reason, we still won't be able to turn this conversion off by default, even in wxWidgets 3.2. However now we at least provide a way to opt-in into safer behaviour. The arguably less interesting part of the changes is that you can now change the value of the compile-time wxUSE_UNSAFE_WXSTRING_CONV option when building the library. It is set to 1 by default, for compatibility, but if you build wxWidgets for the use in your own project, you are strongly advised to set it to 0 to permanently disable the unsafe, in the sense described above, implicit conversions.

Many people, however, don't build their own library, but use the one provided by their package manager under Unix/macOS or download our MSW binaries. These official binaries will continue to provide the unsafe conversions for compatibility, but you can define wxNO_UNSAFE_WXSTRING_CONV when building your own project to disable their use in your code without rebuilding the library. This symbol can be just #define'd before including any wxWidgets headers, but it is better to define it globally, in the compiler options in your make- or project file: just add /DwxNO_UNSAFE_WXSTRING_CONV=1 to it. And the main point of this long post is to convince you that you NEED TO DO just that: please define wxNO_UNSAFE_WXSTRING_CONV for your code and fix the resulting compilation errors to ensure that you don't lose any data entered by the user.

Fixing the compilation errors will, generally speaking, involve doing one of two things:

  • Either stop using char* (or std::string in the STL build) entirely and use wxString directly.
  • Or convert it to wchar_t* (or std::wstring) or convert wxString to UTF-8 encoding which will never lose data, using methods such as utf8_str(), which is a convenient synonym for mb_str(wxConvUTF8), or ToStdString(wxConvUTF8).
Of course, if you really need to use the current locale encoding, e.g. because you call a C standard library function using it, you will still need to perform the conversion to it, using just plain mb_str() and there will still be a possibility of the conversion to it failing, but at least now it won't happen implicitly.

Thanks for reading all this and, if you jumped to the end, hoping to quickly find the conclusion instead of reading this wall of text, please see the conclusion in the very beginning!

Friday, April 01, 2016

Improving Inherently Important Internationalisation Issues

One of the lesser-known advantages of wxWidgets compared to many other libraries is that, using its API, you can centre a window on the screen and set its colour to grey without having to Americanise your programme. But, as natural as this behaviour is, and in spite of agreeing to wxWidgets licence (the very spelling of which should have been a sufficient hint), some users still expressed their surprise in their dialogue with us and refused our advice to accept using this flavour of the API. And so, to accommodate them, wxWidgets has traditionally provided alternatives such as Center() method, wxColor class and wxGRAY constant.

Admittedly, in practice this has been sufficient to avoid any problems for a long time while still remaining true to wxWidgets historically British roots. However, in addition to being British, wxWidgets was always mostly a European effort and, besides, knowing that it was actually born in Edinburgh, it might well end being exclusively European and not British at all in the near future (but don't worry, even if the worst happens, we plan to continue supporting British spelling for at least two more release cycles, in accordance with the general backwards compatibility policy). Considering this, it seems especially strange to have alternative American versions of the spelling, but no attention being paid to the other European languages.

And now, after releasing 3.1.0, it's finally time to do something about it as, clearly, there are few unresolved issues of comparable importance left. To begin with, I obviously had to add wxCouleur class which should undoubtedly help with wxWidgets adoption in francophone countries. But adding just the French variant would certainly be undiplomatic, so another pull request adds, in the grand European tradition of Franco-German alliance, wxFarbe, and both of them will be merged together to avoid any questions of precedence. Further, France and Germany notwithstanding, I certainly don't plan to discriminate against other countries but, in the usual spirit of Open Source, additions of wxColore, wxFarve, wxKleur etc will have to wait until the appropriate patches are submitted (luckily for them, Spanish users can, in the meanwhile, already reuse the American variant, just as they already do with many other words, e.g. burrito, for the classic American food).

Of course, nothing is ever as simple as planned and, while working on this, I quickly realized that Greek might present some practical difficulties as UTF-8 support is still, even in 2016, not perfect on some of the legacy platforms supported by wxWidgets, but it would be difficult to use wxΧρώμα without it. A compromise solution that we're currently discussing would be to introduce a wxChroma class right now and deprecate it by the time 3.2 is released as Unicode support should really be widely available everywhere by then (3.2 release is currently planned for the early 2030-ies).

I do hope you will enjoy these changes, but, let's be honest, this is just the beginning and we clearly still have a lot of work to do in this area. Fully supporting colour internationalization is important, but it's just the first step and we plan to go much further. Just imagine to be able to open a wxFenêtre and choose a wxStylo for drawing on it and then, perhaps if you live in Switzerland, create a wxKnopf as its child. The possibilities are endless! Unfortunately, the same can't be said about our resources, so we count on your help to make wxWidgets the best internationalized library of all time -- and we're looking forward to your contributions!

Wednesday, May 01, 2013

Simpler way to create fonts

Just a quick note about the new and simpler way to create fonts in wxWidgets 2.9: in addition to a multitude of wxFont constructors taking various font attributes, such as size, style, weight, face name and so on, there is now another constructor, taking a single wxFontInfo object that combines all of them together and allows to use the named parameter idiom for the font creation.

It may seem non-obvious how can adding another constructor help with the problem of having too many constructors, so here is a quick example to show how it does: suppose you want to create an underlined, 12 point version of "Courier" font. Here is how you'd do it currently:

wxFont f(12, wxFONTFAMILY_DEFAULT, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_NORMAL, true, "Courier");
And here is how you'd do it with the new constructor:
wxFont f(wxFontInfo(12).Underlined().FaceName("Courier"));

Hopefully the advantages from the point of view of the library user are clear:

  • You don't need to specify the default parameters such as wxFONTFAMILY_DEFAULT, that you don't care about.
  • Each font attribute is clearly written out, while the meaning of true (which meant that the font should be underlined, by the way) was hardly obvious in the first form.
  • You can specify the parameters in any order instead of having to check the documentation every time.
  • Last and really least but still: you can create strike-through fonts (notice that they are only supported under MSW and GTK currently) using the new constructor using the natural Strikethrough() syntax, while this wasn't supported at all with the old constructor.
From our, i.e. developers, point of view, there is one extra important advantage, related to the last point: if support for a new wxFont attribute is added later, this way of creating fonts will be easy to extend to support it.

So my advice is to use wxFontInfo in all the new code using wxWidgets 2.9. The old constructors are not going anywhere and can still be used (except for the one taking int flags which was added for 2.9.4 and now removed to avoid ambiguities with the other existing constructors), but there is no reason to prefer them to the new one.