Определение типа исключения после того, как исключение поймано? - PullRequest
36 голосов
/ 18 февраля 2009

Есть ли способ определить тип исключения, даже если вы знаете, что вы перехватили исключение с помощью catch all?

Пример:

try
{
   SomeBigFunction();
}
catch(...)
{
   //Determine exception type here
}

Ответы [ 9 ]

29 голосов
/ 18 февраля 2009

Краткий ответ: Нет.

Длинный ответ:

Если вы выводите все свои исключения из общего базового типа (скажем, std :: exception) и перехватываете это явно, то вы можете использовать это для получения информации о типе из вашего исключения.

Но вы должны использовать функцию catch, чтобы перехватить как особый тип исключения, а затем работать оттуда.

Единственное реальное использование улова (...):

  • Поймать и выбросить исключение (остановить исключение, исключающее деструктор).
  • Поймать: записать случайное исключение и повторить бросок.

Отредактировано: Вы можете извлечь информацию о типе через dynamic_cast <> () или через typid () Хотя, как указано выше, это не то, что я рекомендую. Используйте кейсы.

#include <stdexcept>
#include <iostream>

class X: public std::runtime_error  // I use runtime_error a lot
{                                   // its derived from std::exception
    public:                         // And has an implementation of what()
        X(std::string const& msg):
            runtime_error(msg)
        {}
};

int main()
{
    try
    {
        throw X("Test");
    }
    catch(std::exception const& e)
    {
        std::cout << "Message: " << e.what() << "\n";

        /*
         * Note this is platform/compiler specific
         * Your milage may very
         */
        std::cout << "Type:    " << typeid(e).name() << "\n";
    }
}
24 голосов
/ 18 февраля 2009

Вы можете определить тип внутри улова (...), но это не очень полезно:

#include <iostream>
#include <exception>

    class E1 : public std::exception {};
    class E2 : public std::exception {};

    int main() {
        try {
            throw E2();
        }
        catch( ... ) {
            try {
                throw;
            }
            catch( const E1 & e ) {
                std::cout << "E1\n";
            }
            catch( const E2 & e ) {
                std::cout << "E2\n";
            }
        }
    }
8 голосов
/ 07 ноября 2017

Нет стандартного, портативного способа сделать это. Вот непереносимый способ сделать это на GCC и Clang

#include <iostream>
#include <cxxabi.h>

const char* currentExceptionTypeName()
{
    int status;
    return abi::__cxa_demangle(abi::__cxa_current_exception_type()->name(), 0, 0, &status);
}

int main()
{
    try {
        throw std::string();
    } catch (...) {
        std::cout<<"Type of caught exception is "<<currentExceptionTypeName()<<std::endl;
    }

    return 0;
}

Выход:

Type of caught exception is std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >
6 голосов
/ 18 февраля 2009

Если вам нужно обрабатывать исключения по-разному, в зависимости от того, чем они являются, вы должны ловить определенные исключения. Если есть группы исключений, все из которых должны обрабатываться одинаково, их можно извлечь из общего базового класса и перехватить базовый класс. Используйте силу и парадигмы языка, не боритесь с ними!

1 голос
/ 18 февраля 2009

Нет.

Для этого по крайней мере потребуется доступ к текущему исключению. Я не верю, что есть стандартный способ сделать это.

Если у вас есть экземпляр исключения, вам придется использовать алгоритм проверки типа. C ++ не имеет внутренней поддержки для этого. В лучшем случае вам понадобится большой оператор if / elseif с dynamic_cast для проверки типа.

0 голосов
/ 11 апреля 2018

при условии, что c ++ 11 доступен,

bool throwing_func()
{
    // something is wrong...
    throw char('5');

    // ...

    return true;
}

