amiga-news ENGLISH VERSION
.
Links| Forum| Kommentare| News melden
.
Chat| Umfragen| Newsticker| Archiv
.

amiga-news.de Forum > Programmierung > Wie recoverable alert unter AOS4.1 debuggen? [ - Suche - Neue Beiträge - Registrieren - Login - ]

-1- 2 [ - Beitrag schreiben - ]

19.02.2012, 21:34 Uhr

Reth
Posts: 1858
Nutzer
Hallo zusammen,

stehe gerade vor dem Problem, dass mein Programm beim Beenden einen recoverable alert verursacht. Leider weiss ich nicht, wie ich diesem auf die Spur kommen soll. Logausgaben helfen hier nicht, da in der Zeit des Alerts der Bildschirm schwarz ist und ich auch nicht auf einen anderen Screen umschalten kann! Nach dem Alert wird das Programm weiter ausgeführt! Ich nutze C++ und den GCC zusammen mit Codebench.

Bin für jeden Tip dankbar!

Ciao

[ - Antworten - Zitieren - Direktlink - ]

19.02.2012, 22:32 Uhr

Holger
Posts: 8116
Nutzer
Das versteh ich nicht. Wieso helfen Dir Log-Ausgaben nicht weiter? Musst Du sie exakt in dem Augenblick lesen, wenn der Alert auf dem Bildschirm ist? Warum nicht einfach dann lesen, wenn der Alert weg ist?

--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

19.02.2012, 22:44 Uhr

thomas
Posts: 7716
Nutzer
@Reth:

Der erste Ahaltspunkt ist immer der Inhalt der Alerts. Eine Erklärung der Alert-Nummer findest du in exec/alerts.h.


--
Email: thomas-rapp@web.de
Home: thomas-rapp.homepage.t-online.de/

[ - Antworten - Zitieren - Direktlink - ]

20.02.2012, 00:09 Uhr

Reth
Posts: 1858
Nutzer
@Holger:
Weil dann alle Ausgaben da sind, von allen Programmteilen. So kann ich nicht mehr identifizieren, wann der Fehler auftrat (hab gerade ne Menge Logausgaben).

@thomas:
Danke, werd ich mal abgleichen.

[ - Antworten - Zitieren - Direktlink - ]

20.02.2012, 11:24 Uhr

Holger
Posts: 8116
Nutzer
@Reth:
Da empfiehlt es sich, beim Logging grundsätzlich den Zeitpunkt mit auszugeben. Das ist bei gepufferter Ausgabe ohnehin unerlässlich, wenn Du die Programmereignisse mit anderen Ereignissen wie dem Alert abgleichen willst.

--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

27.02.2012, 20:16 Uhr

Reth
Posts: 1858
Nutzer
Zitat:
Original von thomas:
Der erste Ahaltspunkt ist immer der Inhalt der Alerts. Eine Erklärung der Alert-Nummer findest du in exec/alerts.h.

Besten Dank für diese Hilfe! Gebe noch Speicher doppelt frei. Nun muss ich nur noch die Stelle finden (das kann was werden - seufz).

[ - Antworten - Zitieren - Direktlink - ]

27.02.2012, 20:35 Uhr

Thore
Posts: 2266
Nutzer
Das ist easy. Vor jedem Free auf NULL prüfen, und nach jedem Free auf NULL setzen.
Beispiel:
if(!(myvar==NULL))
{
free(myvar);
myvar = NULL;
}
So umgehst du grundsätzlich Double Frees.

[ - Antworten - Zitieren - Direktlink - ]

27.02.2012, 20:51 Uhr

Holger
Posts: 8116
Nutzer
@Thore:
So einfach ist das nicht. Die Speicherfreigabe kann Teil einer beliebigen anderen Ressourcefreigabe sein, sofern die jeweilige Bibliotheksfunktion nicht auf doppelte Freigaben prüft.

Außerdem sagt die Tatsache, dass eine Variable null oder nicht null ist, überhaupt nichts darüber aus, ob der zugehörige Speicherbereich freigegeben wurde. Man kann die Adresse eines Speicherbereichs nämlich auch in mehr als nur einer Variablen halten. Das ist bei nicht-trivialen Programmen sogar sehr häufig der Fall.

Will man dagegen auf Fehlersuche gehen, ist es wesentlich sinnvoller, während der Debugging-Phase die free-Funktion selber zu ersetzen, statt um jeden einzelnen Aufruf code drumherum zu schreiben.

PS: if(!(myvar==NULL)) ist grausam. Das schreibt man entweder als if(myvar!=NULL) oder, da es sich um C handelt als if(myvar)

--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

28.02.2012, 13:22 Uhr

thomas
Posts: 7716
Nutzer
@Thore:

Außerdem behebt es das Problem nicht, sondern umgeht es nur. Der Fehler in der Programmlogik bleibt, es tut nur nicht mehr weh, wenn man das zweite mal an der Stelle vorbei kommt. Im Prinzip ist es sogar gefährlich, weil man den Fehler nicht mehr bemerkt.


--
Email: thomas-rapp@web.de
Home: thomas-rapp.homepage.t-online.de/

[ - Antworten - Zitieren - Direktlink - ]

28.02.2012, 16:02 Uhr

Thore
Posts: 2266
Nutzer
Den Fehler kann man sicher auch nur beheben, wenn man sich erstmal grundlegend die Strukturen überlegt. Ich nehme an es ist das gleiche Programm wie im anderen Thread, wo man leicht rauslesen kann, daß der Code schon so komplex ist, daß man nicht mehr weiß was wohin greift oder sonstwie funktioniert.
Am besten mal den Code von Hand analysieren und schauen wo generelle Probleme im Code und der Struktur an sich sind. Also ein System reinbringen. Ich denk dann ist der Double Free automatisch auch weg.

[ - Antworten - Zitieren - Direktlink - ]

28.02.2012, 20:04 Uhr

Reth
Posts: 1858
Nutzer
@Thore:
Ja es ist das gleiche Programm. Und ich habe mir schon ne ganze Menge Gedanken zu den Strukturen und dgl. gemacht. Auch ist der Code schon komplex und bestimmt auch kompliziert, nicht zuletzt durch die fehlende "bessere" API des AmigaOS (z.B. im Vgl. zu SDL, JAVA, was auch immer) und meine ausgesuchte Nutzung von C++ mit dem Ziel so viel wie möglich in späteren Projekten wieder zu verwenden!
Ich würde es auch sehr gern einfacher und übersichtlicher haben, habs aber noch nicht geschafft.

Und durch C++ kommen auch meine derzeitigen Probleme. Denn jedesmal, wenn man ein Objekt mittels new erzeugt muss man es auch mit delete wieder frei geben. Die ganzen Besonderheiten dabei (verschiedenes Behandeln von Objekten auf dem Heap und auf dem Stack, Freigabe von Objekten an versch. Scope-Grenzen, Verwendung und Freigabe von Objekten in assoziativen Containern, Kopieren von Objekten bei Methodenaufrufen, untersch. Verhalten von statischen vs. nicht-statischen Klassen... usw. usf.) machen mir als C++-Anfänger gerade das Leben schwer! Dort liegt momentan auch der Hund begraben. Es werden wohl nicht für alle mit new angelegten Objekte delete gerufen (v.a. für solche, die in den Containern verwaltet werden), dafür werden einige wohl noch doppelt freigegeben (die ich noch nicht gefunden habe). Nun bin ich da gerade am Ausmisten.
Komme allerdings so selten zum proggen, dass ich die meisten der mal angeeigneten Grundlagen von C++ zwischenzeitlich immer wieder vergesse!

