2014-09-04 7 views
6

Ich denke, es ist sicher zu sagen, dass C-Locales allgemein als eine schlechte Idee anerkannt werden.Warum ruft QCoreApplication `setlocale (LC_ALL," ")` standardmäßig unter Unix/Linux auf?

Das Schreiben einer Anwendung, die versucht, textbasierte Maschinenformate zu parsen oder zu schreiben (was ziemlich oft vorkommt) mit C-Standardbibliotheksfunktionen wird nahezu unmöglich, wenn Sie berücksichtigen müssen, dass das Gebietsschema anders als "C" ist. Da das Gebietsschema normalerweise pro Prozess ist (und setlocale oft nicht threadsicher ist), wenn Sie eine Bibliothek schreiben oder ein Multithread-Programm haben, ist es nicht einmal sicher, setlocale(LC_ALL, "C") zu tun und es wiederherzustellen, nachdem Sie Ihre Sachen gemacht haben.

Aus diesen Gründen lautet die Regel normalerweise "vermeiden setlocale, Punkt"; aber: wir wurden in der Vergangenheit mehrmals durch das eigenartige Verhalten von QCoreApplication und abgeleiteten Klassen gebissen; die documentation sagt:

Auf Unix/Linux Qt ist konfiguriert, um die Systemgebietsschemaeinstellungen standardmäßig zu verwenden. Dies kann zu einem Konflikt bei der Verwendung von POSIX-Funktionen führen, beispielsweise beim Konvertieren zwischen Datentypen wie Fließkommazahlen und Zeichenfolgen, da die Schreibweise von Gebietsschemata abweichen kann. Um dieses Problem zu umgehen, rufen Sie die POSIX-Funktion setlocale(LC_NUMERIC,"C") gleich nach der Initialisierung QApplication oder QCoreApplication, um das Gebietsschema, das für die Zahlenformatierung verwendet wird, auf "C" -Locale zurückgesetzt.

Dieses Verhalten wurde in another question beschrieben; Meine Frage ist: Was könnte der Grund für dieses scheinbar törichte Verhalten sein? Vor allem, was an Unix und Linux so besonders ist, hat eine solche Entscheidung nur auf diesen Plattformen ausgelöst?

(Übrigens wird alles brechen, wenn ich setlocale(LC_ALL, "C"); kurz nach dem QApplication zu schaffen? Wenn es in Ordnung ist, warum sie nicht entfernen nur ihre setlocale(LC_ALL, "");?)

+0

Auf Linux-Wide-Char-Funktionen (z. B. wcstok) hat einen zusätzlichen Parameter, um es multithread sicher zu machen. QT sicherlich verwenden Standard-libc wide char-Funktionen auf Linux ... –

Antwort

6

Aus Untersuchungen über den Code Qt Quelle von @Phil Armstrong und mir durchgeführt (siehe the chat log), so scheint es, dass der setlocale Ruf seit der Version 1 aus mehreren Gründen gibt es:

  • XIM, zumindest in der alten mal, das aktuelle locale ohne einen solchen Aufruf nicht richtig "bekommen".
  • Unter Solaris stürzte es sogar mit dem Standard-C-Gebietsschema ab.
  • Auf Unix-Systemen wird (unter anderem in einem komplexen Spiel von Fallbacks) der "Systemzeichensatz" (was auch immer das unter Unix bedeutet) "geschnuppert" und kann somit zwischen der QString Darstellung und der die "lokale" 8-Bit-Codierung (dies ist besonders kritisch für Dateipfade).

Es stimmt, dass es bereits die LC_* Umgebungsvariablen überprüft, wie es mit QLocale tut, aber ich nehme an, dass es nützlich sein kann nl_langinfo dekodieren die aktuellen LC_CTYPE, wenn die Anwendung explizit geändert, es zu haben (aber wenn es zu sehen ist eine explizite Änderung, sie muss mit den Systemstandards beginnen).

Es ist interessant, dass sie tat ein setlocale(LC_NUMERIC, "C") unmittelbar nach dem setlocale(LC_ALL, ""), aber this was removed in Qt 4.4. Die Gründe für diese Entscheidung scheinen in der Aufgabe # 132859 des alten Qt-Bugtrackers zu liegen (der zwischen TrollTech, Nokia und QtSoftware.com wechselte, bevor er spurlos verschwand, nicht einmal in der Wayback Machine), und es wird in bugs Bezug genommen dieses Thema. Ich denke, dass eine autoritative Antwort zu dem Thema da war, aber ich finde keine Möglichkeit, sie wiederherzustellen.

