2009-05-11 2 views
15

Я разрабатываю C api для некоторой функциональности, написанной на C++, и я хочу убедиться, что никакие исключения не распространяются ни на одну из экспортированных функций C.Повторное использование кода в обработке исключений

Простой способ сделать это убедившись, что каждая экспортируемая функция содержится в:

try { 
    // Do the actual code 
} catch (...) { 
    return ERROR_UNHANDLED_EXCEPTION; 
} 

Скажем, я знаю одно исключение, которое часто упускается внутри C++ код станд :: bad_alloc и я хочу относиться к ней специально я бы написать что-то вроде этого, вместо:

try { 
    // Run the actual code 
} catch (std::bad_alloc& e) { 
    return ERROR_BAD_ALLOC; 
} catch (...) { 
    return ERROR_UNHANDLED_EXCEPTION; 
} 

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

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

ответ

27

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

int HandleException() 
{ 
    try 
    { 
     throw; 
    } 

    // TODO: add more types of exceptions 

    catch(std::bad_alloc &) 
    { 
     return ERROR_BAD_ALLOC; 
    } 
    catch(...) 
    { 
     return ERROR_UNHANDLED_EXCEPTION; 
    } 
} 

И в каждом вывозимая функция:

try 
{ 
    ... 
} 
catch(...) 
{ 
    return HandleException(); 
} 
+1

+1: Хорошая идея :-) –

+0

Работал отлично. Благодаря! – Laserallan

+2

В реальном коде не забудьте поймать исключения по ссылке: catch (std :: bad_alloc &) – Jem

0

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

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

+4

В этом случае я считаю, что использование catch (...) - это правильная вещь.В C++ сложно, если не невозможно, предсказать все исключения, которые могут быть выброшены, но он должен помешать им распространяться на свой C-код. – 2009-05-11 09:09:49

+0

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

+0

Итак, вы предпочли бы, чтобы программа потерпела крах, потому что какая-то непонятная библиотека, на которую вы зависите, на двух удаляет решает, что он собирается бросить MyWeirdError? Думаю, я предпочел бы увидеть «Неопознанная проблема с операцией XXX» в журнале. – 2009-05-11 09:28:05

1

насчет:

try{ 
    //Your code here 
} catch(std::exception e) 
{ 
    return translateExceptionToErrorCode(e); 
} catch(...) 
{ 
    return UNKNOWN_EXCEPTION_THROWN; 
} 
+2

Обычно вы выбрали исключение по ссылке, чтобы избежать ненужного копирования –

+3

Не говоря уже о нарезке. – 2009-05-11 09:21:45

+0

Очень хорошие очки. Обработайте мой код как псевдо;) – PaulJWilliams

0

Было бы постыдным, если у вас есть недостающий информации об ошибке, на языке bo undary. Вы действительно должны попытаться перевести все исключения в код ошибки, который можно использовать с C.

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

+3

Перевод исключений в коды ошибок, кажется, именно то, что он делает! – 2009-05-11 09:20:26

1

Jem Ответ немного проще, чем это решение. Но можно заменить использование макроса препроцессора с использованием шаблонов. Что-то вроде этого (дополнительные уточнения, которые вы могли бы сделать):

template <class T, void (T::*FUNC)()> 
class CatchWrapper 
{ 
public: 

    static void WrapCall(T* instance) 
    { 
     try 
     { 
      (instance->*FUNC)(); 
     } 
     catch (std::bad_alloc&) 
     { 
      // Do Something 1 
     } 
     catch (std::exception& e) 
     { 
      // Do Something 2 
     } 
     catch (...) 
     { 
      // Do Something 3 
     } 
    } 
}; 


class Foo 
{ 
public: 
    void SomeCall() 
    { 
     std::cout << "Do Something" << std::endl; 
    } 
}; 


int main(int argc, char* argv[]) 
{ 
    Foo i; 
    CatchWrapper<Foo, &Foo::SomeCall>::WrapCall(&i); 
    return 0; 
} 
+0

Это то, что у меня есть, хотя и первое, и (на самом деле то, что я использовал в некоторых моих проектах). Но с моей точки зрения, Jem's anwser чище. Хотя это меня не беспокоит, иногда шаблоны используют нарушения других программистов ..:/ –

4

Уже есть хороший ответ. Но только FYI, его названная идиома «исключение-диспетчер», см. C++ FAQ 17.15. ИМХО 17.16 также является важным чтением.

+0

Спасибо, это было отличное чтение. – Laserallan

+0

Теперь [17.15] [http://www.parashift.com/c%2B%2B-faq-lite/throw-without-an-object.html] – baruch

+0

@baruch: Спасибо, исправлено до нового URL. – Abhay

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