2014-09-19 14 views
5

W moim interfejsie API mam małą hierarchię wyjątków, wyprowadzoną z std::exception. Mam klasę podstawową Exception, która zapewnia kod błędu, plik, linię i funkcję. Inne bardziej szczegółowe wyjątki pochodzą z Exception. Na przykład jedna klasa pochodna dodaje specyficzny dla platformy kod błędu, a także pole identyfikujące, która funkcja zwróciła kod błędu. To jest jak uproszczona wersja system_error, ale nie mogę używać funkcji C++ 11 (utknąłem przy pracy z VS2005 i bez Boost).Odłączanie wyjątków od formatowania logów

Muszę zalogować te wyjątki za pomocą mojej klasy logowania. Chcę, aby wyjątki były logowane w określonym formacie. Po przeczytaniu różnych forów online i przeczytaniu Boost's Error and Exception Handling Guidelines, nie sądzę, że funkcja what każdego wyjątku lub jakiejkolwiek innej funkcji wirtualnej w obrębie Exception jest odpowiednim miejscem do sformatowania wyjątku do rejestrowania. Dlatego moje funkcje what po prostu zwracają nazwę klasy.

Podczas wychwytywania wyjątków często chcę wychwycić bardzo ogólne wyjątki, zwykle std::exception, i przekazać je do rejestratora. Nie chcę wychwycić indywidualnych wyjątków bardzo często, ponieważ próbuję zapobiec wyjątkom przed wydostaniem się z interfejsu API (publiczna część mojego interfejsu API znajduje się w C) i może istnieć kilka wyjątków, które mogą wystąpić. Chcę uniknąć kod jak:

try { /* blah */ } 
catch {DerivedException const& ex) { logger.log(ex); } 
... 
catch {Exception const& ex) { logger.log(ex); } 

więc moim rejestrowania klasie, mój log funkcja przyjmuje wartości std::exception argumentem. Następnie używa typeid, aby porównać parametr z różnymi klasami wyjątków, odlewając do odpowiedniego typu, a następnie wywołując funkcję logowania wyspecjalizowaną dla tego typu wyjątku. Jest to zasadniczo ta sama technika opisana jako in this other post.

używam typeid zamiast dynamic_cast ponieważ dynamic_cast może odnieść sukces na każdej ważnej przygnębiony, a dla celów konserwacji kodu, ja naprawdę nie chcę kolejność moich if sprawozdania w funkcji log znaczenia.

Czy to jest przyzwoity projekt? Czuję się niesłusznie, używając mnie w ten sposób, ale myślę, że mam uzasadnione powody, aby to zrobić. Nie widziałem wiele wyjątków w obsłudze "na wolności", ponieważ pracujemy głównie z C, więc nie widziałem zbyt wielu podejść do tego tematu. Czy istnieją inne sposoby oddzielania wyjątków od ich formatowania logów, o których powinienem wiedzieć?

EDIT: Co ja zdecydowała się na wdrożenie
Wziąłem sugestię pomocą odwiedzający, ale dostosowany do mojego sytuacji. Chciałem złapać std::exception, ponieważ można je wyrzucać tak samo jak moje, ale sformatować komunikat dziennika na podstawie typu wyjątku.

Każda z moich klas wyjątków wywodzi się z mojej podstawowej klasy Exception i implementuje funkcję wirtualną accept. Stworzyłem klasę ExceptionLogger, która implementuje interfejs ExceptionVisitor zapewniający funkcje visit.

Klasa LogFile ma instancję ExceptionLogger, a także przeciążenie jej funkcji log, która pobiera parametr std::exception. W funkcji log wypróbowuję dynamic_cast dla mojego typu podstawowego, Exception. Jeśli się powiedzie, zadzwonię do funkcji wyjątku, w innym wypadku zadzwonię bezpośrednio do funkcji ExceptionLogger::visit(std::exception const&).Ponieważ std::exception nie implementuje mojej funkcji accept, potrzebowałem dynamic_cast, więc mogłem określić, czy możliwe jest bardziej szczegółowe rejestrowanie.

Wybrałem to zrobić zamiast serii if sprawozdania sprawdzanie typeid ponieważ:

  1. Jest to nazwane wzorzec projektowy, że mogę odnieść przyszłych opiekunów do
  2. Jeżeli opiekun dodaje nowy wynikających wyjątku z mojej bazy Exception, ale zapomina o wprowadzeniu nowej funkcji visit dla tego wyjątku, nadal będę otrzymywać rejestrację, która została zaimplementowana dla bazy Exception - pliku, numeru linii i funkcji.

    Gdybym wdrożył szereg if sprawozdania, bym musiał spaść z powrotem do zachowania std::exception logowania, która jest po prostu wydrukować wyniki od what lub może Próbowałem dynamic_cast do Exception .

    Oczywiście nadal wolałbym błąd kompilatora w tej sytuacji.

+1

Alternatywą byłoby zastosowanie wzoru Odwiedzającego. –

+0

@DDrmmr - Skończyło się na używaniu wzoru Visitor. Uczyń to jako odpowiedź i oznaczy to jako zaakceptowaną. –

Odpowiedz

1

Prostsze rozwiązanie jest przekaż swoją wyjątku w centralnym sposobu formatowania (patrz także this answer). Następnie można przechwycić każdy typ wyjątku i sformatować go.

class Exception : public std::exception {}; 
class DerivedException : public Exception {}; 
void LogThrownException(); 

void DoSomething() 
{ 
    try 
    { 
     // Do something, might throw ... 
    } 
    catch (...) 
    { 
     LogThrownException(); 
    } 
} 

void LogThrownException() 
{ 
    try 
    { 
     throw; 
    } 
    // Order is important to catch all derived types. 
    // Luckily the compiler should warn, if a type is hidden. 
    catch (DerivedException&) 
    { 
     std::cout << "DerivedException"; 
    } 
    catch (Exception&) 
    { 
     std::cout << "Exception"; 
    } 
    catch (std::exception&) 
    { 
     std::cout << "std::exception"; 
    } 
    // ... 
    catch (...) 
    { 
     std::cout << "Unknown\n"; 
    } 
} 
+0

inna odpowiedź, o której wspomniałeś, może zostać usunięta w dowolnym momencie, więc opisz tutaj sthing – manetsus

+0

Dziękuję za podpowiedź, zredagowałem moją odpowiedź. Jestem tu nowy i pomyślałem, że lepiej nie duplikować. – Djan

+0

To znacznie prostsze rozwiązanie. Niestety jest za późno, aby go wdrożyć, ale lubię i będę o tym pamiętać na przyszłość. –