C ++ capturando todas as exceções


244

Existe um equivalente em c ++ do Java

try {
    ...
}
catch (Throwable t) {
    ...
}

Estou tentando depurar código Java / jni que chama funções nativas do Windows e a máquina virtual continua travando. O código nativo parece bem no teste de unidade e só parece travar quando chamado pelo jni. Um mecanismo genérico de captura de exceções seria extremamente útil.



2
Observe que a maioria das falhas não é causada por exceções no C ++. Você pode capturar todas as exceções, mas isso não impedirá muitas falhas.
Mooing Duck

Respostas:


334
try{
    // ...
} catch (...) {
    // ...
}

captura todas as exceções do C ++, mas deve ser considerado um design incorreto. Você pode usar o novo mecanismo current_exception do c ++ 11, mas se você não tiver a capacidade de usar o c ++ 11 (sistemas de código legado que exigem uma reescrita), não precisará de um ponteiro de exceção nomeado para obter uma mensagem ou nome . Convém adicionar cláusulas de captura separadas para as várias exceções que você pode capturar e capturar apenas tudo na parte inferior para registrar uma exceção inesperada. Por exemplo:

try{
    // ...
} catch (const std::exception& ex) {
    // ...
} catch (const std::string& ex) {
    // ...
} catch (...) {
    // ...
}

68
É uma boa prática capturar exceções por referência const. Como em: catch (std :: exception const & ex) {/ * ... * /} #
coryan

12
@ coryan: Por que é uma boa prática capturar por referência const?
Tim MB

19
Evitar cópias desnecessárias é um benefício.
Greg D

21
-1: a sugestão de que isso "capturará todas as exceções em C ++" é enganosa. Tente gerar um erro de divisão por zero dentro do bloco try. Você verá que isso gerará uma exceção que não foi capturada, mas o código está claramente em C ++. Seria mais útil declarar que isso "captura todas as exceções de C ++" e, em seguida, adiciona alguma menção de exceções estruturadas às notas sobre utilidade limitada.
omatai

42
@omatai: Corrigido, ele captura todas as exceções de C ++. A divisão por zero é um comportamento indefinido e não gera uma exceção C ++.
Mooing Duck

151

Alguém deve adicionar que não é possível capturar "falhas" no código C ++. Aqueles não lançam exceções, mas fazem o que quiserem. Quando você vê um programa travando por causa de uma desreferência de ponteiro nulo, ele está fazendo um comportamento indefinido. Não existe std::null_pointer_exception. Tentar capturar exceções não ajudará lá.

Apenas no caso de alguém estar lendo este tópico e achar que pode obter a causa das falhas do programa. Um depurador como o gdb deve ser usado.


4
Bem, como Shy aponta, é possível com o compilador VC. Não é uma boa ideia, mas é possível.
Shog9

7
sim com SEH. mas não com técnicas sãs padrão c ++ :) bem se você ficar com janelas que você pode quase fazer tudo :)
Johannes Schaub - litb

1
Mmm ... obrigado por este boato. Estive procurando a resposta sobre por que minhas exceções de ponteiro nulo não estão sendo capturadas!
Dalin Seivewright

10
Você pode capturar segfaults com SEH no Windows e sinal (2) / sigaction (2) em sistemas POSIX, que abrange a grande maioria dos sistemas em uso atualmente, mas, como no tratamento de exceções, não é algo que deve ser usado para o controle de fluxo normal. É mais um "faça algo útil antes de morrer".
11119 Adam Rosenfield

1
@AdamRosenfield até que você tenha implementado try { .. } catch(...) { ... }a captura usando sinal / sigaction, eu não chamaria de "captura" :) Se em um manipulador de sinal, é relativamente difícil para o programador saber onde no código ocorreu a falha (estou falando sobre detectar programaticamente isso), em comparação com try / catch.
Johannes Schaub - litb

72

