【问题标题】:Is it possible to write/wrap the exception handling components (try,catch) in different class?是否可以在不同的类中编写/包装异常处理组件(try、catch)?
【发布时间】:2014-08-30 05:26:05
【问题描述】:

这是关于将异常处理逻辑包装在某种类中。在编写 c++ 时 代码,很多时候我们需要根据客户端抛出的异常捕获许多类型/变体。这导致我们在 catch() 子句中(多次)编写类似类型的代码。

在下面的示例中,我编写了函数(),它可以以多种可能的形式抛出异常。

我想知道是否可以以类的形式编写/包装这样的逻辑,以便最终用户不得不一次编写类似类型的代码? 有什么意义吗?

#include<vector>
#include<string>
#include<exception>
#include<iostream>

// this function can throw std::exception, std::string, int or unhandled
void function() {
  std::vector<int> x{1,2,3,4,5};
  auto val = x.at(x.size()); //throw out-of-range error
}

int main() {
try { function(); } 
catch(std::exception& e) { std::cout<<e.what()<<std::endl; }
catch(std::string& msg)  { std::cout<<msg<<std::endl; }
catch(int i)         { std::cout<<i<<std::endl; }
catch(...)       { std::cout<<"Unhandled Exception"<<std::endl; }
return 0;
}

到目前为止我是这样想的,下面是伪逻辑

class exceptionwrapper{
exceptionwrapper(function pointer* fp) {
 // functions which would be executing inside try
}
~exceptionwrapper() {
// all catch() clause can be written over here
// or some other member function of this class
}

};

这个类的对象可以通过这种方式在ma​​in()中实例化。

int main() {
 exceptionwrapper obj(function);
 //here execptionwrapper destructor would take care about calling all type of catch
}

【问题讨论】:

    标签: c++ c++11 exception-handling raii


    【解决方案1】:

    可以使用std::exception_ptr:

    Live demo link.

    #include <iostream>
    #include <exception>
    #include <stdexcept>
    
    void universal_exception_handler(std::exception_ptr e)
    {
        try
        {
            std::rethrow_exception(e);
        }
        catch (const std::logic_error& e)
        {
            std::cout << "logic_error" << std::endl;
        }
        catch (const std::runtime_error& e)
        {
            std::cout << "runtime_error" << std::endl;
        }
    }
    
    void foo()
    {
        throw std::logic_error{""};
    }
    
    void bar()
    {
        throw std::runtime_error{""};
    }
    
    int main()
    {
        try
        {
            foo();
        }
        catch (...)
        {
            universal_exception_handler(std::current_exception());
        }
    
        try
        {
            bar();
        }
        catch (...)
        {
            universal_exception_handler(std::current_exception());
        }
    }
    

    您也可以不使用std::exception_ptr 来实现此目的,只需将throw; 替换为std::rethrow_exception(e);,希望仅在 处理活动异常时调用此函数(否则您的程序将是terminate()'ed):

    void universal_exception_handler()
    {
        try
        {
            throw;
        }
        catch (const std::logic_error& e)
        {
            std::cout << "logic_error" << std::endl;
        }
        catch (const std::runtime_error& e)
        {
            std::cout << "runtime_error" << std::endl;
        }
    }
    
    try
    {
        foo();
    }
    catch (...)
    {
        universal_exception_handler();
    }
    

    Yet another live demo link.

    【讨论】:

    • template&lt;typename F&gt; my_trycatch(F f) { try { f(); } catch(const logic_error&amp; e) { cout &lt;&lt; "logic_error\n"; } } 然后my_trycatch([]{ foo(); });
    【解决方案2】:

    您所要求的是可能的,但我认为它不是很有用。首先让我们实现一个机制来接受一个可调用对象及其相关参数,我们将在 exception_wrapper 的析构函数中调用它们。

    template<typename Func, typename... Args>
    struct exception_wrapper
    {
        exception_wrapper(Func f, Args... args) 
        : f_(std::move(f))
        , args_(std::make_tuple(std::move(args)...))
        {}
    
        ~exception_wrapper() 
        {
            try {
                invoke();
            } catch(std::exception const& e) {
                std::cerr << "Caught exception: " << e.what() << std::endl;
            } catch(...) {
                std::cerr << "Caught unknown exception" << std::endl;
            }
        }
    
        template<std::size_t... I>
        void apply(std::index_sequence<I...>)
        {
            f_(std::get<I>(args_)...);
        }
    
        void invoke()
        {
            apply(std::index_sequence_for<Args...>());
        }
    
        Func f_;
        std::tuple<Args...> args_;
    };
    
    template<typename Func, typename... Args>
    auto make_exception_wrapper(Func&& f, Args&&... args)
    {
        return exception_wrapper<Func, Args...>(
            std::forward<Func>(f), std::forward<Args>(args)...);
    }
    

    这利用了 C++14 std::integer_sequence;如果这在您的实现中不可用,那么有几个关于 SO 的答案显示了如何自己实现它(例如this one)。

    要使用它,请创建一个exception_wrapper 对象,当析构函数执行时,您的函数将被调用。

    make_exception_wrapper(function);
    

    Live demo


    现在,我不认为这很有用,因为通常您应该只在您的代码能够处理异常并继续正常运行时才捕获异常。否则,让它们传播到您可能想要安装处理程序的顶层,以便它允许您优雅地退出程序。

    鉴于此,不太可能有一种通用的方法来处理您的代码引发的所有异常,这大大降低了exception_wrapper 的实用性。您可以对其进行修改以采用另一个可调用参数,即将传递被捕获的std::exception 对象的异常处理程序,这使得该类更加通用。

    此外,在析构函数中调用函数意味着您不能将返回值(如果有)传回给调用者。这可以通过在 exception_wrapper::operator() 中调用函数来解决,但这会增加在确实抛出异常的情况下返回的内容,并且您已经抑制了它。

    最后,不要编写抛出不是从std::exception 派生的类型的代码。这使您的代码具有统一性,如果您确实想处理异常,则需要在代码中添加多个 catch 语句,就像您在示例中所做的那样。

    【讨论】:

    • 我认为,如果您有一个不使用异常的 API,并且您正在尝试将异常转换为错误代码,那么这种方法可能会很有用。一组非常相似的函数属于一起。
    猜你喜欢
    • 2011-04-01
    • 1970-01-01
    • 2016-09-13
    • 2011-01-01
    • 1970-01-01
    • 2022-01-19
    • 1970-01-01
    • 2012-05-04
    • 1970-01-01
    相关资源
    最近更新 更多