2014-09-04 11 views
6

Penso che sia sicuro dire che le localizzazioni C sono universalmente riconosciute come una cattiva idea.Perché QCoreApplication chiama `setlocale (LC_ALL," ")` di default su Unix/Linux?

Scrittura di un'applicazione che tenta di analizzare o scrivere formati di macchina basati su testo (cosa che accade abbastanza spesso) con le funzioni di libreria standard di C diventa quasi impossibile se si deve tenere conto dell'impostazione locale impostata su qualcosa di diverso da "C". Poiché la localizzazione è normalmente per processo (e setlocale spesso non è thread-safe), se stai scrivendo una libreria o hai un programma multithreading non è sicuro nemmeno fare setlocale(LC_ALL, "C") e ripristinarlo dopo aver fatto il tuo lavoro.

Ora, per questi motivi la regola è normalmente "evitare setlocale, punto"; ma: siamo stati morsi diverse volte in passato dal comportamento particolare di QCoreApplication e classi derivate; il documentation dice:

Su Unix/Linux Qt è configurato per utilizzare le impostazioni internazionali del sistema per impostazione predefinita. Ciò può causare un conflitto quando si utilizzano le funzioni POSIX, ad esempio, durante la conversione tra tipi di dati come float e stringhe, poiché la notazione potrebbe differire tra le impostazioni internazionali. Per ovviare a questo problema, chiamare la funzione POSIX setlocale(LC_NUMERIC,"C") subito dopo l'inizializzazione di QApplication o QCoreApplication per reimpostare le impostazioni locali utilizzate per la formattazione dei numeri su "C" -locale.

Questo comportamento è stato descritto in another question; la mia domanda è: quale potrebbe essere la logica di questo comportamento apparentemente sciocco? In particolare, cosa c'è di così peculiare su Unix e Linux che ha spinto tale decisione solo su queste piattaforme?

(Per inciso, sarà tutto rompersi se io faccio solo setlocale(LC_ALL, "C"); dopo aver creato il QApplication? Se va bene, perché non hanno semplicemente rimuovere il loro setlocale(LC_ALL, "");?)

+0

Su linux wide char functions (es: wcstok) ha un parametro extra per renderlo sicuro multithread. QT usa sicuramente le funzioni lib char standard su linux ... –

risposta

6

Da indagini attraverso il codice sorgente di Qt condotti da @Phil Armstrong e me (vedi the chat log), sembra che la chiamata setlocale è là a partire dalla versione 1 per diversi motivi:

  • XIM, almeno in antica volte, non ha "ottenuto" correttamente le impostazioni locali senza una tale chiamata.
  • Su Solaris, si è persino arrestato in modo anomalo con le impostazioni internazionali C predefinite.
  • Sui sistemi Unix, è usato (tra gli altri sistemi, in un complesso gioco di fallback) per "annusare" il "set di caratteri di sistema" (qualunque cosa significhi su Unix), e quindi essere in grado di convertire tra la rappresentazione QString e la codifica "locale" a 8 bit (questo è particolarmente critico per i percorsi dei file).

E 'vero che controlla già le variabili d'ambiente LC_*, come fa con QLocale, ma suppongo che possa essere utile avere nl_langinfo decodificare la corrente LC_CTYPE se l'applicazione in modo esplicito cambiato (ma per vedere se ci è un cambiamento esplicito, deve iniziare con i valori di default del sistema).

E 'interessante il fatto che hanno fatto un setlocale(LC_NUMERIC, "C") subito dopo il setlocale(LC_ALL, ""), ma this was removed in Qt 4.4. Il fondamento logico di questa decisione sembra risiedere nell'attività # 132859 del vecchio bugtracker Qt (che si è spostato tra TrollTech, Nokia e QtSoftware.com prima di scomparire senza lasciare traccia, nemmeno nello Wayback Machine), ed è referenziato in twobugs riguardo questo argomento. Penso che ci sia stata una risposta autorevole sull'argomento, ma non riesco a trovare un modo per recuperarla.

La mia ipotesi è che ha introdotto i bug sottili, in quanto l'ambiente sembrava incontaminata, ma in realtà era toccato dalla setlocale chiamata in tutti, ma la categoria LC_NUMERIC (che è il più evidente); probabilmente hanno rimosso la chiamata per rendere l'impostazione locale più evidente e gli sviluppatori di applicazioni agire di conseguenza.

+1

