[Gelöst] Überschneidung von Adressen bei Basisklasse, die nur im Debugmode Member enthält
-
TL,DR: Bei zwei verschiedenen Instanzen bei einer Klasse, die von einer Basisklasse erbt, die nur im Debugmode Member enthält, überschneiden sich die Adressen zweier Member.
Ich bin schon seit mehreren Tagen am Suchen von einem Fehler.
Ich habe mir Iteratorenklassen geschrieben, die im Debugmode bspw. bei einem Vergleich prüfen sollen, ob beide Iteratoren in den selben Container zeigen, bei einem Increment prüfen ob sie nicht über end hinaus laufen etc..
Für diese Checks brauchen die Iteratoren im Debugmode einen Zeiger auf den Container, auf den sie zeigen, aber nicht wenn das entsprechende Makro nicht definiert ist.
Dazu habe ich ein paar Klassen:namespace impl { template <typename Iterator, bool enabled> class iterator_checker { // Funktionen die beim Überprüfen aufgerufen werden und die Überprüfung vornehmen // Pointer auf Container }; template <typename Iterator> class iterator_checker<Iterator, false> { // die gleichen Funktionen, die aber nichts tun // kein Pointer }; } #if SHOULD_CHECK template <typename Iterator> using iterator_checker = impl::iterator_checker<Iterator, true>; #else template <typename Iterator> using iterator_checker = impl::iterator_checker<Iterator, false>; #endifDie Iteratorenklasse erbt von iterator_checker und ruft ggf. die entsprechenden Checkmethoden auf.
So weit, so gut.
Nun hatte ich auf einmal einen hässlichen Bug. Beim Berechnen der distance zwischen zwei Iteratoren in std::equal_range kam irre hohe Werte raus, obwohl der Container zu diesem Zeitpunkt leer war und beide Iteratoren auf nullptr zeigten*!
Nach mehreren Stunden des Debuggen, erhielt ich via geeigneter cout Ausgaben in den Iteratorenkonstruktoren folgendes Ergebnis:END: 0x28fdfc=0 // end Iterator an Adresse 0x28fdfc wird erstellt, der interne Pointer zeigt auf 0
BEGIN: 0x28fe00=0 // begin Iterator an Adresse 0x28fe00 wird erstellt, der interne Pointer zeigt auf 0
COPY: 0x28fdfc=0x28fe70 0x28fda0=0x28fe70 // Kopie des Iterators an Adresse 0x28fdfc (= der end Iterator vom Anfang) wird erstellt, der interne Pointer zeigt aber nun auf 0x28fe70, ebenso bei der Kopie!Woher kommt die Adresse 0x28fe70 in den Pointer des Iterators?
Nachdem ich mir die Adressen angeschaut habe erhielt ich folgendes:
Der end-Iterator an Adresse 0x28fdfc:
-Pointer auf container (von der Basisklasse iterator_checker) an Adresse 0x28fda0
-Pointer in den container an Adresse 0x28fda4
Beim begin-Iterator an Adresse 0x28fe00
-Pointer auf container an Adresse 0x28fda4!!!
-Pointer in den container an Adresse 0x28fda8Der Pointer auf den Parent-container von begin und der eigentliche Pointer in den Container vom end-Iterator teilen sich eine Adresse!
Meine Vermutung liegt daran, dass das etwas damit zu tun hat, dass die Basis-Klasse leer ist, wenn das Makro nicht gesetzt ist, aber einen Pointer Member hat, wenn das Makro gesetzt ist und dass sich das dann irgendwie mit der EBO oder so überschneidet. Weiß einer ob das stimmt bzw. was ich dagegen machen kann?*Das sind random access iteratoren, die Berechnung wird also einfach via last - first durchgeführt. Da beide (eig.) nullptr waren, sollte auch 0 rauskommen.
-
Tönt danach, dass dein Build-System kaputt ist, also dass eine Objektdatei mit Debug kompiliert wurde und die andere nicht und dann zusammengelinkt.
Nach mehreren Stunden des Debuggen, erhielt ich via geeigneter cout Ausgaben [...]
cout-Debugging (besser bekannt als printf-Debugging) war noch nie ein guter Weg.
-
Kannst du mal den Code zeigen der obigen Output erzeugt? Ich hab nicht ganz verstanden, was du genau tust...
-
odr schrieb:
Tönt danach, dass dein Build-System kaputt ist, also dass eine Objektdatei mit Debug kompiliert wurde und die andere nicht und dann zusammengelinkt.
Ja, muss mir nochmal alle Optionen anschauen.
Nach mehreren Stunden des Debuggen, erhielt ich via geeigneter cout Ausgaben [...]
cout-Debugging (besser bekannt als printf-Debugging) war noch nie ein guter Weg.
In diesem Fall hat es geholfen.
@dot:
Also ich hab diese Klassen von oben:namespace impl { template <typename Iterator, bool enabled> class iterator_checker { // Funktionen die beim Überprüfen aufgerufen werden und die Überprüfung vornehmen typename Iterator::container_type *container_;// Pointer auf Container }; template <typename Iterator> class iterator_checker<Iterator, false> { // die gleichen Funktionen, die aber nichts tun // kein Pointer }; } #if SHOULD_CHECK template <typename Iterator> using iterator_checker = impl::iterator_checker<Iterator, true>; #else template <typename Iterator> using iterator_checker = impl::iterator_checker<Iterator, false>; #endifDie werden dann in der Iteratorklasse wie folgt genutzt:
class iterator : private iterator_checker<iterator> { public: // diverse Funktionen halt, z.B: value_type& operator*() const { // Wenn SHOULD_CHECK gesetzt prüft die Funktion ob *this == container_->end(), ansonsten nicht iterator_checker<Iterator>::check_valid(); } private: value_type *ptr_; // der eigentliche Iterator };Nun ist bei mir der Fall aufgetreten das bei zwei Iterator a und b die Adresse von a.ptr_ die selbe ist wie b.container_.
Edit: Habs gefunden. Der Tipp von odr war richtig. Das "SHOULD_CHECK" Makro wurde anhand eines anderen Makros gesetzt, was für das Modul in dem sich der iterator_checker befindet gesetzt war, bei dem Modul des iterators aber nicht, die wurden dann in eine große Lib gelinkt und vom Hauptprogramm verwendet. Ich könnte mich echt facepalmen dafür.