2009-02-18 2 views
25

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

Пример:

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

может у PLZ объяснить, почему это нужно? возможно, мы можем искать альтернативы? –

+0

Я бы, очевидно, никогда не делал этого с нуля, но при определенных обстоятельствах я под ним был бы полезен. Устаревший код. –

ответ

19

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

#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"; 
      } 
     } 
    } 
+3

Этот метод действительно может быть полезен, если 'try {throw; } '... part находится в функции, которая вызывается в блоке' catch (...) '. – wjl

+1

Этот метод * * действительно полезен. Подумайте о вызове обработчика исключений в 'catch (...)'. Обработчик исключений перебрасывает и определяет тип с помощью этой техники. Это позволяет разделять обычные коды кода от неправильного (исключения) кода. – user23573

1

No.

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

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

5

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

25

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

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

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

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

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

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

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

#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"; 
    } 
} 
+0

Как это работает в этом случае с уловом? – JaredPar

+0

Отредактировано, чтобы уточнить, что вам нужно поймать базовое исключение. –

+0

", то вы можете использовать это, чтобы получить информацию о типе из своего исключения.". Как? Через что-то вроде dynamic_cast? –

-2

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

E.g.

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

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

+5

Это не C++. Это C++/CLI. В основном это синтаксис C++ для среды выполнения .NET. Он предлагает функциональность C++, но часть CLI полностью отличается от того, что C++. – Eonil

0

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

Начните подклассов 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 (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:

    runtime_error 
    range_error 
    overflow_error 
    underflow_error 
    

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

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

Наконец, Моя точка является: Вы можете достичь всего этого с помощью метания и ловли только runtime_error.

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

+0

Хмм .. с указанием типа исключений метод throws был устаревшим на некоторое время. –

+0

Да, так сказано. Но на самом деле есть некоторые проблемы, которые могут быть неочевидны, если не указывать тип исключения. Проблема заключается в том, что на самом деле любой метод может бросать что-либо: int, char *, objects и т. Д. Если вы укажете, какой метод X выбрасывает (A, B, C), а затем он бросает что-то еще (например, ошибку malloc), тогда std :: неожиданно вызывается вместо поиска метода обработчика или вызова std :: terminate. Или если вы просто объявляете метод с пустым предложением throw() std :: неожиданным, вызывается в любом исключении. –

1

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

#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> > 
0

Этот вопрос был задан вопрос некоторое время назад, и я предлагаю этот ответ в качестве дополнения к принятому ответ от 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, «Если присутствует, ... обработчик должен быть последним обработчиком для его Ьги блока.»

Смежные вопросы