void exception_handler(std::exception_ptr _Eptr)
{
    try 
    {
        if (_Eptr) {std::rethrow_exception(_Eptr);}
    }


    catch(int _Xi)
    {
        std::cout << "int\n";
    }

    catch(char _Xc)
    {
        std::cout << "char\n";
    }

    catch(const std::exception& _Xe)
    {
       std::cout << "std::exception " << _Xe.what() << "\n";
    }

    catch (...)
    {// develop more catch cases above to avoid what follows
        std::cout << "unhandled exception\n";
        // grande problema
    }
}

int main()
{

    try
    {
        throwing_func();
    }

    catch(...)
    {
        //Determine exception type here

         exception_handler(std::current_exception());
    }

    return 0;
}
0 голосов
/ 12 февраля 2018

Этот вопрос был задан некоторое время назад, и я предлагаю этот ответ в качестве дополнения к принятому ответу 9 лет назад. Я должен согласиться с этим респондентом, что этот ответ "... не очень полезен". Кроме того, это открывает дверь к исключению, которое когда-то обрабатывалось, будучи необработанным. Для иллюстрации позвольте мне опираться на ответ респондента

#include <iostream>
#include <exception>

class E1 : public std::exception {};
class E2 : public std::exception {};
class E3 : public std::exception {};

int main() {
    try {
        throw E3();
    }
    catch( ... ) {
        try {
            // OOOPS!!! E3 is now unhandled!!!!!!
            throw;
        }
        catch( const E1 & e ) {
            std::cout << "E1\n";
        }
        catch( const E2 & e ) {
            std::cout << "E2\n";
        }
    }
}

Альтернативой этому подходу будет следующее:

#include <iostream>
#include <exception>

class E1 : public std::exception {};
class E2 : public std::exception {};
class E3 : public std::exception {};

int main() {
    try {
        throw E3();
    }
    catch( const E1 & e ) {
        std::cout << "E1\n";
    }
    catch( const E2 & e ) {
        std::cout << "E2\n";
    }
    catch( ... ) {
        std::cout << "Catch-all...";
    }
}

Этот второй подход, по-видимому, равносилен первому и имеет преимущество в том, что он специально обрабатывает E1 и E2, а затем перехватывает все остальное. Это предлагается только в качестве альтернативы.

Обратите внимание, что в соответствии с проектом C ++ от 2011-02-28, пункт 15.3, пункт 5 маркера, " Если присутствует, обработчик ... должен быть последним обработчиком для его блока try. «

0 голосов
/ 10 июня 2012

я пробовал разные способы; это работает для меня:

Начните с создания подкласса runtime_error :

/*----------------------------------------------------------------------*/    
/* subclass runtime_error for safe exceptions in try/throw/catch        */

 #include <stdexcept>
/* a little preprocessor magic here -- makes a subclass of runtime_error*/

#define NEWERROR( NE )  class NE  : public runtime_error {              \
        public:  NE ( string const& error ) : runtime_error(error) {}  }


NEWERROR( FileError      );
NEWERROR( NetworkError   );
NEWERROR( StringError    );
NEWERROR( CofeeError     );

/*----------------------------------------------------------------------*/

Тогда вы можете создать несколько экземпляров ваших исключений.

/*----------------------------------------------------------------------*/
/* some example pre-defined exceptions  */

FileError     ReadOnly                ( "ReadOnly"             );
FileError     FileNotFound            ( "FileNotFound"         );
NetworkError  TimeOutExceeded         ( "TimeOutExceeded"      );
NetworkError  HostNotFound            ( "HostNotFound"         );
CoffeeError   OutOfCoffee             ( "OutOfCoffee"          );

/*----------------------------------------------------------------------*/

Явно уведомить компилятор, что ваша функция может выдать исключение или программа, вероятно, прекратит работу в указанной точке, и данные могут быть потеряны или повреждены если ресурсы используются в то время.

«Убедитесь, что вы можете и поймать все, что вы можете бросить.»

(я использую общий runtime_error , потому что его бросание и перехват охватывают все Мои исключения, а также системные.)

