Haben diese Defines irgendeinen Sinn?



  • Logging.

    Aufgrund dieser Information rate ich mal ins Blaue und vermute du meinst Boost:

    BOOST_LOG_TRIVIAL(trace) << "A trace severity message";
    

    Naja, schau dir mal die Implementierung an.

    #define BOOST_LOG_UNIQUE_IDENTIFIER_NAME_INTERNAL_(prefix, postfix)\
        BOOST_PP_CAT(prefix, postfix)
    #define BOOST_LOG_UNIQUE_IDENTIFIER_NAME_INTERNAL(prefix, postfix)\
        BOOST_LOG_UNIQUE_IDENTIFIER_NAME_INTERNAL_(prefix, postfix)
    
    #if BOOST_WORKAROUND(BOOST_MSVC, >=1300)
    #  define BOOST_LOG_UNIQUE_IDENTIFIER_NAME(prefix)\
        BOOST_LOG_UNIQUE_IDENTIFIER_NAME_INTERNAL(prefix, __COUNTER__)
    #else
    #  define BOOST_LOG_UNIQUE_IDENTIFIER_NAME(prefix)\
        BOOST_LOG_UNIQUE_IDENTIFIER_NAME_INTERNAL(prefix, __LINE__)
    #endif
    
    #define BOOST_LOG_STREAM_INTERNAL(logger, rec_var)\
        for (::boost::log::record rec_var = (logger).open_record(); !!rec_var;)\
            ::boost::log::aux::make_record_pump((logger), rec_var).stream()
    
    #define BOOST_LOG_STREAM_WITH_PARAMS(logger, params_seq)\
        BOOST_LOG_STREAM_WITH_PARAMS_INTERNAL(logger, BOOST_LOG_UNIQUE_IDENTIFIER_NAME(_boost_log_record_), params_seq)
    
    #define BOOST_LOG_TRIVIAL(lvl)\
        BOOST_LOG_STREAM_WITH_PARAMS(::boost::log::trivial::logger::get(),\
            (::boost::log::keywords::severity = ::boost::log::trivial::lvl))
    

    Ist ja alles Gut und schön. Aber wenn ein Fehler in dem Makro ist, hast du ein ernsthaftes und unnötiges Debugging Problem.

    Typischer Write-Only Code...



  • Richtig geraten.

    Bitte ein Bit schrieb:

    Ist ja alles Gut und schön. Aber wenn ein Fehler in dem Makro ist, hast du ein ernsthaftes und unnötiges Debugging Problem.

    Ist die Implementierung unschön zu lesen? Ja.
    Ist es lästig zu debuggen? Ja.
    Ist die Implementierung unmöglich zu verstehen? Nein.
    Ist die Implementierung unmöglich zu debuggen? Nein.

    Ist es schweinepraktisch? Ja.

    Ich mag schweinepraktische Sachen. Zeig mir eine Implementierung mit Inline-Funktionen die ähnlich performant ist und ähnlich schöne Syntax erlaubt.

    Typischer Write-Only Code...

    Das ist schwer übertrieben.
    Davon abgesehen: was für einen Grund sollte es geben da grossartig was dran zu ändern? Notfalls ist die gesamte Implementierung klein genug dass man sie einfach komplett neu schreiben kann.

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



  • Makros sind manchmal wahnsinnig praktisch. Einige moderne Sprachmittel wurden vor C++11 mit Makros emuliert.

    • Range-Based For Loop: Boost.Foreach
    • RValue-Referenzen: Boost.Move
    • Variadic Templates: Boost.Preprocessor

    Aber das rechtfertigt natürlich noch lange keinen Code wie _new von diesem Thread...



  • @Nexus
    Das _new Makro in diesem Thread hat mMn. ganz klar den Sinn Code zwischen C und C++ kompatibel zu halten. Und das ohne überall bei malloc manuell die Casts schreiben zu müssen.
    Über den Namen kann man streiten, über einige der anderen Makros auch, aber grundsätzlich finde ich es eine sehr gute Lösung.

    Manuell hundertfach in einem Projekt

    var = (Foo*)malloc(sizeof(Foo));
    

    zu schreiben ist sicher nicht die Lösung. Böse DRY Verletzung.



  • hustbaer schrieb:

    Das _new Makro in diesem Thread hat mMn. ganz klar den Sinn Code zwischen C und C++ kompatibel zu halten.

    Dazu überlädt man operator new und operator delete.


  • Mod

    operator delete schrieb:

    hustbaer schrieb:

    Das _new Makro in diesem Thread hat mMn. ganz klar den Sinn Code zwischen C und C++ kompatibel zu halten.

    Dazu überlädt man operator new und operator delete.

    Und wie überlädt man einen Operator in C?



  • Arcoth schrieb:

    Ist natürlich Unsinn.
    Vor allem, weil es sogar genauso definiert ist:

    Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.

    Höh? Doch nur wenn der Name __ als Prefix hat bzw. ein Großbuchstabe dem _ folgt?


  • Mod

    Ja, ich war sehr überrascht, aber genauso ist es. Deine Regel trifft aber auf etwas anderes zu.

    Hier das Zitat:

    17.6.4.3.2 Global names [global.names]
    Certain sets of names and function signatures are always reserved to the implementation:
    — Each name that contains a double underscore _ _ or begins with an underscore followed by an uppercase letter (2.12) is reserved to the implementation for any use.
    — Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.



  • hustbaer, stimmt, hab gedacht es handle sich um C++-Code. In C gibt es generell mehr legitime Fälle für Makros, weil halt gewisse Alternativen einfach fehlen.

    Für eine Abstraktion des new -Operators in C++ würde ich eher zu Funktionstemplates à la make_shared() greifen.



  • Fein dann sind wir uns ja einig 🙂

    Ja, in C++ gäbe es hier viele andere Möglichkeiten. Wenn es nicht anders geht, bzw. anders nicht gleich "gut" geht, dann finde ich aber Makros auch in C++ eine sehr feine Sache.
    Ein bisschen mächtiger könnten sie gerne sein. Verzichten würde ich aber nie auf Makros wollen.

    Wie der totale Verzicht auf Makros ausgeht sieht man bei C#: haufenweise wiederholter Code, Code der unnötig laaaaaangsam ist weil er über Reflection rumtrickst, Code der sich auf nicht-standardisierte Details verlässt wie z.B. wie Expressions als LINQ-Objekt-Graphen dargestellt werden usw.
    *schauder*

    @Arcoth
    Die krankste Regel is für mich die mit der die ganzen "E" #define s reserviert werden ( EIO, EAGAIN, ENOMEM, EINVAL , ...).



  • Arcoth schrieb:

    Ja, ich war sehr überrascht, aber genauso ist es. Deine Regel trifft aber auf etwas anderes zu.

    Hier das Zitat:

    17.6.4.3.2 Global names [global.names]
    Certain sets of names and function signatures are always reserved to the implementation:
    — Each name that contains a double underscore _ _ or begins with an underscore followed by an uppercase letter (2.12) is reserved to the implementation for any use.
    — Each name that begins with an underscore is reserved to the implementation for use as a name in the global namespace.

    Ah ja genau. So war das.
    Jetzt muss ich mir keine Sorgen um meine Membervariablen mache die ich mit nem _ prefixe. 🙂



  • Ist die Implementierung unschön zu lesen? Ja.
    Ist es lästig zu debuggen? Ja.
    Ist die Implementierung unmöglich zu verstehen? Nein.
    Ist die Implementierung unmöglich zu debuggen? Nein.
    

    Ok das ist ein Punkt.

    Wenn aber ein Fehler auftritt muss man im schlimmsten Fall das Makro komplett auflösen. Und das ist unschön und für meinen Geschmack auch unnötig...

    Vielleicht sollte ich meine Aussage mal revidieren/korrigieren. Einzeiler sind für meine Geschmack ok. Aber bei allen anderem sollte man sich überlegen ob man mit Funktionen besser bedient wäre. Es sei denn man braucht ein Makro zur Compilezeit, wie bei Template-Programmierung.

    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. 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?

    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.



  • 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


Anmelden zum Antworten