Meine Vermutung ist, dass es subtile Bugs eingeführt, da die Umwelt ursprünglich schien, aber es war in der Tat durch den setlocale Anruf in all berühren aber die LC_NUMERIC Kategorie (die das offensichtlichste ist); Wahrscheinlich haben sie den Aufruf entfernt, um die Gebietsschemaeinstellungen deutlicher zu machen, und die Anwendungsentwickler müssen entsprechend agieren.

+1

Gute Zusammenfassung Matteo. Meine persönliche Überzeugung ist, dass eine gut benützte Unix-Anwendung 'setlocale (LC_ALL," ")' in der Anwendungsinitialisierungsphase (wahrscheinlich in der Nähe des Anfangs von 'main()') aufrufen sollte. Innerhalb einer dynamisch geladenen Bibliothek wie Qt ist jedoch kein guter Platz, aus Gründen der Programmiererüberraschung, wenn nichts anderes. Die von uns aufgedeckte Geschichte legt nahe, dass die Qt-Entwickler gute Gründe hatten, sie ursprünglich einzubeziehen, und die Auswirkungen des Entfernens des Codes könnten dazu führen, dass die Qt-Entwickler sie nur widerwillig entfernen. –

+0

Das Gebietsschema "C", das standardmäßig auf Apple liegt (als Ergebnis von setlocale mit leerer Zeichenfolge), führt zum Absturz von Anwendungen mit ungültigen Zeichenfolgenfehlern. Es ist auch nicht empfehlenswert, POSIX-Wide-Char-Funktionen im Qt-Programm zu verwenden, während Framework eine portable Schnittstelle für dieselbe Funktionalität bietet – Swift

2

Was ist POSIX-Systemen so eigen ist (die enthält Unix/Linux-Systeme, die Sie erwähnen) ist, dass die OS-Schnittstelle und die C-Schnittstelle verwechselt werden. Insbesondere der Aufruf C setlocale stört das OS.

Unter Windows ist das Gebietsschema explizit eine per-thread-Eigenschaft (SetThreadLocale), aber wichtiger ist, dass Funktionen wie GetNumberFormat einen Gebietsschema-Parameter akzeptieren.

Beachten Sie, dass Ihr Problem relativ einfach gelöst werden kann: Wenn Sie Qt verwenden, verwenden Sie Qt. Das heißt also reading your text input into a QString, es zu verarbeiten und dann zurückschreiben.

+0

Wie ändert 'setlocale' etwas mehr als es unter Windows tut? Beide betreffen nur die C-Standard-Bibliotheksfunktionen (der Kernel weiß nichts über das Gebietsschema), die sowieso von Qt umgangen werden (zu Lokalisierungszwecken scheint es einen eigenen QLocale zu haben, der nichts mit den kaputten C/C++ - Einrichtungen zu tun hat). Außerdem ist das Problem leider nicht so einfach zu lösen - wir haben mehrere Bibliotheken, die aus "normalem" C++, Qt-C++, "normalem" Python (über SIP) und Python + PyQt verwendet werden müssen eine Option, noch ist tatsächlich notwendig. –

+0

POSIX hat das C-Standardbibliotheksgebietsschema und baut darauf auf. Ansonsten hat der Linux-Kernel kein Gebietsschema. Windows dagegen hat eine native Gebietsschema-Unterstützung, auch für Nicht-C-Sprachen. Es ist also nicht so, dass 'setlocale' sich mehr unter Linux ändert, sondern Dinge, die unter Windows nicht geändert werden können. – MSalters

+0

Aber diese Dinge scheinen nicht von Interesse zu sein, im gesamten Qt-Source-Tree gibt es nur zwei Aufrufe von 'SetThreadLocale' und eine von' SetLocaleInfo', und alle sind in Unit-Tests. Auch wenn Qt bei der Erstellung der 'QApplication' ein" general locale setup "benötigte, wäre es sinnvoll, alles an der gleichen Stelle zu finden, aber nur für Unix-basierte Betriebssysteme. Deshalb bin ich perplex. –

3

