Originariamente inviato da giuseppe500
Per es: cosa fanno L"" _T() FunctionA e FunctionW ecc....?
Qui però stai parlando fondnaamentalmente di stringhe C...
L di fronte ad una stringa (o un carattere) indica al compilatore che si tratta di una stringa (o di un carattere) Unicode, e non ANSI; _T() è una macro che se è definito _UNICODE è definita come
codice:
#define _T(x) L ## x
(o qualcosa del genere), il che in sostanza fa sì che la stringa inserita tra le due parentesi sia trattata come una stringa Unicode, mentre se _UNICODE non è definito è definita come
, per cui lascia la stringa ANSI. L'utilità di questa macro è di consentire di compilare il programma indifferentemente in ANSI o in Unicode semplicemente definendo o non definendo _UNICODE. Naturalmente per poter programmare con "stringhe generiche" bisogna avere anche a disposizione delle funzioni che funzionino con caratteri di tipo generico (ANSI o Unicode, a seconda della definizione della suddetta macro), anch'esse definite in TCHAR.H.
Senza i generic text mappings per poter compilare indifferentemente in Unicode e in ANSI dovresti scrivere le due versioni per ogni blocco di codice che usa le stringhe, in questa maniera:
codice:
#ifdef _UNICODE
wchar_t prova[256];
wcscpy(prova, L"Ciao ajlasloajf");
#else
char prova[256];
strcpy(prova, "Ciao ajlasloajf");
#endif
con i generic text mappings, invece, puoi fare semplicemente
codice:
TCHAR prova[256];
_tcscpy(prova _T("Ciao ajlasloajf"));
.
Dovendo essere compatibile con il C tutto questo accrocchio funziona tramite macro: _tcscpy infatti è definita come strcpy quando si compila in ANSI, wcscpy quando si compila in Unicode.
Trovandomi a dover interagire in un ambiente TCHAR con delle funzioni che accettano soltanto char * o wchar_t * ho scritto qualche macro che può risultare utile in queste situazioni:
codice:
//Mappings for conversions from and to TCHAR *
#ifdef _UNICODE
//TCHAR * -> char * => wchar_t * -> char *
#define _tcstombs wcstombs
//TCHAR * -> wchar_t * => wchar_t * -> wchar_t *
#define _tcstowcs wcsncpy
//char * -> TCHAR * => char * -> wchar_t *
#define _mbstotcs mbstowcs
//wchar_t * -> TCHAR * => wchar_t * -> wchar_t *
#define _wcstotcs wcsncpy
#else
//TCHAR * -> char * => char * -> char *
#define _tcstombs strncpy
//TCHAR * -> wchar_t * => char * -> wchar_t *
#define _tcstowcs mbstowcs
//char * -> TCHAR * => char * -> char *
#define _mbstotcs strncpy
//wchar_t * -> TCHAR * => wchar_t * -> char *
#define _wcstotcs wcstombs
#endif
In C++ si possono utilizzare i TCHAR in maniera piuttosto semplice, dal momento che tutte le classi che usano stringhe e caratteri sono template; nello stesso progetto ho definito qualche typedef per questo fine:
codice:
//Mappings for the STL string and string-related classes
namespace std
{
//String
typedef basic_string<TCHAR> _tcstring;
//String streams
typedef basic_stringstream<TCHAR> _tcstringstream;
typedef basic_ostringstream<TCHAR> _tcostringstream;
typedef basic_istringstream<TCHAR> _tcistringstream;
//String buffer
typedef basic_stringbuf<TCHAR> _tcstringbuf;
//File streams
typedef basic_fstream<TCHAR> _tcfstream;
typedef basic_ifstream<TCHAR> _tcifstream;
typedef basic_ofstream<TCHAR> _tcofstream;
};
Ho poi visto che non è un'idea così originale, e infatti in SmartWin++ hanno fatto la stessa cosa, definendo anche delle funzioni template per convertire le stringhe da un tipo all'altro.
Nella programmazione Windows succede qualcosa di analogo: praticamente di ogni funzione che utilizza le stringhe esistono due versioni, una ANSI e una Unicode, le quali rispettivamente terminano con una A e una W. La versione senza lettera finale in realtà non esiste come funzione, è semplicemente una macro del preprocessore (analoga a quelle di TCHAR.H) che a seconda della macro _UNICODE è definita come la versione A o la versione W. Faccio un esempio: se guardi nelle dll di Windows di fatto non esiste nessuna funzione GetWindowText, ma esistono le funzioni GetWindowTextA e GetWindowTextW. Quando tu nel tuo programma scrivi GetWindowText, il preprocessore sostituisce GetWindowText con GetWindowTextA o GetWindowTextW a seconda di come è definita la macro _UNICODE. Lo stesso ragionamento vale per tutte le strutture che contengono stringhe o puntatori a stringhe.
La documentazione delle singole funzioni (e delle strutture) di solito non menziona le due versioni, ma piuttosto ne specifica solo una fingendo che accetti dei TCHAR (cosa che naturalmente è impossibile, visto che un TCHAR si risolve in un char o in un wchar_t al momento della compilazione, ma le funzioni di Windows sono già compilate; naturalmente l'overloading non è un'opzione contemplata, visto che le API devono essere compatibili con il C).
Alcune funzioni di Windows più recenti esistono solo in versione W, e in tal caso di solito non ne esiste una versione senza suffisso finale.
Sui sistemi di famiglia NT conviene assolutamente compilare in Unicode, sia perché in questa maniera si evitano molti problemi con caratteri stranieri, sia perché le funzioni A non sono altro che wrapper attorno alle funzioni W, che convertono le stringhe in input in Unicode, le passano alla funzione W e quando questa ritorna riconvertono le stringhe di output in ANSI; questo naturalmente implica una maggiore lentezza delle funzioni A. Perché allora non si compila tutto in Unicode? Fondamentalmente per via della compatibilità con vecchio codice e vecchi sistemi operativi; riscrivere tutto il codice che usava char con i wchar_t è una gran menata, specie se si fa ampio uso di sizeof per ottenere il numero di caratteri dei buffer; lasciando il sizeof così com'è e cambiando il tipo a wchar_t infatti si aprono spesso problemi di buffer overflow. Considera ad esempio
codice:
char test[256];
/* ... */
strncpy(test, altrastringa, sizeof(test)-1);
test[sizeof(test)-1]=0;
; convertendolo in Unicode in questa maniera
codice:
wchar_t test[256];
/* ... */
wcsncpy(test, altrastringa, sizeof(test)-1);
test[sizeof(test)-1]=0;
si commette un grave errore: infatti sizeof restituisce le dimensioni di una variabile in numero di char, mentre le funzioni stringa si aspettano il numero massimo di caratteri da copiare; in questo caso (considerando un wchar_t di 2 byte) quindi si va a passare a wcsncpy 511 invece di 255 come numero massimo di caratteri da copiare. Anche peggio, la linea successiva (che servirebbe a terminare con sicurezza il buffer con un NUL) va a scrivere su memoria non allocata.
La soluzione consiste nell'utilizzare sempre i generic text mappings, o almeno nell'evitare l'uso di sizeof per determinare il numero di elementi di un array, ma di usare sempre la macro ARRSIZE
codice:
#define ARRSIZE(arr) (sizeof(arr)/sizeof(*(arr)))
.
Dicevo della compatibilità con sistemi operativi vecchi: il problema fondamentale qui è la famiglia 9x di Windows, che implementa un numero ridotto di API Unicode, a meno che non si installi MSLU, e che comunque internamente funziona in ANSI. Se non utilizzano MSLU (e se questo a sua volta non è installato) è facile che eseguibili compilati in Unicode vadano in crash all'avvio per la mancanza di qualche API Unicode su Windows 9x.
La documentazione di Windows per specificare i puntatori a stringa utilizza una notazione particolare (e odiosa) che è bene imparare; ogni tipo di stringa si definisce in questa maniera:
LP = puntatore
[C] = opzionale, se presente indica che la stringa è costante (const)
[W|T] = opzionale, se assente indica che la stringa è ANSI (char *), se è W indica che la stringa è Unicode (wchar_t *), se è T indica che la stringa è generica (TCHAR *)
STR = indica che è una stringa
Di conseguenza di fatto abbiamo i seguenti tipi:
codice:
LPSTR = char *
LPCSTR = const char *
LPWSTR = wchar_t *
LPCWSTR = const wchar_t *
LPTSTR = TCHAR *
LPCTSTR = const TCHAR *
.
Per finire, ti lascio con un articolo che spiega più ampiamente quello che ho scritto qui (link).