Segmentation fault beim Zugriff auf polymorphes Object

Schnelle objektorientierte, kompilierende Programmiersprache.
Benutzeravatar
Bebu
Beiträge: 562
Registriert: Mi Okt 21, 2009 6:19 pm
Wohnort: In der Nähe von Salzburg - Bin aber kein Österreicher!

Segmentation fault beim Zugriff auf polymorphes Object

Beitrag von Bebu » Mo Nov 01, 2010 5:54 pm

Hi Leute, verrückter Titel, oder :mrgreen:

Ich bastle mal wieder an Dedupe herum und bin dabei auf ein Verhalten gestoßen, das ich mir nicht erklären kann. Ich kopiere erst mal den Code hier herein:

Code: Alles auswählen

/*!
 * @file datavariant.h
 * @brief defines a class that can hold every datatype (Header only)
 * @details
 * @author Bernhard Buchwinkler
 * @date august 2010
 */
#ifndef ORG_PROGGEN_DEDUPE_DATAVARIANT_H
#define ORG_PROGGEN_DEDUPE_DATAVARIANT_H

#include <typeinfo>
#include <string>
#include "statedataholding.h"

#include <iostream>

namespace Dedupe
{
  /**
    *Namespace Dataholding contains everything to operate the dataholding
    *module
    */
  namespace Dataholding
  {
    /**
      *Namespace Variant includes two classes to handle various datatypes
      *which come out of an SQLite Database (alternative to BOOST::Variant)
      */

    namespace Variant
    {
      /**
        *Data is a pure virtual baseclass to use baseclasspointers for the
        *following class
        */

      class Data
      {
        public:
        virtual const std::type_info & GetType() const = 0;
      };

      /**
        *DataVariant is a template, that can hold every datatype
        */

      template<class T> class DataVariant : public Data
      {
        T HoldedData;

        public:

        /**
          *To construct such a object, the datatype in the template and
          *the given data must be the same datatype
          */
        DataVariant( T data ) : HoldedData( data ) {};

        /**
          *@return the holded filetype as type_info struct
          */

        virtual const std::type_info & GetType() const
        {
          const std::type_info &ti = typeid( HoldedData );

          return ti;
        }

        /**
          *@return the holded data
          */
        T GetData() const
        {
          return HoldedData;
        }
      };


      /**
        *A free function to get the data from GetData while using only the
        *baseclasspointer. First argument is the baseclasspointer, second
        *argument is the variable where the data is written, if there were no
        *cast errors.
        *@return one type from CastResults
        */
      template<class T> CastResults CastDataVariant( Data *incoming, T &returning )
      {
        /*Make sure, holded data and the
          returning variable are frome the same type
        */
        if( incoming->GetType() != typeid( T ) ) return DifferentDatatypes;

        //Define the pointer here, cause the cast is in a different scope
        DataVariant<T> *ptr = NULL;

        //catch bad_cast exceptions and return an BadCastExecption error
        try
        {
          ptr = dynamic_cast
          <Dedupe::Dataholding::Variant::DataVariant<T> *>(incoming);
        }
        catch (std::bad_cast& bc)
        {
          return BadCastException;
        }

        if( ptr == NULL ) return CouldNotCast;
        //write the data to the returning varible
        returning = ptr->GetData();

        return CastOK;
      }
    }
  }
}
#endif
Die Frage bezieht sich auf die freistehende Funktion CastDataVariant. Sie dient dazu, über einen Basisklassenzeiger auch auf eine Funktion zugreifen zu können, die nicht in der Basisklasse enthalten ist. Sie funktioniert wunderbar, wenn ich ein solches polymorphes Objekt dynamisch anfordere, z.B. so:

Code: Alles auswählen

Dedupe::Dataholding::Variant::Data *Basepointer = new Dedupe::Dataholding::Variant::Datavariant<int>(20);
Verzichte ich auf das dynamische Erzeugen, sondern richte nur einen Basisklassenpointer auf so ein Objekt aus und versuche damit die CastDataVariant() Funktion zu füttern, bekomme ich beim Zugriff auf

Code: Alles auswählen

 if( incoming->GetType() != typeid( T ) ) return DifferentDatatypes;
einen Speicherzugriffsfehler. Ist das normales Verhalten? Wenn ja, warum meckert da der Compiler nicht? Leider ist Polymorphie Neuland für mich.
Wer immer nach dem Unerreichbaren jagt, der wird irgendwann auf die Schnauze fallen!

Benutzeravatar
cloidnerux
Moderator
Beiträge: 3125
Registriert: Fr Sep 26, 2008 4:37 pm
Wohnort: Ram (Gibts wirklich)