É assim que você pode fazer engenharia reversa do tipo de exceção a partir de dentro, catch(...)caso precise (pode ser útil ao capturar desconhecidos de uma biblioteca de terceiros) com o GCC:

#include <iostream>

#include <exception>
#include <typeinfo>
#include <stdexcept>

int main()
{
    try {
        throw ...; // throw something
    }
    catch(...)
    {
        std::exception_ptr p = std::current_exception();
        std::clog <<(p ? p.__cxa_exception_type()->name() : "null") << std::endl;
    }
    return 1;
}

e se você puder usar o Boost, poderá tornar sua seção de captura ainda mais simples (externamente) e potencialmente multiplataforma

catch (...)
{
    std::clog << boost::current_exception_diagnostic_information() << std::endl;
}

58
try {
   // ...
} catch (...) {
   // ...
}

Observe que o ...interior do catché uma elipse real, ie. três pontos.

No entanto, como as exceções de C ++ não são necessariamente subclasses de uma Exceptionclasse base , não há como realmente ver a variável de exceção lançada ao usar essa construção.


24
No C ++ 11, existe: try {std :: string (). At (1); // isso gera um std :: out_of_range} catch (...) {eptr = std :: current_exception (); // capture}
Mohammad Alaggan

2
@ bfontaine: Bem, sim, mas eu disse isso para distinguir o catchespecificador do espaço reservado para o código existente em um comentário ( // ...) que obviamente não é a sintaxe do C ++.
Greg Hewgill

1
@ GregHewgill: sim, foi apenas um comentário tipográfico.
precisa saber é o seguinte

1
@ bfontaine: Justo. :)
Greg Hewgill

44

não é possível (em C ++) capturar todas as exceções de maneira portátil. Isso ocorre porque algumas exceções não são exceções em um contexto C ++. Isso inclui coisas como divisão por zero erros e outras. É possível invadir e, assim, obter a capacidade de gerar exceções quando esses erros ocorrerem, mas não é fácil e certamente não é fácil acertar de maneira portátil.

Se você deseja capturar todas as exceções de STL, pode fazer

try { ... } catch( const std::exception &e) { ... }

O que permitirá que você use e.what(), que retornará a const char*, o que pode lhe informar mais sobre a exceção em si. Essa é a construção que mais se parece com a construção Java, sobre a qual você perguntou.

Isso não ajudará se alguém for estúpido o suficiente para lançar uma exceção que não é herdada std::exception.


2
por que isso não está no topo?
Ivan Sanz-Carasa

31

Em suma, use catch(...). No entanto, observe que catch(...)deve ser usado em conjunto com throw;basicamente:

try{
    foo = new Foo;
    bar = new Bar;
}
catch(...)       // will catch all possible errors thrown. 
{ 
    delete foo;
    delete bar;
    throw;       // throw the same error again to be handled somewhere else
}

Esta é a maneira correta de usar catch(...).


6
é melhor usar RAII para gerenciamento de memória que lida automaticamente com essas situações de exceção.
paykoob

1
@paykoob Como isso lida com casos em que você conseguiu criar um novo foo, mas ele falhou em uma barra. Ou quando o construtor de bar tenta abrir um arquivo, mas falha e, portanto, lança. então você pode acabar com um foo dangeling
Mellester

2
@MelleSterk A pilha ainda não seria limpa nesse caso, o que executaria Fooo destruidor? Eu pensei que esse era o objetivo da RAII. No entanto, se você precisar de um ponteiro para, em Foovez de apenas criar o Foona pilha, será necessário agrupar o ponteiro em outra coisa declarada na pilha.
Reirab

sim auto foo = std :: make_unique <Foo> (); barra automática = std :: make_unique <Bar> (); // é seguro exceção e não vai vazar, não catch (...) necessária
paulm

Essa resposta merece um voto se apenas para a discussão começou :)
Cristik

21

é possível fazer isso escrevendo:

try
{
  //.......
}
catch(...) // <<- catch all
{
  //.......
}

