Haben diese Defines irgendeinen Sinn?



  • Bitte ein Bit schrieb:

    Wie der totale Verzicht auf Makros ausgeht sieht man bei C#: haufenweise wiederholter Code, Code der unnötig laaaaaangsam

    Arg...

    Den Spruch verstehe ich keinen Deut. Wenn ich Code habe, welcher oft wiederholt wird, dann packe ich diesen in eine Funktion.

    Dort wo man anfängt (z.B. in einer Logging Funktion) über die Reflection API den Callstack durchzuackern, damit man den Aufrufer findet, damit man dann rausbekommen kann wie die aufrufende Klasse/Methode heisst, damit man es mit loggen kann. *würg*
    In C# 5.0 wurde dafür sogar ne Spracherweiterung gemacht, so uninteressant kann es also nicht sein.

    Und in C++ machst du ein Makro das einfach __FUNCTION__ etc. mit übergibt und bist fertig.

    Oder implementier' mal INotifyPropertyChanged . Da musst du den Namen von Properties mitgeben. D.h. du darfst überall "MyPropertyName" hinschreiben. Das ist Mist, weil es beim Refactor-Rename nicht mit umbenannt wird. Vor allem weil beim Compilieren keine Fehler oder Warnings kommen - es funktioniert nur nach dem Refactor-Rename einfach nicht mehr korrekt.

    Also greifen viele Leute auf nen Hack zurück, wo sie nen Lambda ala x => x.MyPropertyName an eine Hilfsmethode übergeben die ne Expression<...> nimmt, und sich dann über Reflection den Namen der Property holt.

    Mit Makros würde das Refactor-Rename den Property Namen zwar auch nicht mit ändern, aber man könnte zumindest einfach sicherstellen dass beim Compilieren ein Fehler kommt. Und die ewige Wiederholung von "if property != wert setze property = wert und ruf blöde funktion auf mit name von property" könnte man sich auch sparen. Zeig mir wie du das ohne Code-Duplizierung und ohne langsame und ohne Nutzung von unsicheren (weil nicht spezifizierte) Sprach-Features hinbekommen willst.

    In C# hast du keine Templates UND keine Makros. Das Resultat ist stellenweise schrecklich. (Ich bin ein grosser Fan von C#, aber einige Dinge sind in C++ einfach 100x eleganter zu machen.)

    Und wenn die Funktion klein ist, stelle ich ein inline vor die Funktion s.d. der Compiler diese nach einer Kosten zu Nutzen Analyse ggf. inlined.

    So und wo soll jetzt das Ganze langsam werden?

    Nein. In C++ verwendest du Makros. Weil's nämlich keine Reflection gibt mit der man es machen könnte. Deine inline Funktion kannst du also gar nicht implementieren, weil es einfach nicht geht.

    Ich glaube echt du verstehst überhaupt nicht wovon ich rede. Ich rede von Sachen wo man Makros braucht weil es nicht anders geht *. Und du kommst immer mit "ich mach das mit inline Funktionen". Nein, machst du nicht, weil es nicht geht 😉

    * OK, und von Fällen wo es theoretisch ginge, aber es einfach nicht praktikabel wäre, z.B. wegen Runtime-Overhead oder was auch immer.

    Und wenn du das schon schlimm findest, dann darfst du auch niemals irgendwo Regexen verwenden.

    Welche ?😕

    In regex sind ein paar kleine Makro's. Ok die stören mich nicht.

    Übrigens: Ich schaue mir mal die Tage mal noch die Log von boost an.

    Du hast behauptet die Makros wären "Write Only Code". Die meisten nicht-trivialen Regexen sind mindestens genau so "write only" wie die paar einfachen Makros hier. Mindestens genau so unleserlich und schwierig zu entziffern. Also böse. Also darfst du keine Regexen verwenden 😉



  • Du hast behauptet die Makros wären "Write Only Code".

    Deswegen wollte ich ja meine Meinung zu Makro's etwas koorigieren, als ich (etwas vorschnell) meinte nimm keine Makro's.

    Aber wenn ich in das Bosst Logging Makro anschaue, dann ist das halt nicht für's Debugging entwickelt worden. Und dann kommt das eine auf das andere und der Stil überträgt sich auf den Programmierstil, man verbesserte ein Sub-Makro und peng trit ein Fehler auf. Mir fehlt da einfach der Gedanke das auch mal ein Fehler auftauchen kann,... 😞

    Du bist aber nicht einfach zu verstehen, wenn du die Programmiersprachen wechselst und man zum Teil raten kann, auf was du dich beziehst.

    Ich werde noch die Effiziens des Boost Logging Makro testen. Aber gibt mir Zeit.

    ---

    Ich kenne folgende Einsatzarten von Makro's:
    1.) Definition von Konstanten (extern ist unschön), abkürzende Schreibweisen (falls typedef nicht funktioniert)
    2.) Sicherstellung des Einbindens von Code ins Assembly (für Code Verschleierung)
    3.) Makro als Compile-Zeit Funktion



  • Bitte ein Bit schrieb:

    Ich kenne folgende Einsatzarten von Makro's:
    1.) Definition von Konstanten (extern ist unschön), abkürzende Schreibweisen (falls typedef nicht funktioniert)
    2.) Sicherstellung des Einbindens von Code ins Assembly (für Code Verschleierung)
    3.) Makro als Compile-Zeit Funktion

    1. kann ich nicht nachvollziehen. 2) auch nicht. Und 3 auch nicht.

    Debuggen, natürlich assert. Auch

    #define SHOW(x) cout<<(#x)<<'='<<(x)<<'\n';
    

    Aufbau von Sachen, die ein wenig Reflexionartiges machen

    #define LEARNTYPE(x) registerType(#x,new LoadSaveCreateCopyDelete<T>);
    

    Logging, wo bei ausgeschaltetem Log die zu loggenden Ausdrücke NICHT ausgewertet werden müssen.
    Reparieren fremden Codes, mißratener Bibliotheken und Compiler

    //repariert alte MS-Compiler
    #define for if(false);else for
    
    //viel praktischer
    #define cout cout<<
    
    //ich bin hauptschule
    #define sin(x) sin(x*3.14/180)
    
    //da vertippe ich mich immer
    #define retrun return
    
    //und natürlich dieses Meisterwerk perfiden Humors
    #define ever (;;)
    


  • Codegenerierung.



  • Kellerautomat schrieb:

    Codegenerierung.

    Hab ich in den letzten Jahren immer mit Templates gemacht.

    #include <iostream>
    using namespace std;
    
    template<int n>
    struct TuWas{
        static void tuWas(){
            TuWas<n-1>::tuWas();
            cout<<n<<'*'<<n<<'='<<n*n<<'\n';
        }
    };
    template<>
    struct TuWas<0>{
        static void tuWas(){
        }
    };
    
    int main(){
        TuWas<10>::tuWas();
    }
    


  • #include <iostream>
    // Kompiliert mit gcc 4.8 unter Codeblocks
    #include <array>
    #include <boost/log/trivial.hpp>
    #include <ctime>
    #include <chrono>
    #include <iomanip>
    #include <thread>
    #include <windows.h>
    #include <mutex>
    
    using std::chrono::duration_cast;
    using std::chrono::milliseconds;
    using std::chrono::steady_clock;
    using std::chrono::system_clock;
    
    std::mutex g_pages_mutex;
    
    void WriteToLog(const std::string& Msg)
    {
        g_pages_mutex.lock();
        try
        {
            //*******************************
            std::time_t now_c = system_clock::to_time_t(system_clock::now());
            std::tm* Time = gmtime(&now_c);
            DWORD ThreadID = GetCurrentThreadId();       // weis nicht warum std::this_thread::get_id() immer 1 einzeigt
    
            // Ausgabe boost: [2013-10-14 22:33:58.634517] [0x000015c4] [trace]   Hallo Welt
            std::cerr << "{" << (Time->tm_year + 1900) << "-" << (Time->tm_mon + 1) << "-" << Time->tm_mday << " " <<
                Time->tm_hour << "-" << Time->tm_min << "-" << Time->tm_sec << "} " << ThreadID << " (trace)" << Msg <<
                std::endl;  // Sollte eigentlich "\n" stehen, damit nicht ständig geflusht wird
                //"\n";  
        }
        catch (...)
        {
            std::cerr << "Irgentein Fehler ist aufgetreten!\n";
        }
        g_pages_mutex.unlock();
    }
    
    void Test()
    {
        static const size_t Max = 1000;
        // Variablen
        size_t i;
    
        auto T3 = steady_clock::now();
        for (i = 0; i < Max; i++)
        {
            BOOST_LOG_TRIVIAL(trace) << "Hallo Welt\n";
        }
        auto T4 = steady_clock::now();
    
        auto T1 = steady_clock::now();
        for (i = 0; i < Max; i++)
        {
            WriteToLog("Hallo Welt\n");
        }
        auto T2 = steady_clock::now();
    
        std::cout << "\n\nZeit 1 (std::cerr): " << duration_cast<milliseconds>(T2 - T1).count() << "ms \n";
        std::cout << "\n\nZeit 2 (BOOST_LOG_TRIVIAL): " << duration_cast<milliseconds>(T4 - T3).count() << "ms \n";
    }
    int main()
    {
        Test();
        std::cin.get();
        return 0;
    }
    

    Zurück zu Effiziens des boost Makros:

    Zeit std::err Lösung: 1264ms +- 169,93ms
    Zeit boost Lösung: 3123ms +- 222,55ms

    Ich habe auch versucht das Makro BOOST_LOG_TRIVIAL aufzulösen. Nachdem meine IDE aber nicht alle Makros finden konnte (es kompiliert aber) habe ich es aufgegeben.



  • Bitte ein Bit schrieb:

    ...

    Ich finde, Logging, was sich nicht auf Makro-Ebene ganz wegmachen läßt, brauchste gar nicht zu messen.



  • void WriteToLog(const std::string& Msg)
    {
    #ifdef _DEBUG
        g_pages_mutex.lock();
        //...
        g_pages_mutex.unlock();
    #endif
    }
    

    So besser? :p



  • Bitte ein Bit schrieb:

    void WriteToLog(const std::string& Msg)
    {
    #ifdef _DEBUG
        g_pages_mutex.lock();
        //...
        g_pages_mutex.unlock();
    #endif
    }
    

    So besser? :p

    Nein. Aufrufen tut man es immernoch mit

    WriteLog(whoAmI()+" sends SQL-Command=\"+sql+"\");
    

    und die Funktionsaufrufe und Stingverarbeitung kosten was.

    Also baut man keine ein/aus-schaltbaren Log-Zeilen ein, sondern loggt lieber gar nicht.



  • Ernstgemeint: Ernsthaft?

    Ich dachte bisher der Compiler würde erkennen, dass die aufzurufende Funktion keinen Inhalt hat und sie daher wegoptimieren 😕 😕



  • wooooo schrieb:

    Ernstgemeint: Ernsthaft?

    Ich dachte bisher der Compiler würde erkennen, dass die aufzurufende Funktion keinen Inhalt hat und sie daher wegoptimieren 😕 😕

    In dem Fall natürlich unter der Voraussetzung das whoAmI() "const" ist



  • @Bitte ein Bit
    Miss mal die Performance wenn die Log-Ausgaben zwar mit reincompiliert sind, aber nicht ausgegeben werden (="dynamisch" ausgeschaltet).
    Das ist ein Fall auf den BOOST_LOG speziell hinoptimiert ist.
    Da bleibt nämlich ausser einem "if" nichts mehr übrig -- auch nicht das Auswerten der Expressions die in die Log-Message reingeshiftet werden.

    Wie schnell es dann beim Schreiben ist, ist mir mehr oder weniger egal. (So lange es nicht komplett krass langsam ist.)



  • wooooo schrieb:

    Ernstgemeint: Ernsthaft?

    Ich dachte bisher der Compiler würde erkennen, dass die aufzurufende Funktion keinen Inhalt hat und sie daher wegoptimieren 😕 😕

    Der kann doch nicht wissen, welche Nebeneffekte whoAmI() hat.

    for(int i=0;i<1000000000;++i){//packt er
    }
    
    string fuu;
    for(int i=0;i<1000000000;++i){//weiß nicht, würde mich wundern
       string bar(foo+"test");
    }
    
    for(int i=0;i<1000000000;++i){//klassische optimiererblindmachzeitschleife
       cout<<"";
    }
    


  • hustbaer schrieb:

    Da bleibt nämlich ausser einem "if" nichts mehr übrig -- auch nicht das Auswerten der Expressions die in die Log-Message reingeshiftet werden.

    Wie soll das denn gehen?



  • BOOST_LOG(logger)
            << Expression1
            << Expression2
            ...;
    
    // Wird zu (sinngemäss, stark vereinfacht)
    
        // --BEGIN-- BOOST_LOG(logger)
        if (!IsLoggerEnabled(logger))
            ;
        else
            LogMessage(logger, __FILE__, __LINE__)
        // --END--
            << Expression1
            << Expression2
            ...;
    

    In diesem Beispiel müsste die LogMessage sich selbst im Destruktor selbst loggen. Ich meine sie haben das in Wirklichkeit über nen for -Loop umgesetzt, aber der Teil ist ja diesbezüglich nicht interessant.
    Wichtig ist dass Expression1 , Expression2 etc. nicht ausgewertet werden wenn der Logger nicht aktiv ist.

    (Und man kann Logger auch statisch deaktivieren -- wobei BOOST_LOG dann über Template-Magic dafür sorgt dass IsLoggerEnabled(logger) compile-time-constant wird.)



  • hustbaer schrieb:

    BOOST_LOG(logger)
            << Expression1
            << Expression2
            ...;
    
    // Wird zu (sinngemäss, stark vereinfacht)
    
        // --BEGIN-- BOOST_LOG(logger)
        if (!IsLoggerEnabled(logger))
            ;
        else
            LogMessage(logger, __FILE__, __LINE__)
        // --END--
            << Expression1
            << Expression2
            ...;
    

    In diesem Beispiel müsste die LogMessage sich selbst im Destruktor selbst loggen. Ich meine sie haben das in Wirklichkeit über nen for -Loop umgesetzt, aber der Teil ist ja diesbezüglich nicht interessant.
    Wichtig ist dass Expression1 , Expression2 etc. nicht ausgewertet werden wenn der Logger nicht aktiv ist.

    (Und man kann Logger auch statisch deaktivieren -- wobei BOOST_LOG dann über Template-Magic dafür sorgt dass IsLoggerEnabled(logger) compile-time-constant wird.)

    Was schreibst Du ins Makro FOO, damit bei

    FOO()<<system("cls");
    

    der Systemaufruf nicht passiert?


  • Mod

    volkard schrieb:

    Was schreibst Du ins Makro FOO, damit bei

    FOO()<<system("cls");
    

    der Systemaufruf nicht passiert?

    Könnte so aussehen:
    http://www.c-plusplus.net/forum/p2362104#2362104



  • SeppJ schrieb:

    volkard schrieb:

    Was schreibst Du ins Makro FOO, damit bei

    FOO()<<system("cls");
    

    der Systemaufruf nicht passiert?

    Könnte so aussehen:
    http://www.c-plusplus.net/forum/p2362104#2362104

    Das hat mich jetzt nicht befriedigt.


  • Mod

    volkard schrieb:

    SeppJ schrieb:

    volkard schrieb:

    Was schreibst Du ins Makro FOO, damit bei

    FOO()<<system("cls");
    

    der Systemaufruf nicht passiert?

    Könnte so aussehen:
    http://www.c-plusplus.net/forum/p2362104#2362104

    Das hat mich jetzt nicht befriedigt.

    Wieso? Das erzeugt Code wie den hier:

    FOO()<<system("cls");
    

    ->

    if (true); else Logger << system("cls");
    

    Da wird doch niemals system aufgerufen, da sollte nicht einmal ein (niemals aufgerufener) Aufruf im Code sein, sondern alles komplett wegoptimiert werden.



  • Oh, wie blind von mir!
    thx.


Anmelden zum Antworten