Exception von Konstruktor fangen bei einem Objekt das ohne new erzeugt wird.



  • hustbaer schrieb:

    boost::optional ist eigentlich recht leichtgewichtig. "Embedded System" ist für mich kein Grund darauf zu verzichten.
    Und so lange du das Objekt nicht nachträglich nochmal zuweist liegt es bei boost::optional auch nicht auf dem Heap.

    Danke für den Hinweis. Habe mir kurz boost::optional angesehen. Klingt interessant.

    hustbaer schrieb:

    godi schrieb:

    Einstweilen werde ich mal bei der oben geposteten Lösung bleiben, dass das Programm terminiert, da der Fall, dass die Konstruktion eines solchen Objektes schiefgeht, doch gering ist.

    Wenn diese Lösung akzeptabel ist, dann ist das sicher das einfachste.

    Ja ist akzeptabel.

    hustbaer schrieb:

    godi schrieb:

    unique_ptr wäre eine Möglichkeit, jedoch hätte ich die Objekte lieber am Stack als im Heap.

    Warum?

    Den verbrauchten Speicherplatz am Stack kann ich gleich nach dem Kompilieren sehen. Den verwendeten Speicherplatz am Heap bekomme ich erst zur Laufzeit und vor allem wenn der Heap voll ist, dann knallts. 😉

    Zur Verwaltung vom Heap verwende ich von freeRTOS heap_4.
    Die ganzen Operatoren wie new, new[], delete, delete[], malloc, calloc und free habe ich überschrieben und auf den heap_4 umgeleitet.


  • Mod

    godi schrieb:

    hustbaer schrieb:

    godi schrieb:

    unique_ptr wäre eine Möglichkeit, jedoch hätte ich die Objekte lieber am Stack als im Heap.

    Warum?

    Den verbrauchten Speicherplatz am Stack kann ich gleich nach dem Kompilieren sehen.

    Wie?

    Den verwendeten Speicherplatz am Heap bekomme ich erst zur Laufzeit und vor allem wenn der Heap voll ist, dann knallts. 😉

    Und wenn der Stack voll ist?

    Verwechselst du eventuell gerade die beiden Begriffe?



  • Mir ist da noch etwas unklar bei den Exceptions.
    Und zwar habe ich gelesen, dass Exceptions am besten "throw by value, catch by reference" übergeben werden. Dies bedeutet, dass beim werfen eine Kopie angelegt wird und beim fangen eine Referenz auf die Kopie entgegengenommen wird.
    Was passiert wenn die Exception weitergeworfen wird? (Wie in dem Fall oben.)
    Wird da wieder eine Kopie erzeugt?

    Damit keine Kopien erstellt werden, habe ich mir überlegt, die Exceptions per Pointer zu werfen und fangen. Dazu habe ich im private Bereich der Klasse, die die Exception wirft, ein Objekt der Exception angelegt. (Siehe Bsp unten.) Den Pointer auf dieses Objekt übergebe ich dann bei throw. Jedoch habe ich gelesen, wenn aus einem Block herausgesprungen wird, wird ein "stack unwinding" durchgeführt. Also die bis zu throw erzeugten Objekte wieder vom Stack entfernt.
    Dadurch stellt sich mir die Frage, was mit dem Exception Objekt passiert. Wird dies auch entfernt?

    Hier mein Beispielcode: (Der bei mir funktioniert, wobei ich nicht weiß ob das zufällig ist oder doch richtig ist.)

    #include <iostream>
    #include <string>
    
    class MyException : public std::exception {
    private:
    	std::string exception;
    public:
    	/** Delete the copyconstructor to make the object of this class noncopyable */
    	MyException(const MyException&) = delete;
    	/** Delete the operator = to make the object of this class noncopyable */
    	MyException& operator=(const MyException&) = delete;
    
    	MyException(const std::string exception) : exception{exception} {};
    	virtual ~MyException(){};
    
    	virtual const char* what() const noexcept override {
    		return exception.c_str();
    	}
    
    	void setException(const std::string exception) {
    		this->exception = exception;
    	}
    };
    
    class MyClass {
    private:
    	MyException exception{"Test my exception"};
    
    public:
    	MyClass() try {
    		throw &exception;
    	} catch (MyException *e) {
    		std::cerr << e->what() << std::endl;
    		e->setException("Exception changed!");
    		std::cerr << e->what() << std::endl;
    		exception.setException("Original exception was thrown.");
    		std::cerr << e->what() << std::endl;
    	}
    
    	virtual ~MyClass(){};
    };
    
    class MainClass {
    private:
    	MyClass myClass{};
    
    public:
    	MainClass() {
    		std::cout << "Run" << std::endl;
    	};
    
    	virtual ~MainClass(){};
    };
    
    int main() {
    	try {
    		MainClass mainClass{};
    	} catch (std::exception *e) {
    		std::cerr << e->what() << std::endl;
    	} catch (...) {
    		std::cerr << "Error!" << std::endl;
    	}
    }
    

    Die Ausgabe ist bei mir:

    Test my exception
    Exception changed!
    Original exception was thrown.
    Original exception was thrown.



  • SeppJ schrieb:

    godi schrieb:

    Den verbrauchten Speicherplatz am Stack kann ich gleich nach dem Kompilieren sehen.

    Wie?

    Zeigt mir meine IDE an. (Atmel Studio)

    SeppJ schrieb:

    godi schrieb:

    Den verwendeten Speicherplatz am Heap bekomme ich erst zur Laufzeit und vor allem wenn der Heap voll ist, dann knallts. 😉

    Und wenn der Stack voll ist?

    Dann weiß ich das schon bevor ich das Programm ausführe.

    SeppJ schrieb:

    Verwechselst du eventuell gerade die beiden Begriffe?

    Bei einer Mikrocontrollerarchitektur, meist Harvard-Architektur, gibt es einen internen (S)RAM. Beim compilieren des Programmes, wird schon für jedes Objekt das nicht dynamisch erzeugt wird ein Speicherplatz reserviert. Das bezeichne ich als Stack. Bei genauerer Überlegung hast du natürlich recht und ich verwende den Begriff Stack falsch, da ja als Stack der Bereich bezeichnet wird, in dem die Rücksprungadressen usw gespeichert werden. Den Bereich den ich jetzt als Stack bezeichnet habe ist einfach der RAM. Ich habe dies deshalb als Stack bezeichnet, da schon bei der Kompilierung die Speicherbereiche, die benötigt werden, aufeinandergestapelt werden.

    Der Heap ist einfach ein anderer Bereich im RAM, in dem die dynamisch erzeugten Objekte abgelegt, bzw entfernt werden. In meinem Fall wird dieser Bereich von freeRTOS heap_4 verwaltet.

    Im linker Script sind dann die Speicherbereiche angegeben:

    /* Memory Spaces Definitions */
    MEMORY
    {
      rom (rx)  : ORIGIN = 0x00400000, LENGTH = 0x00080000
      ram (rwx) : ORIGIN = 0x20000000, LENGTH = 0x00020000
    }
    
    /* The stack size used by the application. NOTE: you need to adjust according to your application. */
    __stack_size__ = DEFINED(__stack_size__) ? __stack_size__ : 0x3000;
    __ram_end__ = ORIGIN(ram) + LENGTH(ram) - 4;
    

    Wobei der ROM der interne flash für den Programmcode ist und der RAM ein interner SRAM ist.

    Aber danke der Nachfrage. Somit sind bei mir auch Unklarheiten verschwunden. 🙂

    Edit:
    Habe gerade nachgesehen wie freeRTOS den Speicher für den Heap anfordert. Das ist einfach ein Array vom Typ unsigned char.



  • Ich glaube diese Frage kann ich mir jetzt, durch die letzte Antwort, auch selbst beantworten:

    Da ja Stack != RAM ist und für die Objekte die nicht dynamisch erzeugt werden, schon der Speicher reserviert ist, wird das nicht dynamisch erzeugte Objekt der Klasse MyException nicht zerstört und kann somit per Pointer übergeben werden.



  • Mit dynamisch und nicht dynamisch musst du aufpassen. Der Verbrauch den deine IDE anzeigt betrifft nur globale und static Variablen. Einfache lokale Variablen (also das was bei mir Stack heißt) werden NICHT erfasst. Das ist auch gar kein so einfaches Problem, nur anhand des Quelltextes herauszufinden wie viel Speicher die lokalen Variablen brauchen. Man denke nur mal an Rekursion.



  • godi schrieb:

    Dies bedeutet, dass beim werfen eine Kopie angelegt wird und beim fangen eine Referenz auf die Kopie entgegengenommen wird.
    Was passiert wenn die Exception weitergeworfen wird? (Wie in dem Fall oben.)
    Wird da wieder eine Kopie erzeugt?

    Soweit ich weiss nix, sofern du mit throw; weiterwirfst. Mit throw ex; wird dagegen sicher ne neue Kopie erzeugt. Kannste aber einfach ausprobieren, schreib nen printf o.Ä. in den Copy-Konstruktor deiner Exception-Klasse.

    Und lass dich bloss nicht auf so Quatsch ein wie Zeiger auf dynamisch erzeugte Exceptions zu werfen. Das nervt mich schon bei der MFC immer tierisch. Sobald du dann irgendwo mal catch (...) schreibst, ohne davor einen Handler für deine Exception-Zeiger zu haben, der brav delete macht, hast du ein potentielles Memory Leak. Bäh.



  • Ok, danke für eure Hinweise.

    Das übergeben einer Exception per Pointer im Konstruktor wie ich es oben beschrieben habe funktioniert nicht.
    Ich habe jetzt Testweise im Destruktor von MyException meinen Exception Text geändert und siehe da, das Objekt von MyException wird schon zerstört sobald die Exception geschmissen wird. Ist irgendwie auch klar, da ja der Konstruktor nicht vollständig erzeugt wurde somit wird der Destruktor aller Variablen des zu erzeugenden Objektes aufgerufen.
    Also muss zwingend die Exception kopiert werden, oder die Exception wird global angelegt.



  • godi schrieb:

    Also muss zwingend die Exception kopiert werden, oder die Exception wird global angelegt.

    🙂



  • 🙂
    Wollte ich noch dazuschreiben, global ist immer schlecht. 🙂


Anmelden zum Antworten