Probleem conext:
Zoals wel vaker ben ik weer behoorlijk aan het prutsen. Niet omdat het heel nuttig is, maar meer voor de lol en puur experimenteel ben bezig met een eigen exception hierarchy. Dus exceptions zonder gebruik te maken van de bestaande std::exception.
Misschien dat ik een heel simpel generiek probleem beschrijf in een veel te ingewikkeld of oninteressante context, het zij zo.
Exception oorzaak:
Indien er iets gebeurt in een programma wat niet de bedoeling is, kan een functie of methode er voor kiezen een exception te gooien om de aanroepende code hiervan op de hoogte brengen. Wat vaak gebeurt is dat de aanroepende code de exception afvangt en zelf ook weer een exception naar boven gooit. Meestal een wat oppervlakkigere exception. Dit kan handig zijn om ervoor te zorgen dat dat de aanroepende code niet wakker geschud wordt met nogal diepzinnige interne exception waar deze helemaal geen verstand van heeft.
Voorbeeldje in de context van een webserver:
Exception trace:
Maar soms is het toch handig om te weten waarom de webserver niet kon starten. Ik zou het handig vinden als je in een exception kon opslaan dat er een andere exception de oorzaak is van alle ellende. Je kan als het ware een exception stack opbouwen en dat mooi aan een gebruiker voorleggen.
Ik zat hier aan te denken:
My exception class + example usage:
Vroegtijdige elliminatie:
Maar dit gaat natuurlijk gruwelijk fout!!
Voornamelijk de constructor en de destructor van de my_exception class. Deze maken maken een nieuwe exception op de heap en laten dit een kopie zijn van de cause, welke gewoon op de stack leefde. Het probleem is hier dus een object wat op de stack leeft en een referentie heeft naar iets wat op de heap leeft.
Omdat er zoveel met deze exception wordt rond gezeuld, en te pas en te onpas kopieen op de stack worden gemaakt, blijft er (veel te vroegtijdig) niets meer over van de my_exception::cause attribute. Deze is vaak al lang in een voorgaande implicite kopie naar de verdoemenis geholpen met een call naar delete. Maar geen delete aanroepen in de destructor zal er voor zorgen dat ie NOOIT opgeruimd wordt, wat zal leiden tot een memory leak.
Eigen smerige suggesties:
De enige twee, nogal smerige, oplossingen die ik kon bedenken zijn het gooien van pointers naar exceptions ipv exceptions (maar dat wil niemand), of het gebruik maken van smart-pointers. De laatste lijkt mij ook geen elegante oplossing.
Heeft er iemand enig idee hoe ik dit probleem aan zou kunnen pakken?
Zoals wel vaker ben ik weer behoorlijk aan het prutsen. Niet omdat het heel nuttig is, maar meer voor de lol en puur experimenteel ben bezig met een eigen exception hierarchy. Dus exceptions zonder gebruik te maken van de bestaande std::exception.
Misschien dat ik een heel simpel generiek probleem beschrijf in een veel te ingewikkeld of oninteressante context, het zij zo.
Exception oorzaak:
Indien er iets gebeurt in een programma wat niet de bedoeling is, kan een functie of methode er voor kiezen een exception te gooien om de aanroepende code hiervan op de hoogte brengen. Wat vaak gebeurt is dat de aanroepende code de exception afvangt en zelf ook weer een exception naar boven gooit. Meestal een wat oppervlakkigere exception. Dit kan handig zijn om ervoor te zorgen dat dat de aanroepende code niet wakker geschud wordt met nogal diepzinnige interne exception waar deze helemaal geen verstand van heeft.
Voorbeeldje in de context van een webserver:
C++:
1
2
3
4
5
6
7
8
9
10
| void HTTP_server::start() { TCP_server s; try { s.bind(); } catch (TCP_bind_exception e) { throw HTTP_server_unable_to_start_exception(); } // serve some webpages } |
Exception trace:
Maar soms is het toch handig om te weten waarom de webserver niet kon starten. Ik zou het handig vinden als je in een exception kon opslaan dat er een andere exception de oorzaak is van alle ellende. Je kan als het ware een exception stack opbouwen en dat mooi aan een gebruiker voorleggen.
Ik zat hier aan te denken:
My exception class + example usage:
C++:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
| // ------------------------------------------------------ // MY_EXCEPTION DEFINITION // ------------------------------------------------------ class my_exception { protected: exception *cause; // MUST be a pointer public: // default, don't care about cause my_exception() : cause(NULL) {} // specify a cause exception my_exception(my_exception c) { // use copy-constructor cause = new my_exception(c); } ~my_exception() { delete cause; } } // ------------------------------------------------------ // EXAMPLE USAGE // ------------------------------------------------------ class TCP_bind_exception : my_exception {}; class HTTP_server_unable_to_start_exception : my_exception {}; void HTTP_server::start() { TCP_server s; try { s.bind(); } catch (TCP_bind_exception e) { throw HTTP_server_unable_to_start_exception(e); // save cause } // serve some webpages } try { HTTP_server s(80); s.start(); } catch (my_exception e) { my_exception *cur = &e; while (cur) { cout << cur->name() << endl; cout << cur->message() << endl; // some exception statistics cur = cur->get_cause(); } } |
Vroegtijdige elliminatie:
Maar dit gaat natuurlijk gruwelijk fout!!
Voornamelijk de constructor en de destructor van de my_exception class. Deze maken maken een nieuwe exception op de heap en laten dit een kopie zijn van de cause, welke gewoon op de stack leefde. Het probleem is hier dus een object wat op de stack leeft en een referentie heeft naar iets wat op de heap leeft.
Omdat er zoveel met deze exception wordt rond gezeuld, en te pas en te onpas kopieen op de stack worden gemaakt, blijft er (veel te vroegtijdig) niets meer over van de my_exception::cause attribute. Deze is vaak al lang in een voorgaande implicite kopie naar de verdoemenis geholpen met een call naar delete. Maar geen delete aanroepen in de destructor zal er voor zorgen dat ie NOOIT opgeruimd wordt, wat zal leiden tot een memory leak.
Eigen smerige suggesties:
De enige twee, nogal smerige, oplossingen die ik kon bedenken zijn het gooien van pointers naar exceptions ipv exceptions (maar dat wil niemand), of het gebruik maken van smart-pointers. De laatste lijkt mij ook geen elegante oplossing.
Heeft er iemand enig idee hoe ik dit probleem aan zou kunnen pakken?