Kleiner WinNT Console / STL Workaround für Umlaute



  • Hallo !

    In dem parallelen Thread "Format Message" http://www.c-plusplus.net/forum/viewtopic-var-t-is-180051-and-start-is-0-and-postdays-is-0-and-postorder-is-asc-and-highlight-is-.html von Meep Meep hat sich ein allgemeineres Problem ergeben; nämlich die korrekte Wiedergabe von deutschen ( und anderen ) Umlauten auf den NT Console.

    Näheres bitte ich dem bezeichneten Thread zu entnehemen

    Hab mal versucht einen kleinen Workaround zu formulieren, es wäre schön wenn jemand der über imtimere Kenntnisse der STL Streams verfügt als ich sich das mal durchsieht.

    Happy Hacking!

    *this

    //...
    #include <iostream>
    //...
    #if defined(_WINDOWS) 
    
    /**
     * @brief  Little STL Workaround for mismangled German chars on WinNT console
     *
     * @author Gast++ 
     * @state DEVELOPMENT - as least as buggy as Perl 0 so "don't ask" ;-)
     * @ref http://www.c-plusplus.net/forum/viewtopic-var-t-is-180051.html
     * @version 0.1
     * @licence Copyleft -  but use at your own risk !
     *
     *      BECAUSE THIS CODE IS LICENSED FREE OF CHARGE, 
     *      THERE IS NO WARRANTY FOR IT, TO THE EXTENT 
     *      PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE 
     *      STATED IN WRITING THE AUTHOR AND/OR OTHER 
     *      PARTIES PROVIDE THE CODE "AS IS" WITHOUT WARRANTY OF 
     *      ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, 
     *      BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 
     *      MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. 
     *      THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF 
     *      THE CODE IS WITH YOU. SHOULD THE CODE PROVE DEFECTIVE, 
     *      YOU ASSUME THE COST OF ALL NECESSARY SERVICING, 
     *      REPAIR OR CORRECTION.
     */
    
    /*
     * for #pragma message
     */
    #define PRAGMASTR2(x) #x
    #define PRAGMASTR(x) PRAGMASTR2(x) 
    
    /*
     * Well, I know this is ugly
     * But fits easily with my current project
     * #undefs are below of course
     *
     * BEWARE OF NAME CONFLITCS WITH YOUR PROJECT !!!
     *
     * Make it better and repost ! ;-)
     */
    #pragma message("STL / NT Console Workaround:")
    #pragma message(__FILE__ "[" PRAGMASTR(__LINE__) "]" ": Once again : Beware of Name Conflicts!")
    
    #if defined(_UNICODE)
    
    #define OSTREAM wostream
    #define COUT wcout
    #define CERR wcerr
    
    #else // _MBCS, _SBCS
    
    #define OSTREAM ostream
    // EDIT
    #define CERR cerr 
    // /EDIT
    #define COUT cout
    
    #endif // define _UNICODE
    
    /*
     * 
     */
    
    std::OSTREAM& operator<<(std::OSTREAM& left, TCHAR* szRight) {
    
        /*
         * Only neccessary for NT console output streams
         */
        if ((left == std::COUT) || (left == std::CERR)) {
    
            DWORD dwErr = ERROR_SUCCESS;    
            DWORD dwErrPop;                 // For conservating error state
    
            dwErrPop = ::GetLastError();    // push in case  we're IN an Error Handling
    
            ::SetLastError(ERROR_SUCCESS);  // For the Fallback Handler below
    
    #if defined(_UNICODE)       
    
            /*
             * Either declaring here or handling 
             * above with UNREFERENCED_PARAMETER() 
             *
             * Anyway...
             */
            HANDLE hConsole = INVALID_HANDLE_VALUE; 
            if ((hConsole = ::GetStdHandle(STD_OUTPUT_HANDLE)) != INVALID_HANDLE_VALUE){ 
    
                // dto.
                DWORD dwMode = 0;
                DWORD dwWritten = 0;
    
    #pragma message("STL / NT Console Workaround:")
    #pragma message(__FILE__ "[" PRAGMASTR(__LINE__) "]" ": guess there's more to be done to surrogate cout behavior, isn't it ?")
                if(::GetConsoleMode(hConsole, &dwMode)) {
                    if(::SetConsoleMode(hConsole, dwMode | ENABLE_PROCESSED_OUTPUT)) {
                        ::WriteConsole(hConsole,(const LPVOID) szRight,
                                               (DWORD)(_tcslen(szRight)),
                                               &dwWritten,
                                               NULL); 
                        ::SetConsoleMode(hConsole, dwMode); // restoring
                    }
                }
            }
    
    #else // _MBCS, _SBCS
    
            // dto. 
            DWORD dwBufSize = (DWORD)_tcslen(szRight); 
            LPSTR lpszDst = NULL; // _NOT_ TCHAR !!
    
            if (NULL != (lpszDst = (LPSTR) ::LocalAlloc(LPTR, (dwBufSize+1)*sizeof(char)))) {
                if (::CharToOemBuff(szRight,lpszDst, dwBufSize)) {
                    std::operator<<(left,lpszDst);
                }
            }
    
            ::LocalFree(lpszDst);   // No need to handle NULL case - it's silent !
    
    #endif // #if defined(UNICODE)
    
    #pragma message("STL / NT Console Workaround:")
    #pragma message(__FILE__ "[" PRAGMASTR(__LINE__) "]" ": Better handle connsole char conversion errors as it fits with _YOUR_ project!")
            /*
             * Fallback Handler for all stuff above
             */
            if ((dwErr = ::GetLastError()) != ERROR_SUCCESS) {
                /*
                 * What else ?
                 * throw // ? What ?
                 * left.bad() // ???
                 */
                LPVOID lpMsgBuf;
    
                if (::FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                                    NULL,
                                    dwErr,
                                    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
                                    (LPTSTR) &lpMsgBuf,
                                    0,
                                    NULL) != 0) {
    
                    _ftprintf(stderr,_T("Conversion of strings for NT console failed\n"));
    
                } else {
    
                    /*
                     * ???
                     * "System dependant STL internals" ?!?!?
                     *
                     *::LocalFree(lpMsgBuf)
                     * throw std::Exception;// ???
                     */
    
                }
                ::LocalFree(lpMsgBuf);  // Harmless, as above
            }
    
            ::SetLastError(dwErrPop);   //restoring
    
        } else {    // Something else on "left" than (w)cout or (w)cin
    
            std::operator<<(left,szRight); // using default behaviour 
    
        }
    
        return left;
    }
    
    std::OSTREAM& operator<<(std::OSTREAM& left, const TCHAR* szRight) {
        return operator<<(left, (TCHAR*) szRight);
    }
    
    /*
     * cleaning up
     */
    #undef CERR 
    #undef COUT 
    #undef OSTREAM 
    
    #undef PRAGMASTR2
    #undef PRAGMASTR
    
    #endif // #if defined(_WINDOWS)
    


  • Sollte er (der Compiler 😉 ) hier nicht meckern:

    #if defined(_UNICODE)
    
    #define OSTREAM wostream
    #define COUT wcout
    #define CERR wcerr
    
    #else // _MBCS, _SBCS
    
    #define OSTREAM ostream
    // EDIT
    #define CERR cerr
    // /EDIT
    #define COUT cout
    
    #endif // define _UNICODE
    

    weil die Symbole (cerr, cout, ...) im Namensbereich std 'versteckt' sind ?

    EDIT: Das ist mir jetzt als erstes aufgefallen *g*.



  • CodeFinder schrieb:

    weil die Symbole (cerr, cout, ...) im Namensbereich std 'versteckt' sind ?

    Das ist dem Präprozessor doch egal. Und der Compiler sieht an dieser Stelle ja nichts mehr.



  • Hm jupp, stimmt, habs grad nochmal getestet. Trotzdem finde ich es übersichtlicher wenn man das std mit ins Makro nimmt 🙄 (Das nur so am Rande) .



  • sieht für meinen geschmack total übertrieben aus.
    geht das nicht einfacher? 😕



  • pale dog schrieb:

    sieht für meinen geschmack total übertrieben aus.
    geht das nicht einfacher? 😕

    Für meinen Geschmack auch.

    Aber das muss imho leider an einer sehr heiklen Stellen ansetzen; ich seh sonst kine Möglickeit std::(w)err in den Griff zu kriegen aös das zentral zu überdecken,

    Wenn jemand einen schlankeren Entwurd hat: bitte her damit!
    Solch einen Klotz stell ich auch nicht gerne ins Feld.

    Grüsse

    *this



  • CodeFinder schrieb:

    Hm jupp, stimmt, habs grad nochmal getestet. Trotzdem finde ich es übersichtlicher wenn man das std mit ins Makro nimmt 🙄 (Das nur so am Rande) .

    Ich find die Makros auch eigenntümlich und unelegant.

    Ich denk grad an einer Formulierung mit einem Template und ggf. 4 {const/nicht const} X (char/wchat_t) mit expliziten Instantiiierungen davon herum.

    Es muss ja auf jeden Fall erzielt werden dass der selbstdefinierte Operator über deckt _auch_ _wenn_

    namespace std
    

    irgendwie in den Scope importiert wurde.
    Das macht das imho etwas heikel.

    Grüsse

    *this



  • Hab es nicht mit der STL probiert... aber sollte eigentlich auch gehen:
    http://blog.kalmbachnet.de/?postid=98



  • Gast++ schrieb:

    pale dog schrieb:

    sieht für meinen geschmack total übertrieben aus.
    geht das nicht einfacher? 😕

    Für meinen Geschmack auch.

    Aber das muss imho leider an einer sehr heiklen Stellen ansetzen; ich seh sonst kine Möglickeit std::(w)err in den Griff zu kriegen aös das zentral zu überdecken,

    Wenn jemand einen schlankeren Entwurd hat: bitte her damit!
    Solch einen Klotz stell ich auch nicht gerne ins Feld.

    naja, du versuchst das ja an diese STL und den operator<< ranzuschnuddeln, was es wohl so unendlich kompliziert und schwierig macht. wenn man's unbedingt in ein C++-korsett zwängen will, dann vielleicht eine eigene klasse daraus machen.
    falls du's aber unbedingt an die STL ankleben willst, vieleicht hilft dir das: http://angelikalanger.com/Articles/C++Report/UserDefinedFacets/UserDefinedFacets.html



  • Jochen Kalmbach schrieb:

    Hab es nicht mit der STL probiert... aber sollte eigentlich auch gehen:
    http://blog.kalmbachnet.de/?postid=98

    Danke!

    (Es stimmt schon "Es gibt nichts Neues unter der Sonne")

    😉

    Den Code den ich gepostet habe hab ich mit der ältesten IDE geschrieben die ich fand - DEV Studio 97 aka VC 5.0.
    ( Das ist nämlich afaik zugleich die schnellste. )

    😉 😉 😉

    Für VC 2005 kann ich dann ja einen eigenen #ifdef machen.

    Bist Du denn mit dem Aufbau einverstanden oder hab ich was übershen?

    Grüsse

    *this



  • pale dog schrieb:

    naja, du versuchst das ja an diese STL und den operator<< ranzuschnuddeln,

    Damit will ich die Verwendung einfach machen.

    pale dog schrieb:

    was es wohl so unendlich kompliziert und schwierig macht. wenn man's unbedingt in ein C++-korsett zwängen will, dann vielleicht eine eigene klasse daraus machen.

    Dann könnte ich zwar std::cout et al. mit einer Instanz dieser Klasse neu versorgen aber operator<< ist nicht virtuell. 😢

    pale dog schrieb:

    falls du's aber unbedingt an die STL ankleben willst, vieleicht hilft dir das: http://angelikalanger.com/Articles/C++Report/UserDefinedFacets/UserDefinedFacets.html

    Danke!

    Mit einem facet ersetze ich den ich imho unelegaaten Vergleich

    (left == std::CIN)
    

    durch ein

    (if left.has_facet<MyFacet>(...))
    

    Was vorr. in der nächsten Version passieren wird.

    Aber um das facet in Betrieb zu nehmen komm ich afaik dann doch wieder nicht um ein Verdecken des Operators herum.

    Das ist es was mir die meisten Sorgen macht.

    Grüsse

    *this


Anmelden zum Antworten