【问题标题】:is there any exception with error code in stl?stl中的错误代码是否有任何异常?
【发布时间】:2017-06-02 19:14:19
【问题描述】:

我阅读了一些关于异常和错误代码的使用以及它们何时适用的讨论。

我相信例外情况更好,但我有一个具体问题:

如果我抛出一个 std::runtime_error("this is an error") 然后我抓住它,我找不到错误是什么,我只是明白有一个错误。

我正在寻找的是一个异常,我可以抛出它并向其添加错误代码,以便稍后我可以在捕获它时检查错误代码,例如:

 enum errorCodes
 {
        error_1,
        error_2,
        error_3
 }

 try
 {
     throw std::runtime_error(error_1,"can not do the job!");
 }
 catch (std::runtime_error & err)
 {
     switch (err.errorCode)
     {
         case error_1:
            // I can not contunue, so re throw it
             rethrow err;
         case error_2:
            // it is not important for me, so I can continue
           break;
        case error_3: 
            //Oh, I need to do something before continue
            re_init();
            break;
        default:
             rethrow err;
  }

我可以写这样的异常,但在写之前,我想知道这种类型的异常是否存在于 STL 或 BOOST 中?

有没有更好的方法来做到这一点(我对抛出不同类型的异常不感兴趣,所以从我的角度来看,捕获不同类型的异常并不是一个解决方案。

【问题讨论】:

  • "(我对抛出不同类型的异常不感兴趣,所以从我的角度来看,捕获不同类型的异常并不是一个解决方案。” 但是 IMO,它是走的路。你可以摆脱你的开关,只捕获你想要管理的错误(在你的例子中只有error_2error_3
  • @Jarod42 但这意味着您需要开发不同类型的异常并抛出它们。例如,如果我想读取一个文件,我可能会遇到两个错误:文件名无效并且没有内存,所以即使对于将文件内容读入缓冲区的简单任务,我也需要两种类型的异常,这不好。
  • 我更喜欢特定异常 (Demo) 而不是错误代码 (Demo)

标签: c++ exception boost stl


【解决方案1】:

是的,有std::system_error。它源自std::runtime_error。它非常面向 Unix,但它确实支持一般的错误代码,我建议你以这种方式使用它。


以下代码演示了如何为特定于应用程序的错误代码定义自己的错误类别:

#include <string>
#include <system_error>
#include <typeinfo>

namespace my{
    struct Error_code
    {
        enum Enum
        {
            error_1 = 101,
            error_2 = 102,
            error_3 = 103
        };
    };

    class App_error_category
        : public std::error_category
    {
    using Base = std::error_category;
    public:
        auto name() const noexcept
            -> char const*
            override
        { return "App error"; }

        auto default_error_condition( int const code ) const noexcept
            -> std::error_condition
            override
        { (void) code; return {}; }

        auto equivalent( int const code, std::error_condition const& condition ) const noexcept
            -> bool
            override
        { (void) code; (void) condition; return false; }

        // The intended functionality of this func is pretty unclear.
        // It apparently can't do its job (properly) in the general case.
        auto equivalent( std::error_code const& code, int const condition ) const noexcept
            -> bool
            override
        { return Base::equivalent( code, condition ); }

        auto message( int const condition ) const
            -> std::string
            override
        { return "An application error occurred, code = " + std::to_string( condition ); }

        constexpr
        App_error_category(): Base{} {}
    };

    auto app_error_category()
        -> App_error_category const&
    {
        static App_error_category the_instance;
        return the_instance;
    }

    class App_error
        : public std::system_error
    {
    using Base = std::system_error;
    public:
        auto app_error_code() const
            -> Error_code::Enum
        { return static_cast<Error_code::Enum>( code().value() ); }

        App_error( Error_code::Enum const code )
            : Base{ code, app_error_category() }
        {}

        App_error( Error_code::Enum const code, std::string const& description )
            : Base{ code, app_error_category(), description }
        {}
    };

}  // namespace my

void foo()
{
    try
    {
        throw my::App_error( my::Error_code::error_1, "can not do the job!" );
    }
    catch( my::App_error const& x )
    {
        switch( x.app_error_code() )
        {
        case my::Error_code::error_1:
            // I can not contunue, so re throw it
            throw;
        case my::Error_code::error_2:
            // it is not important for me, so I can continue
            break;
        case my::Error_code::error_3: 
            //Oh, I need to do something before continue
            //re_init();
            break;
        }
    }
}

#include <iostream>
#include <stdlib.h>     // EXIT_SUCCESS, EXIT_FAILURE
using namespace std;
auto main()
    -> int
{
    try
    {
        foo();
        return EXIT_SUCCESS;
    }
    catch( exception const& x )
    {
        cerr << "!" << x.what() << endl;
    }
    return EXIT_FAILURE;
}

使用 Visual C++ 2015 和 MingW g++ 6.4.0 这会产生输出

!can't do the job!: 发生应用程序错误,代码 = 101

一般来说,定义特定的异常类比使用错误代码更实用。但是,对于系统错误代码,将这些代码与异常一起传递更为实际。而对于,可以避免上面显示的复杂性,因为std::system_category 非常适合。

简而言之,这里的复杂性源于您不随波逐流的要求,而是在某种程度上逆流而上的方向。

【讨论】:

  • 那么我如何创建自定义错误代码并确保它们不会与系统生成的错误代码冲突?
  • @mans:您使用“类别”。我编写了一些几乎可以工作的代码,也许你可以弄清楚如何修复它:godbolt.org/g/WMhvDU
  • @mans:查看constructor documentation。基本上有一个输入代码和一个error category。错误类别定义了错误代码值的含义。
  • 谢谢:这是什么意思?它是 C++ 14 的一部分吗?我在 C++ 11 中看不到它
  • 这都是 C++11 中引入的东西。我已经链接到相关的在线文档。在标准的 PDF 中,您可以只使用搜索功能。
【解决方案2】:

考虑使用开箱即用的std::system_error

请注意,简单地使用 throw; 而不使用参数是一种更好的重新抛出异常的方法(您似乎正在使用 throw err;),因为前者会抛出异常 按引用而不是按值:后者会导致对象切片。

【讨论】:

  • 由于“rethrow err”在语法上作为 C++ 代码无效,因此我认为它是伪代码。
  • 我确实在里面埋了一个“似乎”来掩盖我的 a** ;-) 如果你让我删掉你对 std::system_error 的引用,我要否支持你的回答? (我刚刚完成)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多