Законно ли е повторното хвърляне на изключение във вложен „опит“?

Следното добре дефинирано ли е в C++ или не? Принуден съм да „преобразувам“ изключения в кодове за връщане (въпросният API се използва от много C потребители, така че трябва да се уверя, че всички C++ изключения са уловени и обработени, преди контролът да бъде върнат на повикващия).

enum ErrorCode {…};
ErrorCode dispatcher() {
   try {
      throw;
   }
   catch (std::bad_alloc&) {
      return ErrorCode_OutOfMemory;
   }
   catch (std::logic_error&) {
      return ErrorCode_LogicError;
   }
   catch (myownstdexcderivedclass&) {
      return ErrorCode_42;
   }
   catch(...) {
      return ErrorCode_UnknownWeWillAllDie;
   }
}

ErrorCode apifunc() {
   try {
      // foo() might throw anything
      foo();
   }
   catch(...) {
      // dispatcher rethrows the exception and does fine-grained handling
      return dispatcher();
   }
   return ErrorCode_Fine;
}

ErrorCode apifunc2() {
   try {
      // bar() might throw anything
      bar();
   }
   catch(...) {
      return dispatcher();
   }
   return ErrorCode_Fine;
}

Надявам се извадката да покаже моето намерение. Предполагам, че това е недефинирано поведение, но не съм сигурен. Моля, предоставете цитати от стандарта, ако е приложимо. Алтернативните подходи също се оценяват.

Благодаря!


person Alexander Gessler    schedule 17.03.2010    source източник
comment
Спомням си, че обмислях този подход за намаляване на дублирането на код, произтичащо от различни блокове try/catch, но го внедрих. Какво те кара да мислиш, че може да е незаконно?   -  person    schedule 18.03.2010
comment
Използвал съм това и преди, това е страхотна техника   -  person iain    schedule 18.03.2010
comment
@jdv - Чувствах се малко неудобно с повторното хвърляне от блок try, вложен в catch-клауза (и дори в различна стекова рамка). Просто изглеждаше прекалено красиво, така че исках да съм на сигурно място.   -  person Alexander Gessler    schedule 18.03.2010
comment
throw без аргументи е отхвърлен в c++ 11. Използвах този подход в c++3 без проблеми, но получавам един объркан проблем в g++, опитвайки се да го пренеса (използвайки rethrow_exception и get_exception).   -  person agodinhost    schedule 08.09.2015
comment
@agodinhost Уау Уау Уау. Можете ли да цитирате този източник? en.cppreference.com/w/cpp/language/throw Мисля, че вие може би объркващо със спецификациите на изключение en.cppreference.com/w/cpp/language/except_spec !?   -  person sehe    schedule 15.11.2015
comment
Да, прав си. Тай.   -  person agodinhost    schedule 03.02.2016


Отговори (2)


Това е добре. Изключението е активно, докато не бъде уловено, когато става неактивно. Но той живее, докато обхватът на манипулатора приключи. От стандарта, акцентът е мой:

§15.1/4: Паметта за временното копие на хвърляното изключение се разпределя по неопределен начин, освен както е отбелязано в 3.7.4.1. Временното продължава, докато има манипулатор, който се изпълнява за това изключение.

Това е:

catch(...)
{ // <--

    /* ... */

} // <--

Между тези стрелки можете да хвърлите отново изключението. Само когато обхватът на манипулаторите приключи, изключението престава да съществува.

Имайте предвид, че ако извикате dispatch без активно изключение, ще бъде извикано terminate. Ако dispatch хвърли изключение в едно, ако е манипулатори, това изключение ще започне да се разпространява. Повече информация в свързан въпрос.

person GManNickG    schedule 17.03.2010
comment
Би било добре да използвате този подход в метод, наречен в конструктор? (g++) - person agodinhost; 08.09.2015

Тъй като dispatcher извика в блока catch throw ще хвърли отново изключение. Ако извикате dispatcher извън catch блока, тогава ще бъде извикано terminate() (съгласно 15.1/8). Във всеки случай няма неопределено поведение.

person Kirill V. Lyadvinsky    schedule 17.03.2010