Wie vermeide ich Redundanzen bei Funktionen für LPCSTR, LPCTSTR und LPCWSTR?



  • Was ist eigentlich die schnellste Möglichkeit, so einen String in einen Unicode-String umzuwandeln? Ich würde es so machen:

    void MeineFunktionA (LPCSTR text)
    {
        const UINT LAENGE=strlen (text)+1;
        unsigned short *unicodeText=new unsigned short [LAENGE];
    
        for (UINT i=0; i<LAENGE-1; i++)
            unicodeText [i]=text [i];
    
        unicodeText [LAENGE-1]='\0';
    
        MeineFunktionW (unicodeText);
    
        delete [] unicodeText;
    }
    

    Gibt es noch etwas Schnelleres oder vielleicht sogar eine vorgefertigte Funktion ConvertAToW?



  • Intern ist es so gemacht:
    Es gibt eine A- und eine W-Version.
    Die T-Version gibt es nur als Define. Wir nehmen als Beispiel "FatalAppExit":

    Es gibt im PSDK nur

    VOID WINAPI FatalAppExitA(IN UINT uAction, IN LPCSTR lpMessageText);
    

    und

    VOID WINAPI FatalAppExitW(IN UINT uAction, IN LPCWSTR lpMessageText);
    

    Wenn Du in den Header schaust (winbase.h) siehst Du nach der Deklaration noch folgendes:

    #ifdef UNICODE
    #define FatalAppExit  FatalAppExitW
    #else
    #define FatalAppExit  FatalAppExitA
    #endif // !UNICODE
    

    Dies ist dann sozusagen die T-Version.

    Die native OS-Schnittstelle kennt intern nur noch Unicode-Strings (also W). Der Aufruf von "FatalAppExitA" wird somit auf "FatalAppExitW" umgeleitet mit einer passenden Konvertierung des Strings von/nach ANSI (also der aktuell eingestellten Codepage).
    Die Umwandling von ANSI nach Unicode erfolgt hier mittels RtlInitAnsiString und RtlAnsiStringToUnicodeString.



  • Für deine eigenen Anwendungen benötigst du eigentlich nur die "normalen" Versionen und TCHAR/LPCTSTR - und rufst dann auch jeweils die allgemeine Form der WinAPI-Funktionen auf.

    Naja, normalerweise würde ich das auch so machen, aber es kann ja sein, daß jemand auch die anderen Funktionen haben will. (Wenn er Unicode benutzen oder nicht benutzen will, unabhänngig davon, ob das UNICODE-Makro gesetzt ist.) Ich meine, wenn man nur die TCHAR-Variante bräuchte, wieso gibt es dann jeweils drei Versionen in der Windows.h? Dann hätten die doch auch bloß eine schreiben brauchen.



  • NES-Spieler schrieb:

    Was ist eigentlich die schnellste Möglichkeit, so einen String in einen Unicode-String umzuwandeln? Ich würde es so machen:

    Das was Du gemacht hast ist doch keine Umwandlung!!! Was wandelst Du denn da um???
    Ich glaube Du solltest doch mal meinen Artikel über Unicode lesen 😉http://www.c-plusplus.net/forum/viewtopic-var-t-is-161855.html

    Wenn Du MFC/ATL hast, würde ich CA2CW verwenden.
    SOnst ist die schnelleste Methode die Verwendung von
    "MultiByteToWideChar" (das verwenden intern zumindest alle mir bekannten Funktionen).



  • NES-Spieler schrieb:

    Ich meine, wenn man nur die TCHAR-Variante bräuchte, wieso gibt es dann jeweils drei Versionen in der Windows.h? Dann hätten die doch auch bloß eine schreiben brauchen.

    Die brauchst Du *nur*, wenn Du selber eine API (DLL) zur Verfügung stellst, welche sowohl ANSI als auch UNICODE unterstützt!!!
    Wenn Du eine LIB verwendest, dann kannst Du das aber auch über den Namen der LIB machen; z.B. so:

    #ifdef _UNICODE
        #ifdef _DEBUG
          #pragma comment (lib, "BasicSystemud.lib")
        #else
          #pragma comment (lib, "BasicSystemu.lib")
        #endif
      #else
        // ANSI
        #ifdef _DEBUG
          #pragma comment (lib, "BasicSystemd.lib")
        #else
          #pragma comment (lib, "BasicSystem.lib")
        #endif
      #endif
    


  • O.k., vielen Dank für Eure Antworten.

    Die brauchst Du *nur*, wenn Du selber eine API (DLL) zur Verfügung stellst, welche sowohl ANSI als auch UNICODE unterstützt!!!

    Ja, darum ging es mir ja auch: Ich habe eine Klasse, die bestimmte allgemeine Aufgaben erledigt, und die müßte ja jetzt eigentlich bei jeder Funktion mit einem String darinnen drei Versionen haben.



  • Hast Du zu der Klasse eine LIB/DLL, oder wird der Source direkt verwendet?



  • Wenn ich sie selbst verwende, benutze ich natürlich die Header- und CPP-Datei direkt. Wenn ich das Ding mal online stelle (es fehlen noch die Kommentare im Code), dann muß ich mir überlegen, ob ich den Quellcode freigebe oder eine Lib daraus mache.
    Würde es einen Unterschied machen, ob man nun die CPP- oder die Lib-Datei benutzt? (Diese Variante, daß ich je nach Option eine andere Lib-Datei einbinde, würde ich nämlich nicht bevorzugen.)

    Noch eine Frage: Du hast einerseits

    Die Umwandling von ANSI nach Unicode erfolgt hier mittels RtlInitAnsiString und RtlAnsiStringToUnicodeString.

    und andererseits

    SOnst ist die schnelleste Methode die Verwendung von "MultiByteToWideChar" (das verwenden intern zumindest alle mir bekannten Funktionen).

    geschrieben. Welche Version ist denn jetzt die, die die WinAPI selbst nutzt?



  • Wenn Du die cpp direkt verwendest, dann brauchst Du *nur* die TCHAR Version!
    Auch wenn Du eine LIB zur Verfügung stellst (jeweils eine für Ansi und eine für Unicode) brauchst Du *nur* die TCHAR Version! Auch wenn Du eine DLL für Ansi und eine für Unicode zur Verfügung stellst brauchst Du *nur* die TCHAR Version!
    Fazit: Mach nur TCHAR und entscheide später was Du weitergeben willst.



  • Nur TCHAR machen würde ja an sich gehen, wenn die generelle Unicode-Fähigkeit des Programms allein durch das Makro festgelegt werden würde. Aber man kann ja Unicode auch benutzen, wenn das Makro ausgeschaltet ist, indem man direkt den Wide String verwendet. Was ist nun, wenn jemand irgendwelche Spielchen wie folgendes machen will:

    //#define UNICODE
    
    #include <string>
    #include <windows.h>
    
    using namespace std;
    
    typedef basic_string<TCHAR, char_traits<TCHAR>, allocator<TCHAR> > tstring;
    
    int WINAPI WinMain (HINSTANCE instance, HINSTANCE, LPSTR, int)
    {
    	string textA="Ohne Unicode";
    	wstring textW=L"Mit Unicode";
    	tstring text=TEXT ("Je nachdem");
    
    	textW [0]=0x5DE;
    
    #ifdef UNICODE
    	text [0]=0x5D9;
    #endif
    
    	MessageBoxA (NULL, textA.c_str (), "MB", MB_OK);
    	MessageBoxW (NULL, textW.c_str (), L"MB", MB_OK);
    	MessageBox (NULL, text.c_str (), TEXT ("MB"), MB_OK);
    }
    

    Somit muß ich immer alle drei Varianten einbauen.

    Ich hab übrigens meinen Eintrag von oben nochmal editiert. Da steht noch die Frage:

    Du hast einerseits

    Die Umwandling von ANSI nach Unicode erfolgt hier mittels RtlInitAnsiString und RtlAnsiStringToUnicodeString.

    und andererseits

    SOnst ist die schnelleste Methode die Verwendung von "MultiByteToWideChar" (das verwenden intern zumindest alle mir bekannten Funktionen).

    geschrieben. Welche Version ist denn jetzt die, die die WinAPI selbst nutzt?



  • Was ist nun, wenn jemand irgendwelche Spielchen wie folgendes machen will:

    Was für ein Kuddelmuddel.

    Wenn Du meinst, der Benutzer benötigt wirklich beide Varianten: bau Dir eine einfache Stringklasse als Übergabeparameter für Deine Funktionen, die jede der beiden Textvarianten aufnehmen und (auch die jeweils andere nach interner Konvertierung) ausgeben kann.



  • Danke, aber ich habe mich jetzt für folgendes entschieden: Ich schreibe zwei Funktionen, eine für Strings, eine für WStrings. Das Makro

    #ifdef UNICODE
    #define MeineFunktion MeineFunktionW
    #else
    #define MeineFunktion MeineFunktionA
    #endif
    

    laß ich weg. Dafür haben beide Funktionen den gleichen Namen, ein Vorteil, den es in C ja nicht gibt, in C++ aber schon. Und das ganze sieht dann so aus:

    void MeineFunktion (LPCSTR text);
    void MeineFunktion (LPCWSTR text);
    

    Jetzt kann der Benutzer den Datentyp nehmen, den er will (LPCSTR, LPCTSTR oder LPCWSTR) und braucht nur einen Funktionsnamen aufzurufen.

    Und wie das Umwandeln geht, weiß ich jetzt auch, so daß die normale Funktion dann den String umwandelt und einfach nur die Unicode-Funktion aufruft.



  • Ist aber nicht unbedingt sinnvoll, da Du dann beispielsweise auch UNICODE-Funktionscode dabei hast, obwohl Du auf ANSI-Zeichensatz compilierst, aber hängt natürlich vom Kontext ab 😉 .



  • Genau so ist es ja auch gedacht: Wenn der Benutzer das nehmen will, was durch das Definieren oder nicht Definieren des UNICODE-Makros angedacht ist, dann nimmt er den Datentyp LPCTSTR und der Compiler sucht die richtige Funktion aus. Wenn er etwas unabhängig von diesem Makro machen will, nimmt er entsprechend LPCSTR oder LPCWSTR.

    Es geht mir einfach nur darum, daß in meinen Funktionen genau das möglich ist, was auch mit den Original-Windows-Funktionen möglich ist: Man kann zwar dieses Makro festlegen, um generell zu sagen, ob man nun standardmäßig mit Unicode arbeiten will oder nicht, aber trotzdem kann man das Makro auch an gewissen Stellen ignorieren, wie in der WinAPI, wenn man direkt mit den A- oder W-Funktionen arbeitet.
    Ich meine, hätte man gewollt, daß es immer vom Unicode-Makro abhängt und man dieses nicht übergehen kann, hätte man die A- und W-Funktionen ja ausschließlich in den ursprünglichen C-Dateien definieren können und das, was jetzt das Makro ist, wäre dann eine richtige Funktion, etwa so:

    Header-Datei:

    int MessageBox (HWND hWnd, LPCTSTR lpszText, LPCTSTR lpszCaption, UINT nType);
    

    Quellcode-Datei:

    int MessageBoxA (HWND hWnd, LPCSTR lpszText, LPCSTR lpszCaption, UINT nType);
    int MessageBoxW (HWND hWnd, LPCWSTR lpszText, LPCWSTR lpszCaption, UINT nType);
    
    int MessageBox (HWND hWnd, LPCTSTR lpszText, LPCTSTR lpszCaption, UINT nType)
    {
        return
    #ifdef UNICODE
        MessageBoxW
    #else
        MessageBoxA
    #endif
        (hWnd, lpszText, lpszCaption, nType);
    }
    
    int MessageBoxA (HWND hWnd, LPCSTR lpszText, LPCSTR lpszCaption, UINT nType)
    {
        //Blablabla
    }
    
    int MessageBoxW (HWND hWnd, LPCWSTR lpszText, LPCWSTR lpszCaption, UINT nType)
    {
        //Blablabla
    }
    

    Aber nein, der Benutzer hat vollen Zugriff auf alle drei Versionen und kann sowohl abhängig, als auch unabhängig vom Unicode-Makro arbeiten. Das gleiche soll bei mir auch möglich sein.



  • Ich meine, hätte man gewollt, daß es immer vom Unicode-Makro abhängt und man dieses nicht übergehen kann, hätte man die A- und W-Funktionen ja ausschließlich in den ursprünglichen C-Dateien definieren können und das, was jetzt das Makro ist, wäre dann eine richtige Funktion

    Nein, denn bei Funktionen, die sich nur im Rückgabetyp unterscheiden, funktioniert das Überladen nicht.



  • Ich wiederhole mich einfach mal, dann weißt Du, das Dein Post eigentlich überflüssig war 😃 , aber ist nicht böse gemeint (hoffe das kommt nicht so an 😉 ).

    CodeFinder schrieb:

    [...] aber hängt natürlich vom Kontext ab 😉 .

    mgs_ schrieb:

    Ich meine, hätte man gewollt, daß es immer vom Unicode-Makro abhängt und man dieses nicht übergehen kann, hätte man die A- und W-Funktionen ja ausschließlich in den ursprünglichen C-Dateien definieren können und das, was jetzt das Makro ist, wäre dann eine richtige Funktion

    Nein, denn bei Funktionen, die sich nur im Rückgabetyp unterscheiden, funktioniert das Überladen nicht.

    Das stimmt zwar, aber Seine Parameter sind ja verschieden (LPCSTR != LPCWSTR).



  • @Codefinder

    Zunächst mal kann in c sowieso nicht überladen werden, und die WinApi-Schnittstelle ist in c realisiert.

    Aber selbst in c++ funktioniert Polymorphismus nur für Aufrufparameter, nicht für Rückgabeparameter. Wenn zwei gleichnamige Api-Funktionen sich nur im Rückgabeparameter unterscheiden (TCHAR, d. h. char und wchar), ist Überladung nicht möglich.



  • Zunächst mal kann in c sowieso nicht überladen werden, und die WinApi-Schnittstelle ist in c realisiert.

    Deshalb habe ich ja auch von meinen eigenen Funktionen gesprochen, die definitiv in C++ geschrieben werden.

    Aber selbst in c++ funktioniert Polymorphismus nur für Aufrufparameter, nicht für Rückgabeparameter. Wenn zwei gleichnamige Api-Funktionen sich nur im Rückgabeparameter unterscheiden (TCHAR, d. h. char und wchar), ist Überladung nicht möglich.

    So ist es ja bei mir auch nicht:

    void MeineFunktion (LPCSTR text);
    void MeineFunktion (LPCWSTR text);
    

    Verschiedene Aufrufparameter. (Und der Rückgabewert ist hier nichtmal anders.)



  • Wozu braucht man noch was anderes als Unicode ?



  • Damit ich einem Benutzer von Windows 95/98 nicht sagen muß: "Bitte update Dein Betriebssystem auf Windows 2000 oder höher, um mein 50 KB großes Programm zu starten." Ach ja, und damit ich selbst meine Anwendungen benutzen kann (Windows 98). 😃


Anmelden zum Antworten