istream rekursiv aufrufen



  • Geht's ein wenig präziser als "funktioniert nicht"? 🙄



  • Verstehe ich das richtig? Die Daten deines GeoPoint-Objekts hast du schon mit dem getline()-Aufruf eingelesen und wunderst dich nun, daß sie nicht mehr im istream stehen. Da heißt die Lösung (vermutlich) stringstream.

    (btw, was soll die Bedingung if(!zeile.find("GeoPoint"))... eigentlich bedeuten? find() liefert keinen Boolean zurück, sondern die Position, an der der Teilstring gefunden wurde.)



  • (btw, was soll die Bedingung if(!zeile.find("GeoPoint"))... eigentlich bedeuten? find() liefert keinen Boolean zurück, sondern die Position, an der der Teilstring gefunden wurde.)

    Der Compiler sieht aber 0 als wahr und ungleich 0 als false an. Auf jedenfall funktioniert das und im Falle eines Punktes komm ich auch in den angegebenen Teil.

    Ich versuch jetzt nochmal mein Problem zu erklären:

    Ich habe eine Klasse GeoShape von der die Klassen GeoPoint, GeoPolygon und GeoGroup erben. Ein Geopoint ist nur ein Punkt, ein GeoPolygon ist ein Polygon und eine GeoGoup ist eine Sammlung von Punkten, Polygonen oder wieder Gruppen.

    Der Benutzer soll nun über die Kommandozeileneingabe eine Geometrie eingeben können, wofür in jeder Klasse der globale istream überladen wurde

    Wenn ich z.B. einen Punkt von der Kommandozeile einlesen will, muss der Benutzer den nach diesem Schema eingeben:

    GeoPoint ID: pointtemp  Geometrie: (6,6)
    

    mit GeoPoint p();
    cin >> p;
    wird er dann eingelesen

    Wenn ich ein Polygon von der Kommandozeile einlesen will, muss der Benutzer den nach diesem Schema eingeben:

    GeoPolygon ID: jPol  Geometrie: {(-1,8)(0,10)(-4,11)(3,6)(-1,8)}
    

    wieder mit GeoPolygon poly();
    cin >> poly;

    Diese beiden Fälle funktionieren ohne Probleme

    Das Problem ist nun die GeoGruppe
    **Ich möchte hier in der Überladung des istreams der GeoGroup die istreams der anderen Klassen aufrufen.
    **

    Der Benutzer gibt die GeoGroup dann so ein:

    GeoGroup ID: nGG  Objekte: 
    <
    GeoPoint ID: pointtemp  Geometrie: (6,6)
    GeoGroup ID: mGG  Objekte: 
    <
    GeoPoint ID: hPoi  Geometrie: (8,10)
    GeoPolygon ID: jPol  Geometrie: {(-1,8)(0,10)(-4,11)(3,6)(-1,8)}
    >
    GeoPolygon ID: polygontemp  Geometrie: {(4,6)(3,4)(2,5)(4,6)}
    >
    

    Code aus der Überladung des istreams der GeoGroup

    // Sollange keine spitze schließende Klammer an Ende einer GeoGroup kommt
    while(is.peek() != '>')
        {
          // Auslesen der Objektbezeichnung
          getline(is, zeile, '\n');
    
          // Prüfen ob GeoPoint Ojekt eingelesen wurde
          if(!zeile.find("GeoPoint"))
          {
    Hier soll nun der Aufruf des überladenen istream operators des GeoPoints kommen
              cout << "Punkt" << endl;
          }
          // Prüfen ob GeoPolygon
          if(!zeile.find("GeoPolygon"))
    Hier soll nun der Aufruf des überladenen istream operators des GeoPolygons kommen
              cout << "Polygon" << endl;
          // Prüfen auf GeoGroup
          if(!zeile.find("GeoGroup"))
    Hier soll nun der Aufruf des überladenen istream operators der GeoGroup kommen
              cout << "Gruppe" << endl;
        }
    

    Geht's ein wenig präziser als "funktioniert nicht"?

    Leider kann ich es nicht genauer schreiben, da ich nicht weiß wie der Code auszusehen hat und ob ein Aufruf des istream operators>> im Quellcode ohne Kommandozeile überhaupt möglich ist.



  • Gloem schrieb:

    (btw, was soll die Bedingung if(!zeile.find("GeoPoint"))... eigentlich bedeuten? find() liefert keinen Boolean zurück, sondern die Position, an der der Teilstring gefunden wurde.)

    Der Compiler sieht aber 0 als wahr und ungleich 0 als false an.

    Andersum.

    Auf jedenfall funktioniert das und im Falle eines Punktes komm ich auch in den angegebenen Teil.

    Du suchst nicht, sondern prüfst nur, ob zeile mit dem gesuchten String anfängt. Nur dann liefert dir find nämlich 0 zurück.

    Geht's ein wenig präziser als "funktioniert nicht"?

    Leider kann ich es nicht genauer schreiben, da ich nicht weiß wie der Code auszusehen hat und ob ein Aufruf des istream operators>> im Quellcode ohne Kommandozeile überhaupt möglich ist.

    Klar ist das möglich. Aber du musst den Fehler besser beschreiben. Mit "funktioniert nicht" kann niemand etwas anfangen.

    Was ist das beobachtete Verhalten genau? Fehlermeldung, falsche Werte, Absturz usw. Irgendwie glaube ich nicht, dass da nur eine Meldung "Funktioniert nicht" auf dem Bildschirm erscheint.



  • Du hast per getline() die komplette Zeile eingelesen, in der die Beschreibung des GeoPoint steht - das heißt, dein Lesezeiger steht nun am Anfang von Zeile 4 und der GeoPoint::operator>> versucht, diese Zeile als Punkt zu lesen, was vermutlich schiefgeht. Die Lösung sieht etwa so aus:

    GeoPoint p;
    istringstream l(zeile);
    l>>p;
    ...
    

    (nur bei der mehrzeiligen Darstellung einer GeoGroup könnte es da ein Problem geben - weil du dort die erste Zeile in der Hand und den Rest der Daten im Stream hast. Als Lösungsansatz könntest du deren operator>> so anpassen, daß er auch Eingaben akzeptiert, die mit ">" beginnen (da mußt du die Gruppen-ID nachträglich ergänzen).)

    PS: Eine Alternative Lösung wäre es, GeoShape eine read-Methode zu geben:

    void read(string first_line,istream& input)
    {
      if(first_line=="") getline(input,first_line);
      ...
    }
    
    istream& operator>>(istream& input,Geoshape&data)
    {
      data.read("",input);
      return input;
    }
    


  • cool, danke.

    Das erste funktioniert schonmal, jetzt muss ich nur noch an die GeoGroup machen



  • Ok, es funktioniert doch nicht, er wirft mich dann bei der Ausgabe raus wenn ich versuche mittels dynamic_cast mein Objekt aus dem Vektor wieder herauszubekommen

    GeoPoint *pGeoPoint = dynamic_cast<GeoPoint*>(*first);
    

    Dann eben doch die zweite wahrscheinlich auch bessere Variante:

    Aber wofür brauch ich in der read-Methode den String? Geht das nicht auch ohne?

    GeoGroup.h

    class GeoGroup : public GeoShape
    {
    public:
    	...
    
    	void read(istream& input);
    
    	...
    };
    

    GeoGroup.cpp

    void GeoGroup::read(istream& input)
    {
    	// Hier den ganzen Rotz aus der alten istream-Methode
            if(!zeile.find("GeoGroup"))
    		  read(is);
    }
    
    // Überladen des globalen Output-Streams für die Eingabe einer GeoGroup
    istream& operator>>(istream& is, GeoGroup& g)
    {
    	g.read(is);
    
    	return is;
    }
    


  • Gloem schrieb:

    Ok, es funktioniert doch nicht, er wirft mich dann bei der Ausgabe raus wenn ich versuche mittels dynamic_cast mein Objekt aus dem Vektor wieder herauszubekommen

    GeoPoint *pGeoPoint = dynamic_cast<GeoPoint*>(*first);
    

    Mit was für einer Meldung wirft er dich heraus? Und was genau steht in first? Und wie wird es gefüllt?

    Dann eben doch die zweite wahrscheinlich auch bessere Variante:

    Aber wofür brauch ich in der read-Methode den String? Geht das nicht auch ohne?

    Damit wollte ich das Problem aushebeln, daß du möglicherweise den ersten Teil deines Objekts schon ausgelesen hast, wenn du den Stream weitergibst (IOStream's bieten zwar die Möglichkeit, eingelesene Zeichen zurückzuschreiben, aber der Standard garantiert nicht, daß das bei so langen Folgen fehlerfrei funktioniert). Die GeoGroup::read()-Methode kann damit die bereits eingelesene Zeile des Sub-Objekts an dessen read()-Methode weitergeben:

    if(zeile.find("GeoPoint")==0)
    {
      GeoPoint p;
      p.read(zeile,input);
      ...
    }
    


  • Klar, an das mit dem String hätte ich auch denken können.

    Der Fehler sieht so aus:
    PopUp mit Unhandled exception at 0x7c812a5b in Pflichtblatt2.exe: Microsoft C++ exception: std::__non_rtti_object at memory location 0x0013f9ac..

    Auftreten passiert während des cout << GeoGroup Aufrufs
    Das liegte daran, das er die Geometrien nicht korrekt in den Vector speichert.

    Mein Vector sieht so aus:

    typedef vector<GeoShape*> Geovektor;
    

    sprich er legt die Objekte Polymorph zur abstrakten Oberklasse GeoShape ab

    Eingefügt werden die mit dieser Methode:

    // Methode um GeoObjekte zur GeoGroup hinzuzufügen
    void GeoGroup::addGeoObject(GeoShape* geo)
    {
    		itsgeos.push_back(geo);
    }
    

    aus der Mainmethoder heraus funktioniert auch super, nur aus der neuen read-Methode nicht, da ich es nicht hinbekomme, einen GeoShape zu erzeugen

    if(!zeile.find("GeoPoint"))
    		{
    			GeoPoint temp("0");
    			istringstream l(zeile);
    			l >> temp;
    			this->addGeoObject(&temp);
    		}
    


  • Gloem schrieb:

    aus der Mainmethoder heraus funktioniert auch super, nur aus der neuen read-Methode nicht, da ich es nicht hinbekomme, einen GeoShape zu erzeugen

    Du erzeugst das Objekt schon - es lebt nur nicht allzu lange, nämlich nur bis zur schließenden geschweiften Klammer. Danach hast du einen Zeiger auf ein nicht mehr existierendes Objekt in deinem vector.

    Leg die Objekte mit new an.



  • Du erzeugst schon einen GeoShape, aber nur lokal - am Ende des if()-Blocks wird temp wieder vernichtet und der Zeiger, den du dir gemerkt hast, verweist auf irgendwelchen Datenmüll. Als Lösung mußt du dir dein Objekt auf dem Heap anlegen (und z.B. im Destruktor der GeoGroup wieder freigeben*):

    if(!zeile.find("GeoPoint"))
    {
      GeoPoint* temp= new GeoPoint("0");
      istringstream l(zeile);
      l >> *temp;
      this->addGeoObject(temp);
    }
    

    * Aber ich bin mir hier nicht ganz sicher über die Besitzverhältnisse (kann der selbe Punkt in mehreren Gruppen enthalten sein?) - besser wäre es womöglich, Smart-Pointer zu verwenden.



  • Klar, danke.

    Hatte es auch schon mit new() erzeugt aber vergessen das Referenzzeichen bei

    this->addGeoObject(temp);
    

    zu entfernen und dann hatte ich an der Baustelle erstmal aufgegeben



  • Gloem schrieb:

    Der Fehler sieht so aus:
    PopUp mit Unhandled exception at 0x7c812a5b in Pflichtblatt2.exe: Microsoft C++ exception: std::__non_rtti_object at memory location 0x0013f9ac..

    Ist GeoShape dynamisch? Wenn nicht füge einfach mal virtual ~GeoShape() { } hinzu.



  • Einmal muss ich glaub ich noch:

    meine beiden Methoden istream& operator>>(..) und read(...) sehen nun so aus:

    // Methode zum Einlesen einer GeoGroup
    void GeoGroup::read(string first_line, istream& is)
    {
    	// Eingabe in der Form: 
    	// GeoGroup ID: nGG  Objekte: 
    	// <
    	// GeoPoint ID: pointtemp  Geometrie: (6,6)
    	// GeoGroup ID: mGG  Objekte: 
    	// <
    	// GeoPoint ID: hPoi  Geometrie: (8,10)
    	// GeoPolygon ID: jPol  Geometrie: {(-1,8)(0,10)(-4,11)(3,6)(-1,8)}
    	// >
    	// GeoPolygon ID: polygontemp  Geometrie: {(4,6)(3,4)(2,5)(4,6)}
    	// >
    
    	string id;
    	string nix;
    	string zeile;
    
    	// Wenn keine Zeile übergeben wurde -> erster Aufruf(keine Rekursion)
    	if(first_line=="")
    	{
    		getline(is,first_line, '\n');
    	}
    	// Abschneiden der ersten Teils des String bis zur ID
    	id = first_line.substr(12, first_line.length());
    	// Abschneiden des hinteren Teils des Strings zur ID
    	id = id.substr(0, id.find(" "));
    	// Setzen der eingelesenen ID
    	this->setID(id.c_str());
    	// Bis zum Ende der Zeile lesen
    	getline(is, nix, '\n');
    	// Die öffnende spitze Klammer einer Geogroup überlesen
    	getline(is, nix, '\n');
    	// Solange weiterlesen bis eine schließende spitze Klammer auftaucht
    	while(is.peek() != '>')
    	{
    		// Auslesen der Objektbezeichnung
    		getline(is, zeile, '\n');
    		// Prüfen ob GeoPoint
    		if(!zeile.find("GeoPoint"))
    		{
    			GeoPoint* temppoi = new GeoPoint("0");
    			istringstream l(zeile);
    			l >> *temppoi;
    			this->addGeoObject(temppoi); 
    		}
    		// Prüfen ob GeoPolygon
    		if(!zeile.find("GeoPolygon"))
    		{
    			GeoPolygon* temppol = new GeoPolygon("0");
    			istringstream l(zeile);
    			l >> *temppol;
    			this->addGeoObject(temppol);
    		}
    		// Prüfen auf GeoGroup
    		if(!zeile.find("GeoGroup"))
    		{
    			GeoGroup* tempgg = new GeoGroup("0");
    			// Rekursiver Aufruf der read-Methode mit der Übergabe des GeoGroup-Anfangstrings
    			(*tempgg).read(zeile, is);
    			this->addGeoObject(tempgg);
    		}
    	}
    }
    
    // Überladen des globalen Output-Streams für die Eingabe einer GeoGroup
    istream& operator>>(istream& is, GeoGroup& g)
    {
    	// Aufruf der Methode zum Einlesen einer GeoGroup
    	g.read("", is);
    
    	return is;
    }
    

    Das Problem ist nun, das wohl nicht mehr alles verarbeitet wird und ich weiß nicht warum:

    Eingabe:

    GeoGroup ID: nGG  Objekte: 
    <
    GeoPoint ID: pointtemp  Geometrie: (6,6)
    GeoGroup ID: mGG  Objekte: 
    <
    GeoPoint ID: hPoi  Geometrie: (8,10)
    GeoPolygon ID: jPol  Geometrie: {(-1,8)(0,10)(-4,11)(3,6)(-1,8)}
    >
    GeoPolygon ID: polygontemp  Geometrie: {(4,6)(3,4)(2,5)(4,6)}
    >
    

    Ausgabe:

    Ausgabe: GeoGroup ID:   Objekte:
    <
    GeoGroup ID:   Objekte:
    <
    GeoPolygon ID: jPol  Geometrie: {(-1,8)(0,10)(-4,11)(3,6)(-1,8)}
    >
    >
    

    Kann mir jemand sagen, warum nicht mehr alle Objekte erzeugt werden? Irgendwie scheint mein this-Zeiger nicht mehr zu funktionieren



  • Du hast ein getline() zu viel in dem Code. Zu Beginn der Zeile 32 steht der Eingabezeiger am Anfang der >-Zeile, dann überliest Zeile 32 diese Eingabezeile und Zeile 34 das erste Element deiner Gruppe.



  • Vielen Dank, darauf hätt ich auch selber kommen können.

    Vielen Dank an alle, jetzt funktionierts. So viele und so schnelle Hilfe hatte ich bislang selten in einem Forum


Anmelden zum Antworten