2008-11-09 9 views
18

Czy istnieje sposób na wychwycenie wyjątków, które w przeciwnym razie nie są obsługiwane (w tym wyjątki wyrzucane poza blokiem catch)?Catching wszystkich nieobsługiwanych wyjątków C++?

Nie przejmuję się wszystkimi normalnymi czynnościami czyszczenia wykonanymi z wyjątkami, tylko że mogę je przechwycić, napisać do log/powiadomić użytkownika i zakończyć program, ponieważ wyjątki w tych przypadkach są ogólnie śmiertelne, nieodwracalne błędy.

coś takiego:

global_catch() 
{ 
    MessageBox(NULL,L"Fatal Error", L"A fatal error has occured. Sorry for any inconvience", MB_ICONERROR); 
    exit(-1); 
} 
global_catch(Exception *except) 
{ 
    MessageBox(NULL,L"Fatal Error", except->ToString(), MB_ICONERROR); 
    exit(-1); 
} 

Odpowiedz

21

ten może być stosowany do połowu, nieoczekiwane wyjątki.

Bez bloku try catch, nie sądzę, można złapać wyjątki, tak zorganizować swój program, więc wyjątek kod thowing jest pod kontrolą try/catch.

+0

Chodzi o to, że naprawdę nie chcesz, aby umieścić całą moją aplikację w jednym wielkim bloku „spróbuj super” ... ponieważ wydajność jest trochę krytyczny ... Znam tam kilka gdzieś, bo np. studio wizualne potrafi wykryć/złapać/cokolwiek takiego wyjątkowego i zaoferować przerwanie i debugowanie. –

+0

Działa blok super-testu. Płacisz koszty ustawienia go raz w głównym. Raz nie jest to problem z wydajnością. – EvilTeach

+0

tylko raz? Jestem pewien, że raz w bloku try utrzymywał jakiś "ślad" do czyszczenia po rzucie, czyniąc koszt oparty na zawartości bloku? –

4

To, co zawsze robię w głównej()

int main() 
{ 
    try 
    { 
     // Do Work 
    } 
    catch(std::exception const& e) 
    { 
     Log(e.what()); 
     // If you are feeling mad (not in main) you could rethrow! 
    } 
    catch(...) 
    { 
     Log("UNKNOWN EXCEPTION"); 
     // If you are feeling mad (not in main) you could rethrow! 
    } 
} 
+1

To dobra droga, ale należy pamiętać, że nie spowoduje to wyjątków statycznych init (jak sądzę). – kralyk

+0

@kralyk: Nie ma sposobu na wychwycenie wyjątków wygenerowanych w konstruktorach/destruktorach obiektów czasu przechowywania statycznego. W takim przypadku wywoływane jest 'std :: terminate()'. –

+1

Wiem ... Technicznie, może to być możliwe za pomocą set_terminate() (zobacz moją odpowiedź), ale ponieważ statyczna kolejność init jest zdefiniowana przez implementację, to nie ma gwarancji, że ... – kralyk

9

Można użyć SetUnhandledExceptionFilter w systemie Windows, który złapie wszystkie nieobsłużonych SEH wyjątki.

Ogólnie będzie to wystarczające dla wszystkich problemów, ponieważ IIRC wszystkie wyjątki C++ są implementowane jako SEH.

8

Bez żadnego bloku catch, nie będzie żadnych wyjątków. Możesz mieć blok catch (...) w twoim main() (i jego odpowiednik w każdym dodatkowym wątku). W tym bloku catch możesz odzyskać szczegóły wyjątku i możesz coś z nimi zrobić, na przykład rejestrowanie i zamykanie.

Jednak istnieją również wady dotyczące ogólnego bloku catch (...): system stwierdzi, że wyjątek został obsłużony przez ciebie, więc nie daje on więcej pomocy. W systemach Unix/Linux ta pomoc stanowi utworzenie pliku CORE, który można załadować do debuggera i zobaczyć oryginalną lokalizację wyjątku, którego nie dotyczy. Jeśli będziesz go obsługiwać z funkcją catch (...), ta informacja zostanie już utracona.