Mas há um risco muito imperceptível aqui: você não pode encontrar o tipo exato de erro que foi lançado no trybloco; portanto, use esse tipo de catchquando tiver certeza de que, independentemente do tipo de exceção, o programa deve persistir da maneira definida no catchbloco.


31
Espero que você tenha recebido algum tipo de crachá por responder a uma pergunta quase 5 anos após uma resposta superior ter sido fornecida!


18

Você pode usar

catch(...)

mas isso é muito perigoso. Em seu livro Debugging Windows , John Robbins conta uma história de guerra sobre um bug realmente desagradável que foi mascarado por um comando catch (...). É muito melhor capturar exceções específicas. Pegue o que você acha que seu bloco try pode lançar razoavelmente, mas deixe o código lançar uma exceção mais alto se algo realmente inesperado acontecer.


1
Acabei de pegar alguns usos deles e salpiquei alguns registros nessa fase. Não fazer nada com exceção é definitivamente pedir problemas.
Jxramos # 9/16

15

Deixe-me mencionar isso aqui: o Java

try 
{
...
}
catch (Exception e)
{
...
}

NÃO pode pegar todas as exceções! Na verdade, já tive esse tipo de coisa antes, e é provocador de insanidade; A exceção deriva de Throwable. Então, literalmente, para capturar tudo, você NÃO deseja capturar Exceções; você quer pegar Throwable.

Eu sei que isso soa bem, mas quando você passa vários dias tentando descobrir de onde veio a "exceção não capturada" no código cercado por um bloco try ... catch (Exception e) ", ele fica com vocês.


2
Obviamente, você nunca deve capturar objetos Error - se você os pegasse, eles seriam Exceções. Objetos de erro são coisas completamente fatais, como ficar sem espaço de pilha, etc
SCDF

1
Nem exceções de tempo de execução, na maioria das vezes exceções GoodProgrammerExpected !!!
OscarRyz

3
Tivemos um bug realmente grave causada pela captura de um OutOfMemoryError devido a um catch (Throwable) bloco em vez de deixá-lo matar coisas ...
Trejkaz

1
Claro que catch(Exception)pode não pegar todas as exceções em Java, você está misturando-o com C # ... Java = catch(Thowable), C # = catch(Exception). Não os confunda.
Chef Pharaoh

2
@OscarRyz que soa como o CoderMalfunctionError(que é realmente um verdadeiro Java Errorsubclasse ... embora isso não significa o que parece.)
reirab

9

Bem, se você quiser pegar todas as exceções para criar um minidump, por exemplo ...

Alguém fez o trabalho no Windows.

Consulte http://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus No artigo, ele explica como descobriu como capturar todos os tipos de exceções e fornece o código que funciona.

Aqui está a lista que você pode pegar:

 SEH exception
 terminate
 unexpected
 pure virtual method call
 invalid parameter
 new operator fault 
 SIGABR
 SIGFPE
 SIGILL
 SIGINT
 SIGSEGV
 SIGTERM
 Raised exception
C++ typed exception

E o uso: CCrashHandler ch; ch.SetProcessExceptionHandlers (); // faça isso para um thread ch.SetThreadExceptionHandlers (); // para cada thred


Por padrão, isso cria um minidump no diretório atual (crashdump.dmp)


4

Um mecanismo genérico de captura de exceções seria extremamente útil.

Duvidoso. Você já sabe que seu código está quebrado, porque está travando. Exceções alimentares podem mascarar isso, mas isso provavelmente resultará em bugs ainda mais desagradáveis ​​e sutis.

O que você realmente quer é um depurador ...


13
Eu discordo, há muitos casos em aplicativos em tempo real onde eu prefiro capturar uma exceção desconhecida, gravar qualquer coisa em um log / seguir algum curso de ação genérico de erro, em vez de deixar o aplicativo travar.
f0ster 13/09/11