Qt ruft setlocale(LC_ALL, ""), weil es das Richtige zu tun ist: Jedes Standard-Unix-Programm von cat auf Ruft setlocale(LC_ALL, "") auf. Die Konsequenz dieses Aufrufs ist, dass das Programmgebietsschema auf das vom Benutzer angegebene festgelegt wird. Siehe die setlocale() manpage:

Beim Start des Hauptprogramms, das tragbare locale "C" ausgewählt ist als Standard.

setlocale(LC_ALL, "");

nach Programminitialisierung ...: Ein Programm kann durch den Aufruf zu allen lokalen Umgebungen tragbaren gemacht werden

Da Qt sowohl Text vom Benutzer gelesen werden erzeugt und analysiert durch den Benutzer erzeugten Eingang, wäre es sehr unfreundlich zu verweigern, der Benutzer mit dem Benutzer in ihrer eigenen locale-spezifischen Weise kommunizieren zu lassen. Daher der Aufruf von setlocale().

Ich würde hoffen, dass benutzerfreundlich wäre unumstritten! Das Problem tritt natürlich auf, wenn Sie versuchen, Datendateien zu analysieren, die von Ihrem Programm unter einem anderen Gebietsschema erstellt wurden. Wenn Sie ein Ad-hoc-Text-basiertes Format mit einem Parser auf Basis von sscanf und Freunden verwenden und kein spezifisches Datenformat mit einem "echten" Parser verwenden, dann ist dies ein Rezept für Datenkorruption, wenn Sie ohne Berücksichtigung der Gebietsschemaeinstellungen. Die Lösung besteht darin, entweder a) eine echte Serialisierungsbibliothek zu verwenden, die dieses Zeug für Sie verarbeitet, oder b) das Gebietsschema auf etwas Bestimmtes zu setzen ("C" vielleicht), wenn Daten geschrieben und gelesen werden.

Wenn Thread-Sicherheit ein Problem ist, dann auf modernen POSIX-Implementierungen (oder jedem Linux-System mit GNU libc Version> = 2.3, die zu diesem Zeitpunkt so ziemlich "alle von ihnen" ist) können Sie uselocale() aufrufen thread-lokales Gebietsschema für alle E/A. Alternativ können Sie _l Versionen der üblichen Funktionen aufrufen, die ein Locale-Objekt als zusätzliches Argument verwenden.

Wird alles brechen, wenn Sie setlocale(LC_ALL, "C"); anrufen? Nein, aber das Richtige ist, den Benutzer das gewünschte Gebietsschema festlegen zu lassen und entweder Ihre Daten in einem genau festgelegten Format zu speichern oder das Gebietsschema anzugeben, in dem Ihre Daten zur Laufzeit gelesen und geschrieben werden sollen.

+0

Wie auch immer, die Vorzüge von C-Locales sind meistens irrelevant; Qt hat seine eigenen (viel besseren) Möglichkeiten, sich mit Lokalisierung zu befassen (siehe 'QLocale' und das Translations-Framework), die C-Locales in keiner Weise zu verwenden scheinen. Auch das Argument, den C-Funktionen "das Richtige" aufzuerlegen, ist nicht stichhaltig, da unter Windows der Aufruf "setlocale" ganz vermieden wird. Qt muss wahrscheinlich diesen seltsamen Nebeneffekt aufrufen, der nur für POSIX benötigt wird, aber ich kann nicht genau feststellen, was es ist. –

+0

Das wäre 'strtod_l()'. Oder rufen Sie einfach 'uselocale()'. –

+0

Keiner von ihnen ist auf dem Linux-Rechner verfügbar, von dem ich schreibe, keiner von ihnen ist portabel C; der Ruby-Interpreter fuhr sogar fort, seine eigene - leicht kaputte - Version von 'strtod' zu bündeln, weil es keine tragbare sichere Alternative gibt. Selbst wenn ich eine Nicht-Standard-Funktion in * meinem * Code verwenden würde, kann ich sicherlich keine Drittanbieter-Bibliothek reparieren, die eine "Strtod" verwendet. Im Ernst, der einzige sichere Weg, um in C zu gehen, ist, sich an das C-Gebietsschema zu halten. Aber wieder, wir sind abschweifend, der Punkt ist, "warum tut Qt diesen Anruf, der möglicherweise viele Sachen brechen kann, und warum nur auf POSIX"? –

Verwandte Themen