W systemie Windows nie ma plików CORE, więc sugerowałbym blok catch (...). Od tego bloku, to zazwyczaj wywołać funkcję wskrzesić rzeczywisty wyjątek:

std::string ResurrectException() 
    try { 
     throw; 
    } catch (const std::exception& e) { 
     return e.what(); 
    } catch (your_custom_exception_type& e) { 
     return e.ToString(); 
    } catch(...) { 
     return "Ünknown exception!"; 
    } 
} 


int main() { 
    try { 
     // your code here 
    } catch(...) { 
     std::string message = ResurrectException(); 
     std::cerr << "Fatal exception: " << message << "\n"; 
    } 
} 
+2

Windows ma pliki .dmp, które są w przybliżeniu odpowiada to plikom podstawowym, ale są one przesyłane do witryny raportowania błędów systemu Windows (jeśli użytkownik kliknie "wyślij") zamiast zaśmiecać dysk twardy użytkownika. Ponadto, jeśli masz skonfigurowany debugger typu "dokładnie na czas", system Windows włamie się do debuggera. – bk1e

+1

Konstruktor std :: string może sam rzucić się na wyjątek. I tak samo drukowanie na std: cerr. Z drugiej strony możesz mieć szczęście w 99% przypadków. – rxantos

7

Aktualizacja: Obejmuje C++ 98 tylko.

Od More Effective C++ przez Meyers (str. 76), można zdefiniować funkcję, która zostanie wywołana, gdy funkcja wygeneruje wyjątek, który nie jest zdefiniowany przez specyfikację wyjątku.

void convertUnexpected() 
{ 
    // You could redefine the exception here into a known exception 
    // throw UnexpectedException(); 

    // ... or I suppose you could log an error and exit. 
} 

W aplikacji zarejestrować funkcję:

std::set_unexpected(convertUnexpected); 

Twoja funkcja convertUnexpected() będzie uzyskać nazywane jeśli funkcja generuje wyjątek, który nie jest zdefiniowany przez jego specyfikacji wyjątków ... co oznacza to tylko działa, jeśli używasz specyfikacji wyjątków. ; (

+3

Wiem, że ta odpowiedź została wprowadzona na długo przed udostępnieniem standardu C++ 11, ale obecnie 'std :: set_unexpected' jest przestarzałe. – scrutari

1

Użyj catch (...) we wszystkich twoich wyjątkowych barierach (nie tylko w głównym wątku) Sugeruję, abyś zawsze wracał (...) i przekierowywał standardowe wyjście/błąd do pliku logu, nie możesz zrobić sensownego RTTI na (...).OTOH, kompilator GCC jak wyświetli dość szczegółowy opis o nieobsługiwany wyjątek: rodzaj, wartość, co() itp

19
+0

Czy można uzyskać dostęp do wyjątku w 'terminate_handler'? –

+0

@ KonstantinA.Magg Nie sądzę. Ponieważ wyjątek może być typu _any_, nie ma sposobu, aby uzyskać do niego dostęp ... – kralyk

+1

Tak można użyć std :: uncaugth_exception() & std :: current_exception {) –

3

Pod warunkiem, że C++ 11 jest dostępny, to podejście może być stosowany (patrz przykład z: http://en.cppreference.com/w/cpp/error/rethrow_exception):

#include <iostream> 
#include <exception> 

void onterminate() { 
    try { 
    auto unknown = std::current_exception(); 
    if (unknown) { 
     std::rethrow_exception(unknown); 
    } else { 
     std::cerr << "normal termination" << std::endl; 
    } 
    } catch (const std::exception& e) { // for proper `std::` exceptions 
    std::cerr << "unexpected exception: " << e.what() << std::endl; 
    } catch (...) { // last resort for things like `throw 1;` 
    std::cerr << "unknown exception" << std::endl; 
    } 
} 

int main() { 
    std::set_terminate(onterminate); // set custom terminate handler 
    // code which may throw... 
    return 0; 
} 

Takie podejście pozwala również na dostosowanie wyjścia konsoli do nieobsłużonych wyjątków: mieć coś takiego

unexpected exception: wrong input parameters 
Aborted 

zamiast tego:

terminate called after throwing an instance of 'std::logic_error' 
    what(): wrong input parameters 
Aborted 
Powiązane problemy