[ Dieser Beitrag wurde von Reth am 28.02.2012 um 20:36 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

29.02.2012, 11:02 Uhr

_PAB_
Posts: 3016
Nutzer
@Reth:
Mir bringt es immer extrem viel, mit Debuggern zu arbeiten, so zum Beispiel gdb. Da kann man sein Programm ausführen lassen und (sollte) vor einem Speicherzugriffsfehler eine Meldung vom Debugger bekommen.

Außerdem ist es sehr nützlich, nach jedem schreiben in die Logdatei einen "flush" auszuführen, sodass man sicher sein kann, dass der letzte Eintrag auch wirklich der letzte ist - und vor allem, dass der zuletzt geschriebe Eintrag nicht noch im Puffer liegt, wenn das Programm abbricht.


[ Dieser Beitrag wurde von _PAB_ am 29.02.2012 um 11:02 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

29.02.2012, 21:24 Uhr

Reth
Posts: 1858
Nutzer
@_PAB_:
Der steht bei mir auch noch auf der ToDo-Liste (zunächst möchte ich mal vernünftiges Logging einbauen, derzeit läuft alles nur über std::out).

Leider bin ich von früher von graphischen, integrierten Debuggern verwöhnt (HiSoftC++ war hierbei ein Traum!). Daher muss ich mich erst einmal in die Shell-Bedienung des GDB einarbeiten (nutze GCC unter AOS4 mit Codebench).

Leider bleibt für alles wie immer viel zu wenig Zeit!

[ - Antworten - Zitieren - Direktlink - ]

21.06.2012, 15:03 Uhr

Reth
Posts: 1858
Nutzer
Also ich komme da bisher nicht weiter. Alle Logausgaben, (De-)Aktivierungen von Aufräumarbeiten etc. haben keinerlei Veränderungen gebracht!

Leider steht auch kein funktionierender Debugger zur Verfügung bzw. vielleicht raff ich auch den GDB nicht! Einige Breakpoints werden gar nicht erst erkannt, und wenn er mal anhält, dann rennt er nach Drücken von s oder n gleich ohne Pause weiter bis zum Schluss! Auch sagt er immer, dass er die entspr. Sourcedatei nicht gefunden hat, setzt den Breakpoint dennoch an die entsprechende Stelle und hält dann auch dort (zumindest bei einigen Breakpoints, bei anderen im selben File nicht)!
Beim db101 hab ich noch nicht rausbekommen, wie ich Sourcedateien reinladen kann und wie ich Breakpoints an beliebigen Zeilen setzen kann!

Bleibt nur noch eine Logausgabe in alle Destruktoren aller meiner Klassen zu schreiben, damit ich weiss, welche gerade in Arbeit ist, wenn der Fehler auftritt! Man das ist ne ganz schön mühsame Quälerei, wenn man sonst überall im Programmierumfeld so verwöhnt ist! Komme mit der eigentlichen Aufgabe überhaupt nicht voran, da ich Ewigkeiten mit meiner ineffizienten Fehlersuche beschäftigt bin!

[ - Antworten - Zitieren - Direktlink - ]

21.06.2012, 15:35 Uhr

Thore
Posts: 2266
Nutzer
Du gibst ja Speicher doppelt frei, vermutlich. Kannst Du bei jedem Free und Alloc eine Logausgabe machen, und die dann man "gegenrechnen" obs aufgeht, und welches Free zu viel da ist?
Die Debugs dann am besten in eine Log-Datei speichern und nicht als CLI Ausgabe. Wenns crasht kannst du das Log dann auch nach dem Reboot noch lesen.

[ - Antworten - Zitieren - Direktlink - ]

21.06.2012, 18:30 Uhr

Reth
Posts: 1858
Nutzer
@Thore:
Hab kein Alloc/Free, "nur" new/delete und dabei noch zu beachten, wann Objekte freigegeben werden (scope) und ob ich das nicht zufällig doppelt mache, wenn ich sie in untersch. Vektoren/Maps/etc. ablege.
Letzteres hab ich erst einmal ausgeschlossen, soweit ich das bisher im Source nachvollziehen konnte.

Hätte den Code gern einfacher und übersichtlicher (evtl. isser das ja schon), hab aber noch keine gute Handhabe gefunden. v.a. für die Dinge, die von unterschiedlichen Komponenten an verschiedenen Stellen benötigt werden.

[ - Antworten - Zitieren - Direktlink - ]

21.06.2012, 18:39 Uhr

Thore
Posts: 2266
Nutzer
Würdest Du den Code weitergeben, der mal drüberschauen kann? Kannst mir gerne mal schicken, dann werf ich mal nen Blick drauf, vll seh ichs zufälligerweise.
E-Mail hast Du. Und wenn Du dem zustimmst, dein Code wird selbstverständlich nicht weitergegeben.

[ - Antworten - Zitieren - Direktlink - ]

21.06.2012, 19:44 Uhr

Reth
Posts: 1858
Nutzer
@Thore:
Danke für das Hilfsangebot! Mail ist raus!

[ - Antworten - Zitieren - Direktlink - ]

22.06.2012, 11:29 Uhr

Holger
Posts: 8116
Nutzer
Zitat:
Original von Reth:
@Thore:
Hab kein Alloc/Free, "nur" new/delete…

Man kann auch die Operatoren für new und delete überschreiben und eine Überwachung hinzufügen.

--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

22.06.2012, 13:25 Uhr

Thore
Posts: 2266
Nutzer
Mit alloc und free meinte ich generell das Erstellen und Löschen von Objekten, also grundsätzlich.
Das Überladen könnte dann sogar hilfreich sein.
Allerdings hab ich den Code vorliegen und hab Reth mal ein paar Tips gegeben.
Ich gebe vorerst keine Details zum Programmcode bekannt, nur soviel: Mit ganz geringfügigen Änderungen ist es auch auf MorphOS compilierbar und ausführbar. Das hat mir die Möglichkeit gegeben, den Code nochmal ganz genau anzuschauen.
Bin gespannt ob die Tips das Problem beheben.

[ - Antworten - Zitieren - Direktlink - ]

28.07.2012, 11:53 Uhr

Reth
Posts: 1858
Nutzer
Hallo zusammen,

habe nun die Stelle gefunden, die, wenn ich sie ändere das Problem behebt (laut Thore ist es wohl unter MOS nicht so).
Allerdings habe ich keine Ahnung wieso der Fehler in der einen Konstellation auftritt und in der anderen nicht. Das möchte ich aber unbedingt heraus finden, um zu verstehen, was ich falsch mache.
Leider hab ich keine Ahnung, wie ich das hier verdeutlichen soll, ohne seitenlange Sourcecode-Postings zu machen!

Kurz gesprochen habe ich im Konstruktor (in dem quasi das ganze Spiel abläuft) meiner Hauptklasse (die in main() erzeugt wird) das Problem, dass ich mehrere Objekte erzeuge, die am Ende des Konstruktors automatisch wieder freigegeben werden. Einige davon sind quasi "Observer", die an meine Fensterklasse gehangen und bei entsprechenden Ereignissen informiert werden (glaub schon mal in anderen Threads beschrieben). Diese Observer leiten selbst nochmal von Basisklassen ab.
Wenn ich nun alle Observer erzeuge (die eigentlich nix miteinader zu tun haben und sich auch keine Ressourcen teilen), dann bekomme ich das Freigabeproblem am Ende. Lasse ich einen von den beiden Observern für Mausmessages oder Gadgetmessages weg, bekomme ich das Problem nicht!
Das ist mir völlig schleierhaft!
Ich poste später noch den Codeauszug des Konstruktors, in dem das Ganze passiert und füge ihn in diesen Beitrag ein (geht leider gerade nicht).

So hier wie besprochen der Inhalt des Konstruktors ohne Logausgaben mit Kommentaren, was so passiert:
C++ code:
{
        // zunächst wird eine "Aktivität" eingerichtet, die beim Eintreten von INTUITICKS ausgelöst werden soll. Die Ticks werden über einen entsprechenden Empfänger am Fenster entgegen genommen
	RefreshActivityC refrAct(win);
	inTickMsgRec.addIntuiTickActivity(&refrAct);
	win.setIntuiTickMessageReceiver(&inTickMsgRec);

    // nun werden 2 Gadgets mit Bildern erzeugt und im Fenster dargestellt
    ImageButtonC towerButton(&tower_button_normal, &tower_button_pressed, BUILDTOWER);
	ImageButtonC lightningButton(&lightning_button_normal, &lightning_button_pressed, ATTACK);

	win.addGadgetAt(towerButton, 829, 573, 29, 52);
	win.addGadgetAt(lightningButton, 874, 573, 29, 52);

	win.getRastPort().drawImageAt(&statusimage, 808, 15);
	win.getRastPort().drawImageAt(&buttonimage, 808, 552);

	win.refreshAllGadgets();

	win.getRastPort().setPrimaryPen(1);
	win.setTextFont((char *)"EstroFont.font", 18, 0, 0);

    srand(time(NULL));

        // Spielfeld usw. erstellen 
	setupWindowComponents(win);

      // Weitere benötigte Empfänger erzeugen
      // A C H T U N G ! ! !
      // sobald hier GadgetMessageReceiver und MouseMessageReceiver gemeinsam erzeugt werden bekomme ich die Alerts mit doppelter Speicherfreigeba beim Beenden, auch wenn danach in dieser Methode alles auskommentiert ist!!!
     // Andererseits läuft alles glatt durch, sobald ich im Konstruktor nur die beiden Zeilen mit addGadgetAt auskommentiere!
     // Daher vermute ich, dass es an meinen Gadgets bzw. deren Behandlung und dem ganzen Freigabegedöns von Intuition, Gadgets etc. liegt (ist für mich alles noch viel zu undurchsichtig!)! Allerdings macht dann die Konstellation der aktivierten Zeilen im Zshg. mit der Tatsache, wann der Fehler erscheint und wann nicht absolut keinen Sinn!!! Entweder die Gadgets nicht ins Fenster übernehmen, dann kann ich beide Receiver erstellen, oder die Gadgets übernehmen, dafür nur einen der beiden Receiver anlegen (wohlgemerkt: Nur anlegen, nicht ins Fenster hängen und egal welchen der beiden)! Das ist doch komplett undurchsichtig, kann ich nicht nachvollziehen! Ihr vielleicht???
//    GadgetMessageReceiverC gadMsgRec;
//    MouseMessageReceiverC mouseMsgRec(*cursor);
...


Hier mal noch die Header der beiden Receiverklassen, obwohl das bestimmt nicht viel helfen wird:
C++ code:
#ifndef GADGETMESSREC_H
#define GADGETMESSREC_H

#include <intuition/intuition.h>
#include "IntuiMessageReceiverC.h"

class GadgetMessageReceiverC : public IntuiMessageReceiverC
{
	public:
        GadgetMessageReceiverC();
        ~GadgetMessageReceiverC();

        void receive(IntuiMessageC &intuiMessage);
};
#endif


#ifndef MOUSEMESSREC_H
#define MOUSEMESSREC_H

#include <intuition/intuition.h>

#include "PointC.h"
#include "CursorC.h"
#include "AnimObjectC.h"
#include "RefreshActivityC.h"
#include "IntuiMessageReceiverC.h"
#include "HexManagerC.h"

class MouseMessageReceiverC : public IntuiMessageReceiverC
{
	public:
        MouseMessageReceiverC(CursorC& cursor);
        ~MouseMessageReceiverC();
        
        void receive(IntuiMessageC &intuiMessage);
        void mouseMoved(const PointC& newPos);
        void mouseLeftClicked();
        
    private:
    	CursorC& cursr;
};
#endif


Bin für Meinungen, Vorschläge und Hinweise offen und dankbar! Da ich absoluter C++-Neuling (immer noch) bin, habt bitte Nachsicht, wenn nicht alles mit den für C++ optimalen Möglichkeiten umgesetzt wurde (mach gerade vieles noch im "Java-Style")!

[ Dieser Beitrag wurde von Reth am 28.07.2012 um 20:46 Uhr geändert. ]

[ Dieser Beitrag wurde von Reth am 28.07.2012 um 21:08 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

30.07.2012, 09:47 Uhr

Thore
Posts: 2266
Nutzer
Erstell die Receiver mal mit new zu Beginn und lösche sie mit delete in der hierarchisch richtigen Reihenfolge, bevor die Klassen beendet und Libs geschlossen werden, die diese Klassen noch brauchen. Ich denk die benutzen noch etwas, das bereits freigegeben wurde.

[ - Antworten - Zitieren - Direktlink - ]

30.07.2012, 16:43 Uhr

Holger
Posts: 8116
Nutzer
@Reth:
Es ist nicht sinnvoll, die Anwendung komplett im Konstruktor laufen zu lassen. Das führt schon allein deshalb zu Verwirrungen, weil ich Deinen Satz: „sobald hier GadgetMessageReceiver und MouseMessageReceiver gemeinsam erzeugt werden bekomme ich die Alerts mit doppelter Speicherfreigabe beim Beenden, auch wenn danach in dieser Methode alles auskommentiert ist“ nicht mehr verstehen kann.

Wenn Du in dieser „Methode“ alles danach auskommentierst, redest Du gerade von dem Konstruktor, in dem eigentlich die ganze Anwendung läuft, also dann nicht läuft, weil sie auskommentiert ist?

Ein logischer Fehler, der sich hier durchzieht, ist, dass Du einen Haufen Objekte im Stackframe des Konstruktors erzeugst, die genau dann freigegeben werden, wenn die „Konstruktion“ abgeschlossen ist.

Was beim geposteten Code fehlt, ist die Basisklasse IntuiMessageReceiverC und der Code, der die Art und Weise zeigt, wie diese Observer verwaltet werden. Wenn allerdings das bloße Anlegen eines trivialen Objekts zu einer solchen Verhaltensänderung führt, liegt oftmals der Fehler ganz woanders und wird nur jetzt erst sichtbar.

Da Du soviel Zeug auf dem Stack erzeugst: ist der denn auch groß genug?

--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

30.07.2012, 21:34 Uhr

Reth
Posts: 1858
Nutzer
Ich vermute, dass es an meinem Gadgethandling liegt, da wie geschrieben alle Receiver funktionieren, solange ich die AddGadgetAt()-Methoden weglasse (Beschreibungsstelle ist im Kommentar versteckt). Wenn ich die AddGadgetAt()-Methoden aktiviert lasse, dann muss ich einen der beiden Receiver-Konstruktor auskommentieren, damit ich keine Fehler bekomme.

@Thore:
Also diesen Umbau möchte ich noch ein wenig hinaus zögern, da der MouseMessageReceiver noch ein Cursorobjekt benötigt, das selbst wiederum ein paar andere Objekte braucht usw. D.h, das wäre ein sehr großer Umbau!

@Holger:
Hier der IntuiMessageReceiver (habe die Einrückungen der Codebsps. nicht korrigiert, die werden beim Copy&Paste so komisch übernommen, obwohl im IDE-Editor angegeben ist, dass Spaces statt Tabs verwendet werden sollen, ist wohl an manchen noch sehr alten Codestellen noch nicht der Fall):
C++ code:
#ifndef INTUIMESSREC_H
#define INTUIMESSREC_H

#include "IntuiMessageC.h"

class IntuiMessageReceiverC
{
	public:
        IntuiMessageReceiverC();
        virtual ~IntuiMessageReceiverC() = 0;

	protected:
        virtual void receive(IntuiMessageC &intuiMessage) = 0;
};
#endif

...

#include "IntuiMessageReceiverC.h"

IntuiMessageReceiverC::IntuiMessageReceiverC() {}

IntuiMessageReceiverC::~IntuiMessageReceiverC() {}


Hier noch die Verwaltung der Receiver (gekürzt):
C++ code:
void WindowC::setMenuMessageReceiver(MenuMessageReceiverC *recv)
{
    mnMsgRec = recv;
}

void WindowC::setMouseMessageReceiver(MouseMessageReceiverC *recv)
{
    msMsgRec = recv;
}

void WindowC::setGadgetMessageReceiver(GadgetMessageReceiverC *recv)
{
    gadMsgRec = recv;
}

void WindowC::setIntuiTickMessageReceiver(IntuiTickMessageReceiverC *recv)
{
	intuiTickMsgRec = recv;
}

...

void WindowC::processMessages()
{
	if (getUserPort() == NULL) return;

    while (!closing)
    {
		Wait(1 << getUserPort()->mp_SigBit);

		while (struct IntuiMessage *my_message = (struct IntuiMessage *)GetMsg(getUserPort()))
        {
            IntuiMessageC intuiMessage(my_message);
            ReplyMsg((struct Message *)my_message);

            if (closing) break;

            switch (intuiMessage.getClass())
            {
                case IDCMP_MENUPICK:
                    if (mnMsgRec != NULL) mnMsgRec->receive(intuiMessage);
                    break;

                case IDCMP_GADGETDOWN:
                case IDCMP_GADGETUP:
                	if (gadMsgRec != NULL) gadMsgRec->receive(intuiMessage);
                	break;

                case IDCMP_INTUITICKS:
					if (intuiTickMsgRec != NULL) intuiTickMsgRec->receive(intuiMessage);
                    break;

                case IDCMP_MOUSEMOVE:
                	if (msMsgRec != NULL) msMsgRec->mouseMoved(PointC(intuiMessage.getMouseX(), intuiMessage.getMouseY()));
                	break;
                	
                case IDCMP_MOUSEBUTTONS:
                	if (msMsgRec != NULL) msMsgRec->receive(intuiMessage);
                    break;

                case IDCMP_REFRESHWINDOW:
                	beginRefresh();
                	endRefresh();
					break;
					
                default:
                    break;
            }
        }
    }
}


Also hier mal der ganze Konstruktor in der ersten lauffähigen Variante (habe den Code umgestellt, so dass alle Gadget-relevanten Sachen zusammen passieren):
C++ code:
WizardGroundsC::WizardGroundsC(WindowC& window) : hexManager(NULL), cursor(NULL), playerManager(NULL), win(window), inTickMsgRec()
{
	RefreshActivityC refrAct(win);
	inTickMsgRec.addIntuiTickActivity(&refrAct);
	win.setIntuiTickMessageReceiver(&inTickMsgRec);

	win.getRastPort().drawImageAt(&statusimage, 808, 15);
	win.getRastPort().drawImageAt(&buttonimage, 808, 552);

	win.getRastPort().setPrimaryPen(1);
	win.setTextFont((char *)"EstroFont.font", 18, 0, 0);

       srand(time(NULL));
       setupWindowComponents(win);

       //GadgetMessageReceiverC gadMsgRec;
       MouseMessageReceiverC mouseMsgRec(*cursor);
       MenuMessageReceiverC mnMsgRec(win.getMenu());

       win.setMenuMessageReceiver(&mnMsgRec);
       win.setGadgetMessageReceiver(&gadMsgRec);
       win.setIntuiTickMessageReceiver(&inTickMsgRec);
       win.setMouseMessageReceiver(&mouseMsgRec);

       ImageButtonC towerButton(&tower_button_normal, &tower_button_pressed, BUILDTOWER);
	ImageButtonC lightningButton(&lightning_button_normal, &lightning_button_pressed, ATTACK);

        TowerGadgetActivityC tgActivity(*cursor, *playerManager->getPlayer(HUMAN));
        towerButton.setReleaseActivity(&tgActivity);
        LightningGadgetActivityC lnActivity(*cursor, *playerManager->getPlayer(HUMAN));
        lightningButton.setReleaseActivity(&lnActivity);

	win.addGadgetAt(towerButton, 829, 573, 29, 52);
	win.addGadgetAt(lightningButton, 874, 573, 29, 52);
	win.refreshAllGadgets();

	win.processMessages();
}


Und hier in der zweiten lauffähigen Variante (nur geänderte Stellen wiedergegeben - GadgetMessageReceiver aktiviert, MouseMessageReceiver deaktiviert):
C++ code:
...
       GadgetMessageReceiverC gadMsgRec;
       //MouseMessageReceiverC mouseMsgRec(*cursor);
 ...


Hier die 3. lauffähige Variante (wieder nur Änderungen - beide Receiver aktiviert, Gadgetnutzung deaktiviert):
C++ code:
...
       GadgetMessageReceiverC gadMsgRec;
       MouseMessageReceiverC mouseMsgRec(*cursor);
...
	//win.addGadgetAt(towerButton, 829, 573, 29, 52);
	//win.addGadgetAt(lightningButton, 874, 573, 29, 52);
...


Hier der Vollständigkeit halber noch die Methode AddGadgetAt(). Dummerweise heissen das Objekt und seine struct Gadget leider gleich. Zudem werden die Gadgets erst nach dem Öffnen des Fensters gesetzt (bei der Beschreibung von AddGadget() konnte nichts finden, dass dies nicht erlaubt wäre). Zudem hat das Fenster beim Öffnen kein WA_Gadgets Tag gesetzt:
C++ code:
void WindowC::addGadgetAt(GadgetC& gadget, WORD xPos, WORD yPos, WORD width, WORD height)
{
    if (window)
    {
        gadget.gadget.LeftEdge = xPos;
        gadget.gadget.TopEdge = yPos;
        gadget.gadget.Width = width;
        gadget.gadget.Height = height;

        UWORD pos = AddGadget(window, &(gadget.gadget), -1);

        if (first == NULL)
        {
            last = first = &(gadget.gadget);
        } else
        {
            last = &(gadget.gadget);
        }
    }
}


Fürchte nur, dass das Posten des ganzen Codes hier wenig hilft, da alle anderen Klassen und Methoden, die hier genutzt werden ja nicht mitgepostet sind. Wenn ich das machen wöllte kann ich gleich meinen ganzen Source online stellen.

[ Dieser Beitrag wurde von Reth am 30.07.2012 um 21:46 Uhr geändert. ]

[ - Antworten - Zitieren - Direktlink - ]

31.07.2012, 08:37 Uhr

Thore
Posts: 2266
Nutzer
Eine Vermutung:
Du erstellst Objekte statisch, z.B. mit WindowC win(...);
Dadurch wird es erst freigegeben, wenn die Routine beendet wird.
Deine Libs werden aber schon vorher geschlossen. Wird die Lib noch benutzt, obwohl sie geschlossen ist, crashts. Gilt auch für andere Objekte und Handler, die schon geschlossen sind, aber noch von existierenden Objekten benutzt werden.
Deshalb würde ich die Objekte mit new erzeugen, und die Reihenfolge beim delete umdrehen. Grobes Schema, Pseudocode:
OpenLibs();
c1 = new Class1C(..);
c2 = new Class2C(..);
WaitForClose();
delete c2;
delete c1;
CloseLibs();

Hab Dir doch schonmal ein Beispiel geschickt, wie es mit C++ möglich ist, ohne daß die Klassen Amok laufen ;)

[ - Antworten - Zitieren - Direktlink - ]

31.07.2012, 11:59 Uhr

Holger
Posts: 8116
Nutzer
Zitat:
Original von Reth:
virtual ~IntuiMessageReceiverC() = 0;

Ein abstrakter Destruktor?

Das klingt für mich ziemlich abenteuerlich.

Ich würde das =0 schleunigst entfernen.

Außerdem würde ich ohnehin bei Klassen, die als abstrakte Klassen im Sinne von interfaces gedacht sind, die No-Ops inlinen:
C++ code:
#ifndef INTUIMESSREC_H
#define INTUIMESSREC_H

#include "IntuiMessageC.h"

class IntuiMessageReceiverC
{
	public:
        IntuiMessageReceiverC() {}
        virtual ~IntuiMessageReceiverC() {}

	protected:
        virtual void receive(IntuiMessageC &intuiMessage) = 0;
};
#endif


Zitat:
Original von Thore:
Eine Vermutung:
Du erstellst Objekte statisch, z.B. mit WindowC win(...);
Dadurch wird es erst freigegeben, wenn die Routine beendet wird.
Deine Libs werden aber schon vorher geschlossen.

Weißt Du mehr als ich? Ich sehe nirgendwo Aufrufe zum Schließen von Libs.
Zitat:
Deshalb würde ich die Objekte mit new erzeugen, und die Reihenfolge beim delete umdrehen. Grobes Schema, Pseudocode:
OpenLibs();
c1 = new Class1C(..);
c2 = new Class2C(..);
WaitForClose();
delete c2;
delete c1;
CloseLibs();

Unnötig komliziert. Einfach den Scope anpassen, würde hier vollkommen reichen:

OpenLibs();
{
  Class1C c1(..);
  Class2C c2(..);
  WaitForClose();
}
CloseLibs();

--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

31.07.2012, 13:04 Uhr

Thore
Posts: 2266
Nutzer
> Weißt Du mehr als ich?
Ja ich hab den kompletten Source

[ - Antworten - Zitieren - Direktlink - ]

31.07.2012, 14:46 Uhr

Reth
Posts: 1858
Nutzer
Hi zusammen,

@Thore:
Das ist schon so umgestellt (s. meine letzte Version der Hauptklasse in einer meiner letzten Mails). Die libs werden außerhalb des Scopes geöffnet und geschlossen, in dem das Hauptobjekt gültig ist. Quasi so wie in Holgers Bsp.

@Holger:
Bei Bedarf kann ich Dir den Code gern auch komplett zukommen lassen.
Insgesamt hab ich noch kein "richtiges Gefühl" dafür, wann man am besten was inline anlegt und wann nicht. In der Lektüre habe ich nachgeschlagen, dass man vollständig abstrakte Klassen mit virtuellen Destruktoren erstellen kann. Daher der virtuelle Konstruktor an dieser Stelle.

Habt ihr noch ne Idee, wo ich ansetzen könnte um diese komische Konstellation (hinsichtlich funktionieren/nicht funktionieren) zu testen? Für mich ergibt das immer noch keinen Sinn, dass mal der eine, mal der andere Receiver und mal das Hinzufügen der Gadgets weggelassen werden kann und dann alles gut zu sein scheint?
Ein funktionierender Debugger hätte hier schon etliche Stunden eingespart!

[ - Antworten - Zitieren - Direktlink - ]

01.08.2012, 10:58 Uhr

Holger
Posts: 8116
Nutzer
Zitat:
Original von Reth:
In der Lektüre habe ich nachgeschlagen, dass man vollständig abstrakte Klassen mit virtuellen Destruktoren erstellen kann. Daher der virtuelle Konstruktor an dieser Stelle.

Nicht das "virtual" stört mich, sondern das "=0". Das ist das äquivalent zu "abstract" in Java, nur dass Dich bei C++ der Compiler nicht daran hindert, eine Instanz einer Klasse mit solchen Methoden zu erzeugen. Und da ein Destruktor normalerweise immer den Destruktor der Superklasse aufruft, die Unterklassen also den aus ihrer Sicht nicht existierenden Destruktor aufrufen, klingt das für mich nach einem ziemlich groben Fehler.

Dann hast Du allerdings in einer anderen Datei eine Implementierung IntuiMessageReceiverC::~IntuiMessageReceiverC() {}, was im Widerspruch zur Deklaration der Methode als abstrakt steht (gibt es da keine Warnung?). Und damit sind wir beim wunderschönen „Modulkonzept“ von C/C++. Wer "IntuiMessageReceiverC.h" einbindet, ohne die andere Datei mit der Implementierung zu kennen, nimmt etwas anderes an, als die Implementierung. Aber man kann solch inkompatible Sachen durchaus verlinken.

Zitat:
Habt ihr noch ne Idee, wo ich ansetzen könnte um diese komische Konstellation (hinsichtlich funktionieren/nicht funktionieren) zu testen? Für mich ergibt das immer noch keinen Sinn, dass mal der eine, mal der andere Receiver und mal das Hinzufügen der Gadgets weggelassen werden kann und dann alles gut zu sein scheint?
Die Auswirkungen von Fehlern müssen nicht logisch erscheinen. Es ist nur ein Indiz dafür, dass Du bei Deiner Suche momentan noch sehr weit von der eigentlichen Stelle entfernt sein kannst.
Zitat:
Ein funktionierender Debugger hätte hier schon etliche Stunden eingespart!
Vielleicht. Man kann auch mit Debugger den Wald vor lauter Bäumen nicht sehen.

--
Good coders do not comment. What was hard to write should be hard to read too.

[ - Antworten - Zitieren - Direktlink - ]

01.08.2012, 12:05 Uhr

Reth
Posts: 1858
Nutzer
[quote]
Original von Holger:
Zitat:
Nicht das "virtual" stört mich, sondern das "=0". Das ist das äquivalent zu "abstract" in Java, nur dass Dich bei C++ der Compiler nicht daran hindert, eine Instanz einer Klasse mit solchen Methoden zu erzeugen. Und da ein Destruktor normalerweise immer den Destruktor der Superklasse aufruft, die Unterklassen also den aus ihrer Sicht nicht existierenden Destruktor aufrufen, klingt das für mich nach einem ziemlich groben Fehler.

Dann hast Du allerdings in einer anderen Datei eine Implementierung IntuiMessageReceiverC::~IntuiMessageReceiverC() {}, was im Widerspruch zur Deklaration der Methode als abstrakt steht (gibt es da keine Warnung?).

Nein, kommt nicht (compiliere mit -Wall). Das ganze Konstrukt mit abstrakten Basisklassen einschl. leerer Implementierung hab ich aus einschlägiger Literatur (z.B. Thinking in C+ und C++ Primer) und Foren erstellt. Daher bin ich davon ausgegangen, dass dies so passt (oder hab hier irgendwo was falsch verstanden).

Kann Dir wie gesagt den Source gern mal schicken, dann kannst Du ihn bei Dir mal durch die "Toolchain" laufen lassen. Vielleicht hast Du ja noch andere Einstellungen beim Compiler/Linker, so dass Du ein paar mehr Hinweise bekommst?

[ - Antworten - Zitieren - Direktlink - ]


-1- 2 [ - Beitrag schreiben - ]


amiga-news.de Forum > Programmierung > Wie recoverable alert unter AOS4.1 debuggen? [ - Suche - Neue Beiträge - Registrieren - Login - ]


.
Impressum | Datenschutzerklärung | Netiquette | Werbung | Kontakt
Copyright © 1998-2024 by amiga-news.de - alle Rechte vorbehalten.
.