3
Eu suspeito que você esteja pensando em casos em que pode seguir algum curso de ação genérico de erro, ignorando convenientemente aqueles em que a pilha está na lixeira ou a memória está esgotada e a manipulação de erros genérica também não será bem-sucedida. Não há nada de errado em capturar erros dos quais você pode se recuperar, mas o IMHO realmente deve existir apenas como isolado (pilha separada, memória pré-alocada), lógica cuidadosamente escrita, chamada imediatamente antes do encerramento do programa; se você não sabe qual é o problema, não pode ter certeza de que ele pode ser recuperado.
precisa saber é o seguinte

1
Ou seja, instale um manipulador de sinal que desenrola algum log criado durante o tempo de execução para descobrir onde o programa travou e, esperançosamente, por quê.
Clearer

3
  1. Você pode executar seu aplicativo Java usando JNI a partir de uma janela do console (inicie-o a partir de uma linha de comandos java) para ver se há algum relatório do que pode ter sido detectado antes do travamento da JVM. Ao executar diretamente como um aplicativo de janela Java, pode haver mensagens ausentes que apareceriam se você executasse a partir de uma janela do console.

  2. Em segundo lugar, você pode stub sua implementação da DLL JNI para mostrar que os métodos na DLL estão sendo inseridos na JNI, você está retornando corretamente, etc.?

  3. Caso o problema ocorra com o uso incorreto de um dos métodos da interface JNI do código C ++, você verificou se alguns exemplos simples de JNI são compilados e funcionam com sua configuração? Estou pensando em particular no uso dos métodos da interface JNI para converter parâmetros em formatos C ++ nativos e transformar resultados de funções em tipos Java. É útil stub-las para garantir que as conversões de dados estejam funcionando e que você não esteja distraído nas chamadas do tipo COM na interface JNI.

  4. Há outras coisas a serem verificadas, mas é difícil sugerir alguma, sem saber mais sobre quais são seus métodos Java nativos e o que a implementação JNI deles está tentando fazer. Não está claro que a captura de uma exceção no nível do código C ++ esteja relacionada ao seu problema. (Você pode usar a interface JNI para refazer a exceção como Java, mas não está claro pelo que você fornece que isso ajudará.)


2

Para o problema real de não conseguir depurar corretamente um programa que usa JNI (ou o erro não aparece ao executá-lo em um depurador):

Nesse caso, geralmente ajuda a adicionar wrappers Java nas chamadas JNI (ou seja, todos os métodos nativos são privados e os métodos públicos da classe os chamam) que realizam algumas verificações básicas de integridade (verifique se todos os "objetos" estão liberados e "objetos" não são usados ​​após a liberação) ou sincronização (apenas sincronize todos os métodos de uma DLL para uma instância de objeto único). Deixe os métodos do wrapper java registrarem o erro e lançarem uma exceção.

Isso geralmente ajuda a encontrar o erro real (que surpreendentemente está principalmente no código Java que não obedece à semântica das funções chamadas, causando algumas liberações duplas ou similares desagradáveis) mais facilmente do que tentar depurar um programa Java massivamente paralelo em um depurador nativo ...

Se você souber a causa, mantenha o código nos métodos do invólucro que o evitam. É melhor que seus métodos de wrapper gerem exceções que seu código JNI trava a VM ...


1

Bem, isso realmente depende do ambiente do compilador. O gcc não os captura. O Visual Studio e o último Borland que eu usei fizeram.

Portanto, a conclusão sobre falhas é que depende da qualidade do seu ambiente de desenvolvimento.

A especificação C ++ diz que catch (...) deve capturar qualquer exceção, mas não ocorre em todos os casos.

Pelo menos do que eu tentei.


1

Estar ciente

try{
// ...
} catch (...) {
// ...
}

captura apenas exceções no nível do idioma, outras exceções / erros de baixo nível como Access Violatione Segmentation Faultnão serão detectadas.


Coisas como falha de segmentação não são realmente exceções, são sinais; portanto, você não pode capturá-los como exceções típicas. No entanto, existem algumas soluções alternativas como esta .
MAChitgarha 26/03
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.