Witam
Mam metodę inicjalizującą która korzysta z jakiejśtam dostępnej free powszechnie używanej w świecie libki np. sd-busa (służącego do komunikacji z systememd w linuxie i obsługiwanymi przez niego procesami). Często w takich 3rd party libkach aby uzyskać dostęp do jakiejś funkcjonalności trzeba wywołać pod rząd szereg metod z ktorych każda może zwracać errorCode, zainicjować obiekt ktory należałoby zwolnić po zakończeniu programu lub w przypadku niepowodzenia. Często też te 3rd party libki nie korzystają z C++11/14 więc nie da się zastosować inteligentnych wskaźników. Tutaj przykład kodu:
void init()
{
Typ1* wskaDoBusa = NULL;
Typ2* wskDoEventa= NULL;
Typ3* wskDoSubskrypcji= NULL;
int r = openBus(&wskDoBusa);
if(r < 0) {zwolnijPamiec(&wskDoBusa, &wskDoEventa, &wskDoSubskrypcji), wyjdz z programu}
r = openEvent(&wskDoEventa)
if(r < 0) {zwolnijPamiec(...), wyjdz z programu}
r = powiążEventZBusem(&wskDoBusa, &wskDoEventa)
if(r < 0) {zwolnijPamiec(...), wyjdz z programu}
r = zasubskrybujSieNaCos(&wskDoBusa, &wskDoEventa, &wskDoSubskrypcji)
if(r < 0) {zwolnijPamiec(...), wyjdz z programu}
r = podlaczSieDoPetliMonitorującej(&wskDoBusa, &wskDoEventa, &wskDoSubskrypcji)
if(r < 0) {zwolnijPamiec(...), wyjdz z programu}
...i tak jeszcze z 2-3 metody by uzyskać dostęp do pewnej funkcjonalności
}
To co wyżej nie jest dobre bo może pojawić się jakiś delikwent który przy implementacji jakiegoś ficzera wymagającego dodania kolejnego ifa zapomni wywołać metodę zwalniającą pamięć, da tylko return i mamy memory leaka. Prawdziwy przykład z życia, w pracy ktoś tak zrobił i urządzenie po ok. 60 dniach się resetowało jako że brakło pamięci.
Inne rozwiązanie to w każdym z if-ów robić throw a następnie za ostatnią metodą łapać wyjątek i po bloku try{}catch{} wywołać 1 raz metodę zwolnijPamięć():
void init()
{
try{
wywołania metod jak wyżej z tym że robimy if(r<0) {throw wyjatek}
}
catch(...)
{}
zwolnijPamiec(...)
}
Inne jeszcze rozwiązanie to użycie goto:
void init()
{
...
// wywołania metod jak wyżej z tym że robimy if(r<0) {goto: finish}
...
finish:
zwolnijPamiec(...)
}
Ale goto jest powszechnie wyśmiewanym patternem.
Jaki sposób jest najbardziej optymalny tutaj?