Buon riassunto Matteo. La mia convinzione personale è che un'applicazione unix ben educata dovrebbe chiamare 'setlocale (LC_ALL," ")' nella fase di inizializzazione dell'applicazione (probabilmente vicino all'inizio di 'main()'). All'interno di una libreria caricata dinamicamente come Qt non è comunque un buon posto, per ragioni di sorpresa del programmatore se non altro. La storia che abbiamo scoperto suggerisce che gli sviluppatori di Qt avevano buone ragioni per includerlo in origine, e gli effetti della rimozione del codice potrebbero rendere riluttanti gli sviluppatori di Qt a rimuoverlo. –

+0

Le impostazioni locali "C" impostate come predefinite su Apple (come risultato di setlocale con stringa vuota) causano l'arresto anomalo delle applicazioni con errori di stringa di caratteri non validi. È anche sconsigliato provare e utilizzare le funzioni di char di POSIX nel programma Qt, mentre il framework offre un'interfaccia portatile per la stessa funzionalità – Swift

2

Cosa c'è di così particolare sui sistemi POSIX (che include il Sistemi Unix/Linux menzionati) è che l'interfaccia del SO e l'interfaccia C sono confusi. La chiamata C setlocale interferisce in particolare con il sistema operativo.

Su Windows, in confronto, la locale è esplicitamente una proprietà per-thread (SetThreadLocale), ma ancora più importante, funzioni come GetNumberFormat accettano un parametro locale.

Nota che il tuo problema è abbastanza facile da risolvere: quando usi Qt, usa Qt. Quindi questo significa reading your text input into a QString, elaborarlo e quindi scriverlo di nuovo.

+0

Come "setlocale" cambia qualcosa di più di Windows? Entrambe influenzano solo le funzioni della libreria standard C (il kernel non sa nulla delle impostazioni locali), che AFAICT sembra essere bypassato da Qt in ogni caso (a fini di localizzazione sembra avere il proprio QLocale, non correlato alle strutture C/C++ rotte). Inoltre, sfortunatamente il problema non è così facile da risolvere: abbiamo diverse librerie che devono essere utilizzate da C++ "normale", Qt-C++, Python "normale" (tramite SIP) e PyQ + PyQt, usando sempre Qt internamente non è né un'opzione, né è effettivamente necessaria. –

+0

POSIX ha le impostazioni locali della libreria standard C e si basa su questo. Oltre a questo, il kernel di Linux non ha una locale. D'altra parte, Windows ha il supporto delle impostazioni locali native, anche per le lingue non C. Quindi non è che "setlocale" cambi di più su Linux, è che ci sono cose che non possono cambiare su Windows. – MSalters

+0

Ma queste cose non sembrano interesse di Qt, nell'intero albero dei sorgenti di Qt ci sono solo due chiamate a "SetThreadLocale" e una a "SetLocaleInfo", e sono tutte in unit test. Inoltre, se Qt avesse bisogno di un "setup generale delle impostazioni internazionali" nella creazione di 'QApplication', sarebbe ragionevole trovarlo tutto nello stesso posto, ma succede solo per i SO basati su Unix. Ecco perché sono perplesso. –

3

Qt chiama setlocale(LC_ALL, ""), perché è la cosa giusta da fare: ogni programma Unix standard da cat su chiamate setlocale(LC_ALL, ""). La conseguenza di tale chiamata è che il locale del programma è impostato su quello specificato dall'utente. Vedere la manpage setlocale():

All'avvio del programma principale, la locale "C" portatile è selezionata come come impostazione predefinita. Un programma può essere portabile su tutte le versioni locali chiamando:

setlocale(LC_ALL, "");

dopo l'inizializzazione del programma ...

Dato che Qt sia genera testo per essere letto dall'utente e analizza ingresso generati dall'utente, sarebbe molto ostile di rifiutare di consentire all'utente di comunicare con l'utente in modo loro specifici della locale. Da qui la chiamata a setlocale().

Mi auguro che essere facile da usare sarebbe senza controversie! Il problema, naturalmente, viene quando si tenta di analizzare i file di dati creati dal programma in esecuzione in una diversa localizzazione. Chiaramente, se si utilizza un formato basato su testo ad-hoc con un parser basato su sscanf e amici, piuttosto che un formato dati specificato con un parser "reale", allora questa è una ricetta per il danneggiamento dei dati se effettuata senza considerare il impostazioni locali. La soluzione è a) utilizzare una libreria di serializzazione reale che gestisce questa roba per te o b) impostare le impostazioni locali su qualcosa di specifico ("C" forse) durante la scrittura e la lettura dei dati.

Se la sicurezza del thread è un problema, sulle moderne implementazioni POSIX (o su qualsiasi sistema Linux con GNU libc version> = 2.3, che è praticamente "tutto di loro" in questo momento) è possibile chiamare uselocale() per impostare un locale locale thread per tutto l'I/O. In alternativa è possibile chiamare le versioni _l delle normali funzioni che accettano un oggetto locale come argomento supplementare.

Si interromperà tutto se chiami setlocale(LC_ALL, "C");? No, ma la cosa giusta è lasciare che l'utente imposti le impostazioni internazionali che preferiscono e salvare i dati in un formato ben specificato o specificare le impostazioni locali in cui i dati devono essere letti e scritti in fase di runtime.

+0

Comunque, i meriti di C locales sono per lo più irrilevanti; Qt ha le sue (migliori) strutture per gestire la localizzazione (vedi 'QLocale' e il framework delle traduzioni), che non sembrano usare le localizzazioni C in alcun modo. Inoltre, l'argomento sull'imporre "la cosa giusta" alle funzioni C non regge l'acqua, poiché su Windows la chiamata "setlocale' viene evitata del tutto. Probabilmente è necessario che ciò richieda uno strano effetto collaterale che è necessario solo su POSIX, ma non riesco a individuare esattamente di cosa si tratta. –

+0

Questo sarebbe 'strtod_l()'. Oppure chiama 'uselocale()'. –

+0

Nessuno di questi è disponibile sulla macchina Linux dalla quale sto scrivendo, nessuno di questi è portatile C; l'interprete di Ruby ha persino aggiunto la sua versione - leggermente spezzata - di 'strtod' perché non esiste un'alternativa sicura portatile. Anche se dovessi usare una funzione non standard nel codice * my *, non posso certamente aggiustare alcuna libreria di terze parti che potrebbe usare un 'strtod'. Seriamente, l'unico modo sicuro per andare in C è quello di attenersi al locale C. Ma ancora una volta, stiamo divagando, il punto è "perché Qt fa questa chiamata che potenzialmente può distruggere molte cose, e perché solo su POSIX"? –

Problemi correlati