Klassenfrage :)


  • Gesperrt

    (Das Forum harkt gerade extrem bei mir...)

    Man schaue auch mal nach den anderen Madelbrot-Themen in diesem Forum ... oder geht es darum gar nicht?



  • @wob
    diese struct _AppleData speichert nur die Parameter-Sätze mit denen das einzige Objekt der Klasse _Apple immer wieder neu initialisiert wird.
    Es gibt vier Klassen von denen allen zur Laufzeit jeweils nur ein Objekt mit new instaziert wird.
    Colorinterface->Apple->Userinterface->Display.
    Ein einziges Objekt der Klasse "_Display" wird einem einzigen Objekt der Klasse " _Userinface" übergeben, dieses Userinterface-Objekt wird einem einzigen Objekt der Klasse _Apple übergeben welches slebst dem einzigen Objekt der Klasse "_Colorinterface" Übergeben wird.

    Ich "liebe" diese Mandelbrotbilder und hab mit meiner ersten Python version diesen Programms und ffmpeg, Filme mittels Bildfolge der einzelnen "Farb-iterationsstufen" erstellt; Die sind Großformatfüllend betrachtet, berauschend.
    Um solch eine Funktionalität nutzerfreundlich zu realisieren ist dieses mandelbrodt gedacht.
    Ich habe Start- und Endparameter solch einer Bildsequenz vor meinem geistigen Auge, welche die Berechnung steuern. Muss aber alles erst noch Programmiert werden, bis jetzt ist ja nur möglich in das Apfelmänchen zu zoomen und es einzufärben und dann zu betrachten. (Von den spontanen Abstürzen abgesehen, die ich erst weg haben will bevor Funktionalität hinzugefügt wird)



  • @omggg
    Jawoll 😃 ich bekomm mit deinen flags nen haufen compilermeldungen mit denen ich mich nun auseinander setzten kann. Das -fsanitaze:memory funktioniert bei mir nicht, aber die Anderen werfent einen haufen Meldungen aus. Die versuch ich erst mal zu verstehen. Danke



  • @Finnegan
    Verstehe ich das richtig, das -fsanitize mir Meldungen(Abstürze) erst zur laufzeit "verursacht" ?
    Ah blöde Frage, eben seh ich das er es gar nicht kompiliert mit diesem Flag



  • Findet sich hier vielleicht ein Kenner in c++ der mir die einfachste Klasse "_Display" mit ihren drei kleinen Methoden in sauberem c++ darlegen kann, als minimal-Bsp-Code in Betracht ihrer Beziehung Colorinterface->Apple->userinterface->Display. Dann kann ich den Rest nach diesem Muster neu gestalten. Sonst muss ich durch den "Wolf" weil mir grad die Mittel fehlen für ein neues Fachbuch



  • @EL-europ sagte in Klassenfrage 🙂:

    @Finnegan
    Verstehe ich das richtig, das -fsanitize mir Meldungen(Abstürze) erst zur laufzeit "verursacht" ?
    Ah blöde Frage, eben seh ich das er es gar nicht kompiliert mit diesem Flag

    Ja, die sanitize-Optionen sind zur Laufzeit.

    Versuchs mal mit clang-tidy oder cppcheck (wobei das Herumdoktoren an den Symptomen wäre, neu schreiben erscheint hier als plausible Option).

    /tmp/mandelbrodt/include/_Apple.cpp:65:66: error: arithmetic on a pointer to void [clang-diagnostic-error]
                            pthread_create( &thrIds[aktThr], NULL, &thrFunc, (void *)tmp_x+aktThr*sizeof(int));
                                                                             ~~~~~~~~~~~~~^
    /tmp/mandelbrodt/include/_Apple.cpp:149:2: warning: 'sprintf' will always overflow; destination buffer has size 17, but format string expands to at least 18 [clang-diagnostic-fortify-source]
            sprintf(iterText, "I-Werte % 8d", countsOfIter);
            ^
    /tmp/mandelbrodt/include/_Apple.cpp:171:3: warning: 'sprintf' will always overflow; destination buffer has size 16, but format string expands to at least 18 [clang-diagnostic-fortify-source]
                    sprintf(text[5], "Itr2= % 10d", iterMembers[countsOfIter-2][0]);
                    ^
    /tmp/mandelbrodt/include/_Colorinterface.cpp:53:2: warning: 'sprintf' will always overflow; destination buffer has size 8, but format string expands to at least 9 [clang-diagnostic-fortify-source]
            sprintf(text,"%- 7d",id);
            ^
    /tmp/mandelbrodt/include/_Colorinterface.cpp:55:2: warning: 'sprintf' will always overflow; destination buffer has size 8, but format string expands to at least 9 [clang-diagnostic-fortify-source]
            sprintf(text2,"%- 7d",satzcount);
            ^
    /tmp/mandelbrodt/include/_Colorinterface.cpp:88:2: warning: 'sprintf' will always overflow; destination buffer has size 9, but format string expands to at least 10 [clang-diagnostic-fortify-source]
            sprintf(text,"% 8d",elements.at(apos).id);
            ^
    /tmp/mandelbrodt/include/_Colorinterface.cpp:102:2: warning: 'sprintf' will always overflow; destination buffer has size 9, but format string expands to at least 10 [clang-diagnostic-fortify-source]
            sprintf(text2,"% 8d",members);
            ^
    /tmp/mandelbrodt/include/_Colorinterface.cpp:245:63: error: non-constant-expression cannot be narrowed from type 'unsigned long' to 'int' in initializer list [clang-diagnostic-c++11-narrowing]
            struct _farbe farbe2{elements.at((elements.size()-1)).color, (elements.size()-1)};
                                                                         ^~~~~~~~~~~~~~~~~~~
    /tmp/mandelbrodt/include/_Colorinterface.cpp:245:63: note: insert an explicit cast to silence this issue
            struct _farbe farbe2{elements.at((elements.size()-1)).color, (elements.size()-1)};
                                                                         ^~~~~~~~~~~~~~~~~~~
                                                                         static_cast<int>(  )
    /tmp/mandelbrodt/include/_Userinterface.cpp:135:2: warning: 'sprintf' will always overflow; destination buffer has size 30, but format string expands to at least 31 [clang-diagnostic-fortify-source]
            sprintf(text[1], "i = %.24f", i);
            ^
    /tmp/mandelbrodt/include/_Userinterface.cpp:152:33: warning: 'sprintf' will always overflow; destination buffer has size 8, but format string expands to at least 10 [clang-diagnostic-fortify-source]
            if(element < 2 || element > 5) sprintf(text, "% 8d", (int)elements.at(element).wert);
                                           ^
    /tmp/mandelbrodt/include/graphics/font8x8.h:67:25: error: constant expression evaluates to 255 which cannot be narrowed to type 'char' [clang-diagnostic-c++11-narrowing]
        { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00},   // U+002A (*)
                            ^~~~
    /tmp/mandelbrodt/include/graphics/font8x8.h:67:25: note: insert an explicit cast to silence this issue
        { 0x00, 0x66, 0x3C, 0xFF, 0x3C, 0x66, 0x00, 0x00},   // U+002A (*)
                            ^~~~
                            static_cast<char>( )
    /tmp/mandelbrodt/include/graphics/font8x8.h:120:49: error: constant expression evaluates to 255 which cannot be narrowed to type 'char' [clang-diagnostic-c++11-narrowing]
        { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF},   // U+005F (_)
                                                    ^~~~
    /tmp/mandelbrodt/include/graphics/font8x8.h:120:49: note: insert an explicit cast to silence this issue
        { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF},   // U+005F (_)
                                                    ^~~~
                                                    static_cast<char>( )
    

    PS: Warum checkst du die compilierte Datei mit ins Repo ein?



  • Ich nutz das git "nur" als Backup, bei mir sterben die usb sticks, sd-karten und andere speichermeiden manchmal.
    Die erste compiler meldung bekomm ich auch (ohne die sanitizer) und bedeuted nur das er die Größe/Breite des Integerwertes, der zur Adresse addiert wird zur compileZeit nicht kennt. Das sit kein Problem meines erachtens in c, in c++ ist es vielleicht ein prop?
    Die zweite meldung interpretiere ich so, das er den Rest des Strings einfach abschneidet? Aber lass mich belehren und werde die c++ Version std::format nutzen müssen.
    Die Folgende Meldung "narrowed from type 'unsigned long'": bedeuted (vermute ich) nur das ein Long Int zu Int verengt wird, das "prob" umgehe ich in dem ich nur Werte bis 9999999 im prog zulasse

    Die nächste Meldung welche die char-Fontarrays betrifft: Wäre es besser den Typ uint8_t zu nehmem? Nur weiss ich grad net obs dann props mit Funktionen auf ihnen gibt.



  • Gerade die Display ist undankbar, weil System abhängig. Ich sitze z.B. gerade an einem Windows Rechner und probiere zumindest gerne aus, ob das funktioniert 😉

    Ich versuche mal ein bisschen was, anhand der mandel.cpp zu erklären.

    #define BORDER 2
    

    hier würde man in modernem C++

    constexpr unsigned int 2
    

    nehmen. Der Vorteil zur Preprozessor Ersetzung ist, dass das typsicher ist.

    _Display *Display;
    _Userinterface *Ui;
    _Apple *Apple;
    _Colorinterface *Ci;
    

    Wie schon mehrfach geschrieben, globale Variablen nutzt man nicht. Da weiß man nie genau welche in welchem Zustand ist. Da definieren, wo sie gebraucht werden und u.a. als Parameter weiter reichen.

    in _AppleData sind ein paar Member nicht default initialisiert, besser

    struct _AppleData{
    	int xpos{};
    	int ypos{};
    	int width{};
    	int height{};
    	int xres{1920};
    	int yres{1080};
    	long double rmin{-1};
    	long double rmax{2};
    	long double imin{-1};
    	long double imax{1};
    	int depth{100};
    };
    
    std::vector<struct _AppleData> myApples;
    

    das Wort struct brauchst du hier nicht. MIt struct und class definierst du Typen, das hier reicht:

    std::vector<_AppleData> myApples;
    

    Das hier

    int aktApple = 0;
    

    ist irgendwie der Index nach dem letzten Element in myApples. Das brauchst du nicht, ein std::vector kapselt alles, was du für den Umgang damit benötigen könntest.

    Dein _Apple scheint teilweise die gleichen Member zu haben, wie _AppleData. Gib _Apple doch einen Member _AppleData appleData stattdessen und eine Funktion

    _AppleData _Apple::GetAppleData()
    {
       return appleData;
    }
    

    Jetzt wird es wild

    void newApple(){
    		myApples.push_back(*new _AppleData);
    

    new _AppleData erzeugt einen _AppleData auf dem Heap und gibt einen Pointer darauf zurück. Den dereferenzierst du mit * und rufst über push_back impliziet den Copy Ctor auf. Du merkst die aber den Pointer nicht, der auf den Heap zeigt und kannst den Speicher gar nicht mehr freigeben.

    Wenn du es so behalten möchtest, wie du es gerade hast, einfach:

    void newApple(){
    		myApples.push_back(_AppleData());
    

    Wenn du den vorgeschlagenen Getter von mir implementiert hast:

    void newApple(){
    		myApples.push_back(Apple->GetAppleData());
    }
    

    und die Funktion ist fertig.

    Hier

    void ui_callback(int i){
    	if(i<1){
    		myApples.at(aktApple-1).xres = Apple->ui->getWert(0);
    

    willst du auf das letzte element von myApples zugreifen.

    Ohne Indexzugriff und extra merken von aktApple:

    void ui_callback(int i){
    	if(i<1){
    		myApples.back().xres = Apple->ui->getWert(0);
    

    Das hier:

    Apple->xres = myApples.at(aktApple-1).xres;
    			Apple->yres = myApples.at(aktApple-1).yres;
    			Apple->xpos = myApples.at(aktApple-1).xpos;
    			Apple->ypos = myApples.at(aktApple-1).ypos;
    			Apple->width = myApples.at(aktApple-1).width;
    			Apple->height = myApples.at(aktApple-1).height;
    			Apple->rmin = myApples.at(aktApple-1).rmin;
    			Apple->rmax = myApples.at(aktApple-1).rmax;
    			Apple->imin = myApples.at(aktApple-1).imin;
    			Apple->imax = myApples.at(aktApple-1).imax;
    			Apple->depth = myApples.at(aktApple-1).depth;
    

    Sieht so aus, als ob du nicht nur einen Getter auf einen _AppleData Member in Apple brauchen könntest, sondern auch einen Setter:

    void _Apple::SetAppleData(const _AppleData& data)
    {
       appleData = data;
    }
    

    Dann fällt das oben in dashier zusammen:

    Apple->SetAppleData(myApples.back());
    

    Erstmal als Anregung. Ich muss jetzt noch ein bisschen selbst arbeiten 😉

    P.S: Mach die _ vor deinen Typen weg. Die stören doch nur beim Tippen.



  • @Schlangenmensch
    Hey danke für deine umfangreichen und detaiillierten Tips, mit denen hab ich nun auch Arbeit 🙂



  • @EL-europ sagte in Klassenfrage 🙂:

    Die erste compiler meldung bekomm ich auch (ohne die sanitizer) und bedeuted nur das er die Größe/Breite des Integerwertes, der zur Adresse addiert wird zur compileZeit nicht kennt. Das sit kein Problem meines erachtens in c, in c++ ist es vielleicht ein prop?

    Nein, es ist unklar, was void* für eine Breite hat. Wurde dir oben schon erklärt. Siehe auch https://stackoverflow.com/questions/3523145/pointer-arithmetic-for-void-pointer-in-c

    Die zweite meldung interpretiere ich so, das er den Rest des Strings einfach abschneidet?

    Wieder falsch. Es wird nicht magisch passend zurechtgekürzt. Es wird Speicher überschrieben, der dir (bzw. der Variable) nicht gehört. Undefiniertes Verhalten, alles kann passieren. Das Programm darf abstürzen, falsche Dinge tun etc.



  • @EL-europ sagte in Klassenfrage 🙂:

    Jawoll 😃 ich bekomm mit deinen flags nen haufen compilermeldungen mit denen ich mich nun auseinander setzten kann. Das -fsanitaze:memory funktioniert bei mir nicht, aber die Anderen werfent einen haufen Meldungen aus. Die versuch ich erst mal zu verstehen. Danke

    Ich schrieb, dass -fsanitize=memory nicht von GCC unterstützt wird, nur von Clang. Bei Bedarf einfach mit clang++ statt mit g++ kompilieren. Die sind weitgehend kompatibel und arbeiten bei allen grundlegenden Sachen mit den selben Parametern. Bestimmte Sanitizer kann man aber nicht zusammen verwenden, wie z.B. -fsanitize=address und -fsanitize=memory. Da müsste man das Programm mehrmals durchtesten, einmal mit dem einen und einmal mit dem anderen Sanitizer.

    Verstehe ich das richtig, das -fsanitize mir Meldungen(Abstürze) erst zur laufzeit "verursacht" ?

    Ja, wie @wob schon erwähnt hat. Oft hängen die Bedingungen, die zu einem Fehler führen, von erst zur Laufzeit bekannten Inputs ab und können nicht sinnvoll während des Kompilierens analysiert werden. Dafür sind die Sanitizer da. Für statische Analyse (Probleme nur anhand des Codes erkennen) gibt es die von @wob erwähnten Tools wie clang-tidy, cppcheck und andere. Es ist nicht verkehrt, die ebenfalls zu verwenden.

    Auch den Compiler aufzufordern sehr strikte Warnungen auszugeben und jede Warnung als Fehler zu interpretieren -Wall -Wpedantic -Werror (und eventuell sogar noch -Wextra) kann hilfreich sein, da es einen zwingt, sich zumindest mal mit potentiellen Problemen auseinanderzusetzen. Selbst wenn die Warnungen manchmal unproblematisch sind, wenn man weiss dass diese in ihrem jeweiligen Kontext nicht zu einem Problem führen können (da sollte man sich aber sicher sein).

    Ah blöde Frage, eben seh ich das er es gar nicht kompiliert mit diesem Flag

    Die Sanitizer werden aber schon untersützt oder? Ich glaube ein aktuelles Debian z.B. hat GCC 12, damit sollte das gehen. Die Version kannst du mit g++ -dumpversion herausfinden.

    Ansonsten sehe ich es auch so, dass es wohl besser ist das Progamm weitgehend neu zu schreiben anstatt "an den Symptomen he­r­um­zudok­tern". Die Sanitizer und die statischen Analysetools sollten dir allerdings helfen, dich auch davon zu überzeugen - oder dich zwingen, das implizit zu tun, wenn du das alles "fixen" willst 🙂



  • @Finnegan
    ja ich hab den g++ 12.2.0, der kennt auch kein #include <format>
    Mit Codeanalyse hab ich mich in solch gefordertem Ausmaß noch nicht beschäftigt, wird wohl Zeit?



  • @wob
    Es ist also Zufall das die thread-Funktion den richtigen x-wert bekommt?
    Und wie kann man es umsetzen so das es gutes c++ ist?



  • @EL-europ sagte in Klassenfrage 🙂:

    @Finnegan
    ja ich hab den g++ 12.2.0, der kennt auch kein #include <format>

    Features der Standardbibliothek werden mitunter abhängig vom C++-Standard aktiviert, mit dem der Compiler arbeitet. Ich weiss gerade nicht, was der Default-Standard für GCC 12 ist (könnte C++14 sein), aber std::format und den zugehörigen Header gibt es erst ab C++20. Du solltest diesen Explizit mit dem Compiler-Flag -std=c++20 aktivieren. Dann steht dir auch der <format>-Header zur Verfügung.

    Edit: Blödsinn, "Text formatting" und damit std::format scheint es laut dieser Support-Matrix tatsächlich erst ab GCC 13 und Clang 17 zu geben. Bei diesen gilt dann aber, was ich oben geschrieben habe. Das ist nur aktiv, wenn der Compiler in dem entsprechenden Sprachstandard arbeitet.

    Mit Codeanalyse hab ich mich in solch gefordertem Ausmaß noch nicht beschäftigt, wird wohl Zeit?

    Schadet gewiss nicht. Du scheinst da ja auch ein gutes Testprogramm zu haben, mit dem du sehr viel über das Thema lernen kannst 😛



  • @EL-europ sagte in Klassenfrage 🙂:

    ja ich hab den g++ 12.2.0, der kennt auch kein #include <format>

    std::format ist da noch nicht drin. Alternativ zu einem Kompiler-update könntest du fmtlib nehmen.
    Diese Lib kannst du dann bei Zeiten mehr oder weniger 1:1 gegen std::format austauschen (hoffe ich zumindest - so ist es bei uns geplant).



  • @Schlangenmensch
    Betreff:
    _Display *Display
    _Apple * Apple
    ... meint
    Ich soll im Konstruktor der _Userinterface ein eigenes Objekt "_Display" mit new instazieren? Das hab ich eben schnell versucht und zum Beginn der Laufzeit speicherzugriffsfehler erhalten. Oder meinst du eine andere Herangehensweise?



  • @Jockelx
    Der zweite zweck der Software ist mit dem Standart der Linuxdistro auszukommen, alle (auch ich) verschieben nur Registerinhalte und machen nix Neues nur anders. Ich will nicht von Fremdbibliotheken abhängen; Natürlich kann ich ohne Andere auch Nichts, aber es soll nicht an Softwarebibliotheken hängen.



  • @EL-europ Ich bin mir nicht sicher, was du meinst. Ich bin mir nur sicher, dass ich kein new gemeint habe.
    Wenn ein Userinterface ein Display haben soll, warum soll das denn auf dem Heap Alloziert werden und ein Pointer in Display sein?



  • @Schlangenmensch
    Achso* 🙂
    Du meinst es brauchen gar keine Objekte instanziert werden wenn ich sowieso nur auf einem "operiere"?

    Ich probier mal ohne "new" auszukommen, vermute aber das ich damit alles, auch die Aufrufe, ändern muss



  • @EL-europ sagte in Klassenfrage 🙂:

    Du meinst es brauchen gar keine Objekte instanziert werden wenn ich sowieso nur auf einem "operiere"?

    Ich denke @Schlangenmensch meint vermutlich so etwas hier:

    int main()
    {
        _Display Display;
        _Userinterface Ui{ BORDER, BORDER, Display, ui_callback };
        ...
    }
    

    das bedeutet nicht, dass keine Objekte instanziert werden, sondern dass sie als automatische Variablen hierbei direkt auf dem Stack liegen und nicht im Heap.

    Das bietet sich gerade bei denen besonders an, weil die Objekte zu Beginn des Programms einmal erzeugt werden und über die gesamte Laufzeit erhalten bleiben. Diese Objekte werden dann mit der schließenden geschweiften Klammer am Ende von main automatisch zerstört und deren Destruktoren aufgerufen. Kein delete notwendig.

    Der zweite zweck der Software ist mit dem Standart der Linuxdistro auszukommen, alle (auch ich) verschieben nur Registerinhalte und machen nix Neues nur anders. Ich will nicht von Fremdbibliotheken abhängen; Natürlich kann ich ohne Andere auch Nichts, aber es soll nicht an Softwarebibliotheken hängen.

    libfmt hat scheinbar auch einen "header-only"-Modus:

    • Optional header-only configuration enabled with the FMT_HEADER_ONLY macro

    Damit wäre das Einbinden sehr simpel, da nur das #include benötigt wird, ohne die Bibliothek extra bauen und linken zu müssen. Du könntest deren Quellcode z.b. in ein Unterverzeichnis deines Projekts kopieren, und die Header von dort einbinden. Ausserdem wäre das nur eine Übergangslösung, bis die Default-Compiler der Distributionen das unterstützen (wenn alles gut geht müsste man dann nur das #include <fmt/format.h> in ein #include <format> und den Namespace bei den Aufrufen von fmt:: nach std:: abändern).