Re: Segmentation fault beim Zugriff auf polymorphes Object

Beitrag von cloidnerux » Mo Nov 01, 2010 6:08 pm

Ich hab gerade selber keine Direkte Antwort auf das Problem, aber ich kann folgendes schon einmal sagen:
Der Compiler meckert nicht rum, weil er selber nicht weiß, ob zur Laufzeit alle Pointer valide sind und ob sie auch alle das machen, was DU willst.
Deiner Fehlerbeschreibung zufolge klingt das so, dass du versuchst einen Pointer auf ein nicht dafür ausgelegtes Objekt zeigen zu lassen.
hast du mal versucht, einen Pointer auf das vorher von dir Dynamisch erzeugte Objekt zeigen zu lassen?
Redundanz macht wiederholen unnötig.
quod erat expectandum

Benutzeravatar
Bebu
Beiträge: 562
Registriert: Mi Okt 21, 2009 6:19 pm
Wohnort: In der Nähe von Salzburg - Bin aber kein Österreicher!

Re: Segmentation fault beim Zugriff auf polymorphes Object

Beitrag von Bebu » Mo Nov 01, 2010 6:14 pm

Ja, das habe ich auch. Wenn ich zuerst mit dem Pointer ein Objekt dynamisch erzeuge und anschließend den Pointer auf ein nichtdynamische Objekt ausrichte, bekomme ich den Fehler nicht. Ich habe allerdings nicht überprüft, ob dann alle Objektdaten korrekt sind.
Wer immer nach dem Unerreichbaren jagt, der wird irgendwann auf die Schnauze fallen!

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Segmentation fault beim Zugriff auf polymorphes Object

Beitrag von Xin » Mo Nov 01, 2010 9:12 pm

Ist der Code im Repository, dass ich ihn mal auschecken und testen kann?

Wenn ja, welche Schritte muss ich machen, um den Fehler auszulösen?
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
Bebu
Beiträge: 562
Registriert: Mi Okt 21, 2009 6:19 pm
Wohnort: In der Nähe von Salzburg - Bin aber kein Österreicher!

Re: Segmentation fault beim Zugriff auf polymorphes Object

Beitrag von Bebu » Mo Nov 01, 2010 9:40 pm

Ich habe es dir gerade hochgeladen, jetzt ist der Code so, das der Fehler auftritt. Das ganze liegt im Branch Dataholding, die Datei heißt testdatavariant.cpp und dort ab Zeile 55. Den Bereich darunter habe ich auskommentiert, das ist so eine Art Workaround, um die Unittests trotzdem machen zu können. Der Fehlerbereich gehört so nicht zum Code, habe es nur zum Testen reingesetzt. Vielleicht auch nur ein ganz banaler Fehler, den ich wieder nicht sehe. Der eigentlich Fehler tritt beim Zugriff auf GetType() über den Zeiger auf, innerhalb der CastDataVariant Funktion (Datei: datavariant.h)
Wer immer nach dem Unerreichbaren jagt, der wird irgendwann auf die Schnauze fallen!

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Segmentation fault beim Zugriff auf polymorphes Object

Beitrag von Xin » Mo Nov 01, 2010 10:00 pm

Das kompiliert bei mir nicht, es fehlen Dateien.

Code: Alles auswählen

xin@trinity:~/workspace/dedupe/branches/dataholding$ cmake .
-- Configuring done
CMake Error in CMakeLists.txt:
  Cannot find source file "testsqlitewrapper.h".  Tried extensions .c .C .c++
  .cc .cpp .cxx .m .M .mm .h .hh .h++ .hm .hpp .hxx .in .txx


-- Build files have been written to: /home/xin/workspace/dedupe/branches/dataholding
xin@trinity:~/workspace/dedupe/branches/dataholding$ 
Die .cpp fehlt auch.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
Bebu
Beiträge: 562
Registriert: Mi Okt 21, 2009 6:19 pm
Wohnort: In der Nähe von Salzburg - Bin aber kein Österreicher!

Re: Segmentation fault beim Zugriff auf polymorphes Object

Beitrag von Bebu » Mo Nov 01, 2010 10:33 pm

Achja, mein Fehler, ich habe die beiden Dateien schon in die Cmake Datei eingetragen, sie aber nicht hochgeladen, weil sie beide noch leer sind. Lösche entweder die Dateien aus der Cmake Datei, leg dir zwei leere Dateien mit den Namen an, oder checke nochmal aus. Du kannst es dir aussuchen.
Wer immer nach dem Unerreichbaren jagt, der wird irgendwann auf die Schnauze fallen!

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Segmentation fault beim Zugriff auf polymorphes Object

Beitrag von Xin » Mo Nov 01, 2010 11:29 pm

