2016-09-19 3 views
1

У меня есть две функции a() и b(), которые имеющие собственные классы исключений (последовательно a_exc и b_exc), наследуемых от std::logic_error.Вызов метода из собственного класса исключения поймала его базового класса

void a() { (...) throw a_exc(some_val) } 
void b() { (...) throw b_exc(some_val) } 

class a_exc : public std::logic_error 
{ 
private: 
    int foo; 
public: 
    a_exc(int val, const std::string& what_msg="Msg.") 
    : std::logic_error(what_msg), foo(val) {} 
    void show() { //show foo } 
} 

class b_exc : public std::logic_error 
{ 
private: 
    std::string bar; 
public: 
    a_exc(std::string val, const std::string& what_msg="Msg.") 
    : std::logic_error(what_msg), bar(val) {} 
    void show() { //show bar } 
} 

Допустим, я следующая часть кода:

try { 
    a(); 
    b(); 
} 
catch (const std::logic_error& e) 
{ 
    e.what(); 
    // e.show(); 
} 

catch (const std::logic_error& e) уловы как a_exc и b_exc. Конечно, этот блок не может использовать e.show(), потому что пойманный obj равен std::logic_error.

И вот моя проблема. Интересно, есть ли вероятность называть метод show() в std::logic_error catch catch, когда исключено исключение, было a_exc или b_exc. Я знаю, что вызов show() возможен, если я создаю отдельные блоки catch для a_exc и b_exc, но я хочу назвать этот метод с помощью только один блок catch. Является ли это возможным?

+0

То, что вы хотите, - это шаблонный улов, и он так же редко встречается (= в настоящее время несуществующая библиотека w/oa), как шаблонные виртуальные машины. – lorro

ответ

1

Некоторые мысли по дизайну.

Запрос типа исключения в блоке catch логически не отличается от простого предоставления двух блоков catch.

Чтобы был ясен:

catch(X& x) 
{ 
    if (dynamic_cast<Y*>(&x)) { 
    // it's a Y 
    } 
    if (dynamic_cast<Z*>(&z)) { 
    // it's a Z 
    } 
    else { 
    // it's an X 
    } 
} 

логически так же, как:

catch(Y& t) 
{ 
    // it's a Y 
} 
catch(Z& z) 
{ 
    // it's a Z 
} 
catch(X& x) 
{ 
    // it's an X 
} 

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

Первый использует «код для поиска кода», который всегда является катастрофой обслуживания, ожидающей своего появления.

Ваш вопрос вызывает больше вопросов своих собственных:

  1. a_exc ли и b_exc два вида той же ошибки?Если это так, это говорит о полиморфном базовом классе, который вы можете уловить, предпочитая std::logic_error

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

  3. Предполагая, что вам нужно show и a_exc и b_exc действительно два вида той же ошибки, мы все еще можем избежать полиморфизм. Возможно, мы можем вывести сообщение «show» в виде строки и построить его в конструкторе. Теперь это всего лишь данные. Нет суеты, никаких осложнений.

(полное) пример с использованием полиморфного базового класса (a_excb_exc являются виды одного и того же)

#include <stdexcept> 
#include <string> 

struct showable_logic_error : std::logic_error 
{ 
    using std::logic_error::logic_error; 

    virtual void show() const = 0; 
}; 

class a_exc : public showable_logic_error 
{ 
private: 
    int foo; 
public: 
    a_exc(int val, const std::string& what_msg="Msg.") 
    : showable_logic_error(what_msg) 
    , foo(val) 
    {} 

    void show() const override 
    { 
     //show foo 
    } 
}; 

class b_exc : public showable_logic_error 
{ 
private: 
    std::string bar; 
public: 
    b_exc(std::string val, const std::string& what_msg="Msg.") 
    : showable_logic_error(what_msg) 
    , bar(val) 
    {} 

    void show() const override 
    { //show bar 
    } 
}; 

void a() { throw a_exc(1); } 
void b() { throw b_exc("b"); } 

int main() 
{ 
    try 
    { 
     a(); 
    } 
    catch(showable_logic_error const& e) 
    { 
     e.show(); 
    } 
} 

полный пример, в котором не требуется полиморфизма:

#include <stdexcept> 
#include <string> 
#include <sstream> 

struct message_builder 
{ 
    template<class T> 
    static std::string build_what(const std::string& whatstr, T&& info) 
    { 
     std::ostringstream ss; 
     ss << whatstr << " : " << info; 
     return ss.str(); 
    } 
}; 

class a_exc 
: public std::logic_error 
, private message_builder 
{ 
public: 
    a_exc(int val, const std::string& what_msg="Msg.") 
    : std::logic_error(build_what(what_msg, val)) 
    {} 

}; 

class b_exc 
: public std::logic_error 
, private message_builder 
{ 
private: 
    std::string bar; 
public: 
    b_exc(std::string val, const std::string& what_msg="Msg.") 
    : std::logic_error(build_what(what_msg, std::move(val))) 
    , bar(val) 
    {} 

}; 

void a() { throw a_exc(1); } 
void b() { throw b_exc("b"); } 

int main() 
{ 
    try 
    { 
     a(); 
    } 
    catch(std::logic_error const& e) 
    { 
     e.show(); 
    } 
} 
+0

Благодарим вас за такой всеобъемлющий и ценный ответ! – mdjdrn1

+0

@ mdjdrn1 удовольствие. Спасибо, что оценили это. –

2

Вы можете, при условии, что show() функция const члена:

catch (const std::logic_error& e) 
{ 
    e.what(); 
    if(const a_exc* a = dynamic_cast<const a_exc*>(&e)) 
     a->show(); 
    else if(const b_exc* b = dynamic_cast<const b_exc*>(&e)) 
     b->show(); 
} 

Посмотри Live on Coliru. Хотя, как правило, плохой идеей вызывать другие функции, которые могут throw в вашем обработчике исключений catch.

+0

Незначительная деталь, но 'show()' не является функцией-членом-константой, поэтому нужно иметь дело с фактом 'a_exc :: show' и' b_exc :: show', не связаны. –

+1

Так лучше ли иметь отдельные блоки catch для 'a_exc' и' b_exc', которые вызывают метод 'show()'? – mdjdrn1

+0

@ mdjdrn1, Его до вас. Вы могли бы (1) создать общий базовый класс, который происходит из 'std :: logic_error' и предоставляет виртуальную функцию-член' show() ', из которой' a_exc' и 'b_exc' должны выводиться и переопределяться. Или, (2) вы можете создать несколько блоков catch, таких как [this] (http://coliru.stacked-crooked.com/a/016faad8c1373c2b) – WhiZTiM

1

Вы должны рассмотреть вопрос о создании производного типа:

struct show_exc : public std::logic_error 
{ 
    virtual void show() = 0; 
}; 

class a_exc : public show_exc 
{ 
    int foo_; 
public: 
    virtual void show() override { /*...*/ }; 
}; 

, а затем использовать отличительную улов:

catch (const show_exc& e) { 
    // .. 
} 
catch (const std::logic_error& e) { 
    // .. 
} 
+0

Да, но это делает что-то совершенно другое. (Он больше не будет ловить 'std :: logic_error' - с другим блоком' catch', который OP пытается избежать.) – lorro

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