Pufferung von Texten zur Ausgabe mehrteiliger Inhalte in Konsolenfenstern



  • Ich bräuchte mal hilfe zu folgendem

    #include <iostream>
    #include <iterator>
    #include <vector>
    #include <cstdlib>
    
    class rowbuffer
    {
          private:
                 char text;
          public:
                 rowbuffer *next;
                 rowbuffer *prev;
                 char get(){return(text);};
                 void set(char x){text = x;};
    };
    
    class linebuffer
    {
      public:
        rowbuffer *first;
        rowbuffer *last;
        linebuffer *nextline;
        linebuffer *prevline;
    
    //    create linebuffer                                                         
    
        void create_linebuffer()
        { 
          rowbuffer *actual;
          first = new rowbuffer;
          last = new rowbuffer;
          actual=first;
    
          for (int i=0; i<80; i++)
          {
            actual->set(0);
            actual->next = new rowbuffer;
            actual = actual->next;
            last=actual;
          };
    
          first->prev=0;
          last->next=0;
        };
    
    //    getline                                                                   
    
        std::vector<char> getline()
        {
          std::vector<char> a(80);
          rowbuffer *actual;
          actual = first;
    
          for (int i = 0; i<80; i++)
          {
            a[i]=actual->get();
    
            if (actual->next != 0)
            {
              actual=actual->next;
            };
    
      };
    
      return a;
    };
    
    //    setline                                                                   
    
        void setline(std::vector<char> text, int pos_y)
        {
          rowbuffer *actual;
          actual=first;
          for (int i=0;i<80;i++)
          {
            if (i>=pos_y) {actual->set(text[i-pos_y]);};
            if (actual->next != 0){actual=actual->next;};
          };
    
        };
    
    };
    
    class fieldbuffer
    {
          public:
                 linebuffer *firstline;
                 linebuffer *lastline;
                 fieldbuffer *nextwindow;
                 fieldbuffer *lastwindow;
    
                 int pos_x;
                 int pos_y;
    
          void create_fieldbuffer()
          {
               firstline = new linebuffer;
               lastline = new linebuffer;
               linebuffer *actualline;
               actualline=firstline;
               pos_x=0;
               pos_y=0;
               for (int i=0; i<25; i++)
               {
                   actualline->nextline = new linebuffer;
                   actualline = actualline->nextline;
                   actualline->create_linebuffer();
                   lastline=actualline;
               };
               firstline->prevline=0;
               lastline->nextline=0;
          };
          void goto_xy(int x, int y)
          {
    
          pos_x=x;
          pos_y=y;
    
          }
    
          void print()
          {
               linebuffer *actual;
               actual = firstline;
               for (int i=0; i<25; i++)
               {
                   std::vector<char> tmp = actual->getline();
                   copy(tmp.begin(),tmp.end(),std::ostream_iterator<char,char>(std::cout,""));
                   actual=actual->nextline;
               };
          };
    
          void write(std::string text)
          {
               linebuffer *actual;
               actual = firstline;
               for (int i = 0; i < pos_x+1; i++)
               {
               actual=actual->nextline;
               }
               std::vector<char> textvector(80);
               for (int i=0; i<80-pos_y; i++)
                   {
                     if (text[i] != 0)   
                        {textvector[i]=text[i];}
                     else   
                        {textvector[i]=' ';};
                   };
               actual->setline(textvector, pos_y);    
          };
    };
    

    Das ganze soll einen Puffer erzeugen, in dem Zeichen für Zeichen der Text für eine Konsolenausgabe geschrieben wird. Da ich keine Lust hatte, die zerlegung manuell vorzunehmen (d.h. wirklich jedes Zeiche einzeln zu schreiben), wird der Text als std::string übergeben.
    Das Problem ist, das irgendwas beim zerlegen und/oder Schreiben nicht so läuft, wie es eigentlich geplant ist. Ich erhalte zu beispiel bei

    int main(int argc, char *argv[])
    {
        fieldbuffer buffer01;
        buffer01.create_fieldbuffer();  
        buffer01.write("Guten Morgen");
        buffer01.goto_xy(5,7);
        buffer01.write("und tschuess!");
        buffer01.goto_xy(20,10);
        buffer01.write("Na wenns den sein muss");
        buffer01.goto_xy(21,0);
        buffer01.write("qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM");
        buffer01.print();
    
        std::cin.get();
    
    }
    

    folgende Ausgabe:

    °°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°°
    Guten Morgen ♂ ♣ u Guten Morgen ♂ ♣ u Guten Morgen ♂ ♣

    und tschuess! ♂ ♣ u und tschuess! ♂ ♣ u und tschuess!

    Na wenns den sein muss ê♥♠ ► x☺= x☺= qwertzui
    opasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM ~♥
    ► x☺= x☺=

    Das stellt zwar schon eine verbesserung gegenüber der vorherigen, uneklärlichen leeren Konsole dar, ist aber immernoch wenig zweckdienlich. Ich habe schon mehrere Leute dazu befragt, es wusste aber keiner, was nicht stimmt. Für konkrete Hilfe wäre ich dankbar, möglichst per E-Mail und mit Name (ich muss das ganze zur Bewertung abgeben).



  • Debugger.



  • Hab ich auch schon dran gedacht, aber der von Dev-Cpp findet nichts, und Borland C++-Builder kommt damit nicht zurecht



  • Ich hab jetzt eine provisorische Lösung, sieht so aus, dass ich die String-Länge mit übergebe, ich kann mich da aber dumpf errinner, das man die irgendwie auch auslesen konnte, weis jemand, wie???



  • Speicherst du den string in einem vector? Da kannst du die enthaltenen Elemente mit size() abfragen. Ansonsten kannst du hier gucken:
    http://www.cppreference.com/



  • Danke, funktioniert in dem Kontext, aber weist du zufällig auch, wie ich Variablen anlegen kann, die im ganzen Programm nutzbar sind, und nicht nur in einzelnen Teilen (namespace hab ich schon versucht, gibt aber beim einbinden in mehrere Teile eines Programms fehlermeldung vonwegen mehrfacher Definition)

    Programm gibts zur einsicht unter kosan.ko.funpic.de/programme/Space Colonizing



  • globale Variablen können im ganzen Programm benutzt werden, aber sie sind nicht gerade hoch angesehen



  • Kann möglich sein, dass du deine globalen Variablen mit dem Schlüsselwort extern deklarieren musst. Je nachdem, ob du sie in mehreren Quellcode-Dateien (*.cpp) verwendest. Da empfielt sich dann eine Header-Datei mit den (externen) globalen Variablen. 😉



  • was bedeuted der zusatz extern hier genau?

    extern wie bei dll's?



  • Gregor Kosan schrieb:

    was bedeuted der zusatz extern hier genau?

    Also wenn du die Definition einer Variable in eine Header stellst (ohne extern) und dann diesen Header in mehrere *.Cpp-Dateien inkludierst...dann enthält jede *.obj Datei eine Definition dieser Varialbe...das wär ja einerseits nicht 'richtig' gobal und anderseits doppelt gemoppelt 😉

    Deswegen extern:

    Mal ein Beispiel:

    Demo.h

    #ifndef _DEMO_H_
    #define _DEMO_H_
    
    // Deklaration:
    extern int g_iGlobalCounter;
    // ...
    
    #endif /* _DEMO_H_ */
    

    Main.cpp

    #include "Demo.h"
    // ...
    // Definition:
    int g_iGlobalCounter = 0;
    // ...
    

    Alle anderen *.cpp-Dateien, die g_iGlobalCounter verwenden wollen inkludieren jetzt Demo.h
    und können auf die Varialbe zugreifen bzw. diese modifizieren. 👍

    Beispiel:

    Definition.cpp

    #include "Demo.h"
    // ...
    // 'Benutzung':
    g_iGlobalCounter = 345; // oder was auch immer...
    // ...
    

    Edit: Kommentare eingefügt 😉



  • Danke, scheint erstmal zu funktionieren



  • Hab schon wieder so einen unerklärlichen Fehler: zugriff auf NULL, wo ich alle zugriffe auf NULL ausgeschlossen habe.

    Code sieht volgendermaßen aus:

    #include "includes_ext.hpp"
    
    class rowbuffer
    {
      private:
        char text;
      public:
        rowbuffer *next;
        rowbuffer *prev;
        char get(){if (text != 0){return(text);} else {return ' ';};};
        void set(char x){text = x;};
    };
    
    class linebuffer
    {
      public:
        rowbuffer *first;
        rowbuffer *last;
        linebuffer *nextline;
        linebuffer *prevline;
    
    //    create linebuffer                                                         
    
        void create_linebuffer()
        { 
          rowbuffer *actual;
          first = new rowbuffer;
          last = new rowbuffer;
          actual=first;
    
          for (int i=0; i<80; i++)
          {
            actual->set(' ');
            actual->next = new rowbuffer;
            actual = actual->next;
            last=actual;
          };
    
          first->prev=0;
          last->next=0;
        };
    
    //    getline                                                                   
    
        std::vector<char> getline()
        {
          std::vector<char> a(80);
          rowbuffer *actual;
          actual = first;
    
          for (int i = 1; i<80; i++)
          {
            a[i]=actual->get();
    
            if (actual->next != 0)
            {
              actual=actual->next;
            };
    
      };
    
      return a;
    };
    
    //    setline                                                                   
    
        void setline(std::vector<char> text, int pos_y, int length)
        {
          rowbuffer *actual;
          actual=first;
          for (int i=1;i<80;i++)
          {
            if ((i>=pos_y) and (i<length+pos_y)) {actual->set(text[i-pos_y]);};
            if (actual->next != 0){actual=actual->next;};
          };
    
        };
    
    };
    
    class fieldbuffer
    {
          public:
                 linebuffer *firstline;
                 linebuffer *lastline;
                 fieldbuffer *nextwindow;
                 fieldbuffer *lastwindow;
    
                 int pos_x;
                 int pos_y;
    
          void create_fieldbuffer()
          {
               firstline = new linebuffer;
               lastline = new linebuffer;
               linebuffer *actualline;
               actualline=firstline;
               pos_x=0;
               pos_y=0;
               for (int i=0; i<25; i++)
               {
                   actualline->nextline = new linebuffer;
                   actualline = actualline->nextline;
                   actualline->create_linebuffer();
                   lastline=actualline;
               };
               firstline->prevline=0;
               lastline->nextline=0;
          };
          void goto_xy(int x, int y)
          {
    
          pos_x=x;
          pos_y=y;
    
          }
    
          void print()
          {
               linebuffer *actual;
               actual = firstline;
               for (int i=0; i<25; i++)
               {
                   std::vector<char> tmp = actual->getline();
                   copy(tmp.begin(),tmp.end(),std::ostream_iterator<char,char>(std::cout,""));
                   actual=actual->nextline;
               };
          };
    
          void write(std::string text/*, int lenght*/)
          {
               linebuffer *actual;
               actual = firstline;
               for (int i = 0; i < pos_x+1; i++)
               {
               actual=actual->nextline;
               }
               std::vector<char> textvector(80);
               for (int i=0; i<80-pos_y; i++)
               {
                  if (i < text.size())
                     {textvector[i]=text[i];}
                  else
                     {textvector[i]=' ';};
               };
               actual->setline(textvector, pos_y, text.size());    
          };
    };
    

    das ganze gibt nun bei

    char get(){if (text != 0){return(text);} else {return ' ';};};
    

    einen Segmentation Fault aufgrund des Auslesens des Speicherbereiches 0x00000000.

    Ich wäre dankbar, wenn mir jemand sagen könnte, warum.



  • Obiges hat sich ers mal erledigt, ist zwar nicht behoben(Lösung wäre also immer noch gut), tritt aber nicht mehr auf.

    Was jetzt kommt, hat eigentlich nichts mehr mit dem Buffer zu tun, der Buffer gehört nur zu dieser Anwendung dazu.

    Und zwar hab ich ein Problem mi Pointern.

    Ich hab ein Array aus Objekten, die in einem hexagonalen Raster (wie Bienenwaben) angeordnet sind, und jedes Objekt zeigt auf seine Nachbarn. Eines dieser Felder ist ausgewählt. Diese Auswahl soll jetzt auf eines der Nachbarfelder verwschoben werden. Da die Auswahl über einen Pointer auf das ausgewählte Feld läuft, dachte ich mir, ich richte den Pointer auf das Ziel eines der Pointer, die auf die Nachbarfelde zeigen. Das sieht bei mir da wie folgt aus:

    switch(pos_director)
      {
        case(8):
            actual=actual->next1;
        case(9):
            actual=actual->next2;
        case(3):  
            actual=actual->next3;
        case(2):
            actual=actual->next4;
        case(1):
            actual=actual->next5;
        case(7):
            actual=actual->next6;
        default:
            actual=actual;
      };
    

    Das Problem ist jetzt, das immer 0x00000014 ausgelesen werden soll. Der Debugger weigert sich mitlerweile öfters mal, überhaupt noch was zu tun, und wenn, dann bleibt er bei dem obigen stehen.(wenn das hier noch nicht an Code reicht, den rest gibts unter http://kosan.ko.funpic.de/Space Colonizing)



  • Ärrrg! Von diesem Quelltext bekommt man ja Augenkrebs. Die Ursache für den Fehler, den du beschrieben hast, könnte überall liegen. In dem Auszug, den du angegeben hast, war allerdings nichts zu erkennen, und ich habe ehrlich gesagt keine Lust mir noch mehr Code in diesem Stil anzusehen.

    Was mir nur gerade aufgefallen ist:

    void create_linebuffer()
        {
          rowbuffer *actual;
          first = new rowbuffer;
          last = new rowbuffer;
          actual=first;
    
          for (int i=0; i<80; i++)
          {
            actual->set(' ');
            actual->next = new rowbuffer;
            actual = actual->next;
            last=actual;
          };
    

    An dieser Stelle hast du ein Memory-Leak (überprüf mal die Verwendung von last).

    Außerdem:

    for (int i = 1; i<80; i++)
          {
            a[i]=actual->get();
    
            if (actual->next != 0)
            {
              actual=actual->next;
            };
    

    Bist du sicher, daß du hier bei 1 anfangen willst zu zählen?

    Ich würde vorschlagen, daß du dieses Programm erst mal etwas robuster machst. Dann fällt nämlich auch das Debugging leichter. Spendier deinen Klassen erst einmal Konstruktoren und Destruktoren, mach die Daten-Member private. Und wenn du auf diesem manuellen Herumgefullem mit Zeiger bestehst, dann setzt wenigstens ein paar assert-Anweisungen ein (das wirkt manchmal Wunder).



  • 1. Davon gibts höchstens ne Netzhautablösung, Krebs am Auge ist sehr unwarscheinlich.

    2. Das einzige, was bei der zuweisung von last nicht passt, ist, dass das ganze auch auserhalb der Schleife passieren könnte.

    3. Nein, ich bin mir sicher, das ich hier doch lieber bei 0 anfange.

    4. Was soll robuster heisen???

    5. Ich würde ja konstruktoren und destruktoren verwenden, nur hat mir noch niemand erklären können, wie die verwendet werden und zu was die gut sind.

    6. Gibt es denn eine alternative zur manuellen zuweisung der Zeiger?

    7. Was soll mir eine assert Anweisung bringen???



  • zu 1.: Ok, da hast du recht. Ist aber auch nicht viel besser.

    zu 2.: Nein! Du weist last ein mit new angelegtes Objekt zu. Später überschreibst du den Zeiger wieder, wodurch der Zugriff auf besagtes Objekt nicht mehr möglich ist. Das nennt man ein Memory-Leak.

    zu 3.: Gut!

    zu 4.: Robust heißt, resistenter gegen Probleme. Das kann zum Beispiel bedeuten, daß auftretende Fehler weniger obskur und damit leichter zu debuggen sind.

    zu 5.: Dann lern es. Das sind elementare Grundlagen und solange du die nicht beherrschst, hat es eigentlich keinen Sinn mit Klassen zu arbeiten. Ich bin mir sicher, daß sich hier im Forum irgendwo ein guter Link dazu findet (mußt halt ein Bißchen suchen).

    zu 6.: Du könntest z.B. die rowbuffer-Objekte "innerhalb" eines linebuffer-Objektes in einen std::vector legen. Dann sparst du dir zumindest schon mal den prev/next-Krempel (sehe sowieso nicht wieso du da an dieser und an anderen Stellen Zeiger brauchst; würde es eine Array-artige Datenstruktur nicht auch tun?)

    zu 7.: Du hast offensichtlich Zeiger-Probleme. assert kann da nützlich sein.



  • re: zu 1. Doch, ist besser, weil vollständig heilbar 😉

    re: zu 2. OK, da hast du recht.

    re: zu 6. Das hab ich schon versucht, das ist Version 4.2 des Textbuffers, version 1.0 - 4.1 haben nicht funktioniert, weil der zugriff nicht möglich war (Frag mal einen gewissen Lars Hupel, was bei vector<vector<char>> passierte, oder bei einem 2dim Array, auserdem ist das ganze stark rudimentär, sollte ursprünglich dynmamisch an die Konsolengröße/Textgröße angepasst werden, hat sich aber erübrigt, ich hab nur keine Lust, das ganze noch mal zu machen, weil es endlich mal funktioniert. Und das gennnte Problem ist auch nicht wirklich teil des Textbuffers). Und zu dem Problem mit den Zeigern bei den Felder, die stellen nur eine vereinfachung dar, z.B. im bezug auf die Ausgabe, bei der ich sonst die Felder aus der liste einzeln auswählen müsste, was mir erstens zu viel Arbeit und zweitens zu unübersichtlich ist.

    re: zu 7. assert hätte die gleiche auswirkung wie der Fehler selbst. (hab mitlerweile nachgeschlagen)

    Und was konstruktoren und destruktoren angeht, so weis ich nur so viel, das der konstruktor ein Objekt anlegt (eine Instanz einer Klasse), und der destruktor dieses Objekt wieder zerstört. Wenn das ganze noch weiterführernde bedeutung hat, bzw. essentiell ist, da wüsste ich das gerne auch mal.



  • Was soll bei std::vector<std::vector<char> > passieren? Das ist ein absolut legales Konstrukt, das bestens funktioniert (jedenfalls, wenn man es korrekt anwendet). Ich nehme an, du beziehst dich jetzt auf irgendeinen anderen Thread. Aber den habe ich offensichtlich nicht mitverfolgt und kann deshalb dazu jetzt nichts weiteres sagen.

    Zu Konstruktor und Destruktor:

    Mit dem Konstruktor wird ein Objekt einer Klasse angelegt und initialisiert. Was du in deiner create_linebuffer-Funktion stehen hast, ist mehr oder weniger das, was stattdessen eigentlich im Konstruktor stehen sollte.

    Der Destruktor zerstört ein Objekt und räumt vorher noch auf. Das fehlt in deinem Code völlig, soweit ich sehe. Du erzeugst Objekte mit new, aber es gibt kein passendes delete dazu.



  • das mit std::vector<std::vector<char>> wurde auch nicht im Forum besprochen, ich kenne Lars persönlich. Und an diesem Konstrukt als solches ist auch nichts auszusetzen, nur das es keine möglichkeit eines organisierten zugriffes gibt, und da könnte ich ja auch gleich ein2dim array nehmen un jeden char einzeln reinschreiben.

    Und zu dem ding mit den Konstruktoren und Destruktoren:

    Wie kann ich aus meinen create-Prozeduren Konstruktoren machen und eine Instanz erzeugen?

    (destruktoren brauch ich nicht, das objekt fieldbuffer soll nicht gelöscht werden, sondern wärend der gesamten Laufzeit aktiv sein)

    (Neue, korrigierte version ist hochgeladen, bitte genauere schilderung der Probleme mit dem Quelltext-Aufbau)



  • Keine Möglichkeit eines organisierten Zugriffs? Und was ist mit:

    Vec[x][y]
    

    ?

    (destruktoren brauch ich nicht, das objekt fieldbuffer soll nicht gelöscht werden, sondern wärend der gesamten Laufzeit aktiv sein)

    Also das will ich mal überhört haben. Es ist zwar in der Regel richtig, daß der reservierte Speicher nach Beendigung des Programms automatisch wieder freigegeben wird. Aber das ist trotzdem keine Entschuldigung für einen fehlenden Desturktor.

    Was den Konstruktor angeht: Ok, ich will mal nicht so sein. Hier ist ein Konstruktor für die Klasse fieldbuffer ausgehenden von dem obigen Quelltext:

    fieldbuffer() : firstline (0), lastline (0), nextwindow (0), lastwindow (0)
          {
               firstline = new linebuffer;
               linebuffer *actualline;
               actualline=firstline;
               pos_x=0;
               pos_y=0;
               for (int i=0; i<25; i++)
               {
                   actualline->nextline = new linebuffer;
                   actualline = actualline->nextline;
                   actualline->create_linebuffer();
                   lastline=actualline;
               };
               firstline->prevline=0;
               lastline->nextline=0;
          };
    

Anmelden zum Antworten