In Testdatavariant.cpp, Zeile 59 rufst Du das Template. Das erste Argument ist NULL.

Dann schreibst Du "*BasePointer = IntVariant". Du kopierst also an die Adresse 0 den Inhalt von "IntVariant". Schon erstaunlich, dass Dir das nicht um die Ohren fliegt. BasePointer verändert damit seinen Wert nicht, beleibt also NULL.

Im Template in datavariant.h, Zeile 91 fragst Du incoming->GetType() ab, wobei incoming NULL ist => Segmentation Fault.
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Benutzeravatar
Bebu
Beiträge: 562
Registriert: Mi Okt 21, 2009 6:19 pm
Wohnort: In der Nähe von Salzburg - Bin aber kein Österreicher!

Re: Segmentation fault beim Zugriff auf polymorphes Object

Beitrag von Bebu » Di Nov 02, 2010 8:49 am

Womit wir wieder einmal bewiesen haben, das Zeiger teuflische Werkzeuge sind. ;) Nein im Ernst, ich kann dann also keine bereits bestehenden Objekte einem Basisklassenzeiger zuweisen, es sei denn, ich caste sie brutal?! Gibt es eigentlich eine Möglichkeit zur Laufzeit zu überprüfen, ob ein Zeiger auf eine gültige Adresse zeigt? Damit meine ich keine Überprüfung, ob der Zeiger auf NULL zeigt.
Wer immer nach dem Unerreichbaren jagt, der wird irgendwann auf die Schnauze fallen!

Benutzeravatar
Xin
nur zu Besuch hier
Beiträge: 8862
Registriert: Fr Jul 04, 2008 11:10 pm
Wohnort: /home/xin
Kontaktdaten:

Re: Segmentation fault beim Zugriff auf polymorphes Object

Beitrag von Xin » Di Nov 02, 2010 9:43 am

Bebu hat geschrieben:Womit wir wieder einmal bewiesen haben, das Zeiger teuflische Werkzeuge sind. ;) Nein im Ernst, ich kann dann also keine bereits bestehenden Objekte einem Basisklassenzeiger zuweisen, es sei denn, ich caste sie brutal?!
Moment... lies, was Du da schreibst. Wenn Du einem bestehendem Objekt einen Basispointer zuweisen willst, hast Du schon im Konzept etwas falsch gemacht. Das ist als würdest Du eine Stadt mit einem Wegweiser gleichsetzen. Ein Wegweiser ist aber keine Stadt.

Weiterhin hast Du einen Pointer, den Du nicht initialisiert hast. Du hast also nur einen Wegweiser, der ins nichts zeigt. Und nur weil Du dann eine Stadt ins Nirwana schickst, hast Du den Wegweiser nicht beschriftet.

Du musst den Pointer auf das Objekt initialisieren, statt das Objekt ins Nirwana zu kopieren, wo der Pointer derzeit hinzeigt.

Code: Alles auswählen

basepointer = &IntegerObject;
(ich habe den Code jetzt nicht hier, um die Namen der Variablen anzupassen)
Bebu hat geschrieben:Gibt es eigentlich eine Möglichkeit zur Laufzeit zu überprüfen, ob ein Zeiger auf eine gültige Adresse zeigt? Damit meine ich keine Überprüfung, ob der Zeiger auf NULL zeigt.
Gibt es eine Möglichkeit in der Realität zu überprüfen, ob ein Wegweiser in die Richtung zeigt, wie er beschriftet ist?

Natürlich gibt es sie. Wenn Du ein GPS-Gerät hast, um Deine Position zu bestimmen, einen Kompass, eine Karte und ein Lineal, um die Richtung von Deiner Position aus in die Karte einzutragen, dann kannst Du das vor Ort überprüfen. Das geht in C++ natürlich auch - sogar automatisiert.
Du kannst operator new und operator delete überschreiben und bei new die Adresse in eine Liste eintragen und bei delete die Adresse aus der Liste entnehmen. Die Liste kannst Du dann fragen, ob Du Zugriff auf eine Adresse hast.
Das ist aufwendig - es kostet Zeit und deswegen macht es keiner. Man vertraut einfach darauf, dass niemand den Wegweiser verdreht hat.

Du folgst einem Wegweiser ins Nichts. Warum sollte Dich C++ davon abhalten, woher sollte C++ wissen, dass nicht genau das das gewünschte Verhalten ist - schließlich hast du es ja explizit programmiert?
Merke: Wer Ordnung hellt ist nicht zwangsläufig eine Leuchte.

Ich beantworte keine generellen Programmierfragen per PN oder Mail. Dafür ist das Forum da.

Antworten