/*----------------------------------------------------------------------*/
/* example function that may throw an exception */

#include <fstream>

ifstream& getFileStream (string fname) throw (runtime_error)
 {

    if ( fname == "" ) 
      throw StringError( "<getFileStream> fname:empty string" );
      // processing stops here if thrown

    try 
      {
       ifstream Inputfstream;  

       ifstream& ifsref = Inputfstream;

       // ifstream has its own <legacy> exception
       // mechanisms and procedures 
       ifsref.exceptions ( ifstream::failbit | ifstream::badbit );

       ifsref.open (fname , ifstream::in);  // could fail ==> ifstream::failure exception
      }
    catch (ifstream::failure e) 
      {
       throw FileError( fname + string(e.what() ) ); 
      }

    return ifsref;
 }

/*----------------------------------------------------------------------*/

тогда в вашем try / catch

/*----------------------------------------------------------------------*/
catch (FileNotFound fnf) //catch a specific error
 {
  if (DEBUG) cerr << "[File Not Found Error: " << fnf.what() << "]" << endl;
  ... (handle it) ... 
 }  
catch (FileError fe) //catch a specific type
 {
  if (DEBUG) cerr << "[File Error: " << fe.what() << "]" << endl;
  ... (handle it) ... 
 }  
catch (runtime_error re ) // catch a generic type
 {
   if (DEBUG) cerr << "[Runtime error: " << re.what() << "]" << endl;          

    // determine type by string comparison
   if ( re.what() == string("ResourceNotavailable") )  ...
   if ( re.what() == string("NetWorkError")         )  ...   

  ...

}
catch ( ... )  // catch everything else 
 { ... exit, rethrow, or ignore ... }

/*----------------------------------------------------------------------*/

Класс runtime-error имеет хорошую поддержку в стандартных библиотеках c ++, и компиляторы знают об этом внутренне, и как оптимизировать память и диспетчеризацию, так что вы можете безопасно и уверенно использовать их в разных базах кода. Код является переносимым и совместимым со многими различными компиляторами и архитектурами.

Может быть предпочтительнее и несколько быстрее отлавливать каждую ошибку отдельно в предложении catch, от более специфического к более универсальному, если вы чувствуете, что последовательность совпадений строк является ужасной тратой ресурсов процессора и памяти (хотя компилятор оптимизирует их) ,

<stdexcept> дает вам несколько видов исключений в 2 группах:

  • Логические ошибки:

    logic_error
    domain_error
    invalid_argument
    length_error
    out_of_range
    
  • Ошибки времени выполнения:

    runtime_error
    range_error
    overflow_error
    underflow_error
    

Синтаксис использования немного отличается для некоторых из них.

Обычная мудрость в C ++ говорит, что ваши исключения должны быть относительно "плоскими", Это означает, что большие иерархии определенных категорий исключений следует избегать в предпочтение кратких общих, но информативных для общих задач программирования. Задачи, специфичные для предметной области, такие как логика сетевой системы, высшая математика и т. Д., Могут выиграть от специфики, но это может быть легко достигнуто путем создания интеллектуальных строк ошибок с общими исключениями времени выполнения / логики.

Наконец, Моя точка зрения : всего этого вы можете достичь, бросать и ловить только runtime_error .

Вам не нужно создавать целую сумку со специфическими исключениями (как это делает Java) для каждого класса, каждый обрабатывает одну конкретную ошибку.

0 голосов
/ 09 июня 2010

Если вы используете Visual C ++ (управляемый), вы можете использовать метод GetType (), чтобы получить тип исключения и обработать его оттуда.

* 1003 Е.Г. *

try
    {
        // Run the application
        Application::Run(mainForm);
    }
    catch (Exception^ e)
    {
        String^ exception_type = e->GetType()->ToString();
        throw;
    }

Строка будет содержать что-то вроде «System.ArgumentOutOfRangeException».

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...