博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
system: system error facility
阅读量:6419 次
发布时间:2019-06-23

本文共 12682 字,大约阅读时间需要 42 分钟。

hot3.png

errno:

在C++标准库中被定义为预处理宏.如果想要使用需要#include<cerrno>.

是一套POSIX error codes用于获取当前线程运行时errors因此可以理解为errno为一个具有thread_local 生命周期的变量.

 

首先明确什么是Not all errors are exceptional:

例如: 在network-program中:

  • You were unable to connect to a remote IP address.
  • Your connection dropped out.
  • You tried to open an IPv6 socket but no IPv6 network interfaces are available.

这些都是可能发生的情况因此并不一定非要throw.我们可以通过errno的值来进行判断,进行对各种情况的处理.

 

demo1:

#include 
#include
#include
#include
#include
#include
#include
#include
#include
std::mutex global_mutex;//notice that: get the log of value which is less than 0;void function(const double& value)noexcept{ std::lock_guard
lg{ global_mutex }; std::cout << value << std::endl; std::log(value); std::cout << "errno: " << errno << std::endl;}int main(){ std::thread t1{ function, -1.0 }; std::thread t2{ function, 10.0 }; t1.join(); t2.join(); return 0;}

 

demo2:

#include 
#include
#include
#include
#include
int main(){ double not_a_number = std::log(-1.0); if (errno == EDOM) { std::cout << "some logic error occur!" << std::endl; } return 0;}

 

由于POSIX中的错误码可能不够用或者有的库作者不愿意使用于是就自己弄了一套自己的:

例如:

举一个例子,getaddrinfo() 族的函数使用独立的一套错误码(EAI_* ,有别于 POSIX 的 E* 错误码),但是(这些状态码)依然和 GetLastError() 处于同一套命名空间当中。SSL 使用另一套错误码.

Windows 的 GetLastError() 那堆是一套(WSA* 也在这里),POSIX 的 E* 是一套,SSL 的又是一套。

感谢@lh_mouse的指点.

 

于是自打C++11以来标准库提供了<system_error>来改善这种情况其指导思想为:

1),Not all errors are exceptional(不是所有的错误都是异常)

2),Errors come from multiple sources

3),Be user-extensible

4),Preserve the original error code

 

class error_category: 作为具体的error category的基类,比如std::system_category/std::iostream_category均继承自class error_category. 特别重要的是每个class error_category或者它的派生类提供class error_code和class error_condition之间的相互映射,同时class error_category或者它的派生类还提供对class error_condition的描述.

class error_code:  依赖平台(platform-dependent)的且在不同的category的class error_code下面的值(error code)是可以一样的, 每个class error_code object由一个error code和一个class error_category或者class error_category的派生类组成.

class error_condition: 不依赖平台(platform-independent)的error code(这里的error code就只是代表错误码,必须唯一的),同样每个class error_condtion object由一个 error code和一个class error_category或者class error_category的派生类组成.

enum class erroc: 一组通用的错误条件值,从POSIX派生而来。

 

接着让我们看一个例子

demo2:

void create_directory(    const std::string& pathname,    std::error_code& ec);
std::error_code ec;create_directory("/some/path", ec);

这个函数调用可能因为一下情况失败:

  • The directory already exists.
  • The path is too long.
  • The parent path doesn't exist.

如果只是想简单的判断是否创建成功下面这些是足够了的:

std::error_code ec;create_directory("/some/path", ec);if (!ec){  // Success.}else{  // Failure.}

但是假设我们想要细化一下错误的具体类型到底是哪种导致的呢?

std::error_code ec;create_directory("/some/path", ec);if (ec.value() == EEXIST) // No!  ...

这种情况下在POSIX platform的平台上面跑是没问题的但是如果并不是完全遵循POSIX的平台比如微软,这么写绝对是不对的,比如微软平台的是 ERROR_ALREADY_EXISTS 才对应上文的EEXIST.

于是C++0x以来解决了这个问题我们可以这样:

std::error_code ec;create_directory("/some/path", ec);if (ec == std::errc::file_exists)  ...

这样是没有问题的因为C++0x以来标准库实现了对 EEXIST or ERROR_ALREADY_EXISTS 到std::errc::file_exists转换的封装.

 

其中在demo2中:

if (ec == std::errc::file_exists)

虽然只是简单的一句 operator== 调用但是背后远远没有我们想象的那么简单.

Step1: 判断 enum(例如demo2中的 std::errc::file_exists)是属于class error_code还是class error_condition

具体判断的实现如下,标准库中把位于std::errc则个enum class归与class error_condition比较,然后由于operator== 提供了class error_code和class error_condition之间的比较但是我们比较的却是一个enum类型于是这里也涉及到一个隐式的转换.

这里的隐式转换涉及到 SFINAE:

因为std::errc既能转为class error_condition又能转为class error_code.

namespace std{template 
struct is_error_code_enum : public false_type {};template
struct is_error_condition_enum : public false_type {};template <>struct is_error_condition_enum
: true_type {};bool operator==(const error_condition& lhs, const error_condition& rhs) noexcept;bool operator==(const error_code& lhs, const error_condition& rhs) noexcept;bool operator==(const error_condition& lhs, const error_code& rhs) noexcept;class error_condition{ template
::value, void>::type> error_condition(ErrorConditionEnum e);//notice that: the enum value can change inexplicitly to std::error_condition { *this = make_error_condition(e); }};class error_code{ template
::value, void>::type> error_code(ErrorCodeEnum e); //notice that: the enum value can change inexplicitly to std::error_code { *this = make_error_code(e); }};}

 

看了上面的肯定还是不太明白class error_category去哪了?

class error_category做为其中重要的一环,用来指示该错误所属的category,既然有疑惑让我们接着往下看:

namespace std{inline error_condition make_error_condition(std::errc _Errno) noexcept	{	// make an error_condition	return (error_condition((int)_Errno, generic_category()));	}inline error_code make_error_code(std::errc _Errno) noexcept	{	// make an error_code	return (error_code((int)_Errno, generic_category()));	}}

其中的std::generic_category()又再次带给了我们新的问题这个函数是干什么的呢?

这个函数并不是提供给库的使用者使用的,但是又非常的重要该函数的实现必须保证每次返回的都是对同一个object的引用,在operator==比较的过程中class error_category也要参与比较:

namespace{  struct generic_category : public std::error_category  {    virtual const char*    name() const noexcept    { return "generic"; }    virtual string    message(int i) const    {      return string(std::strerror(i));  //其实类似与std::to_string(int number);    }  };}namespace std{inline const error_category& generic_category(){  static generic_category instance;  return instance;}}

 

在 if (ec == std::errc::file_exists) 中经过上述一路波折终于 std::errc::file_exisits 转为了 std::error_condtion,接着就是 std::error_code == std::error_condtion比较的问题了,标准库提供了以下几个重载函数:

namespace std{  inline bool  operator==(const error_code& __lhs, const error_code& __rhs) noexcept  { return (__lhs.category() == __rhs.category()	    && __lhs.value() == __rhs.value()); }  inline bool  operator==(const error_code& __lhs, const error_condition& __rhs) noexcept  {    return (__lhs.category().equivalent(__lhs.value(), __rhs)	    || __rhs.category().equivalent(__lhs, __rhs.value()));  }  inline bool  operator==(const error_condition& __lhs, const error_code& __rhs) noexcept  {    return (__rhs.category().equivalent(__rhs.value(), __lhs)	    || __lhs.category().equivalent(__rhs, __lhs.value()));  }  inline bool  operator==(const error_condition& __lhs,	     const error_condition& __rhs) noexcept  {    return (__lhs.category() == __rhs.category()	    && __lhs.value() == __rhs.value());  }}

 

以我们的上面的比较为例子这里会调用这个函数:

inline bool  operator==(const error_code& __lhs, const error_condition& __rhs) noexcept  {    return (__lhs.category().equivalent(__lhs.value(), __rhs)	    || __rhs.category().equivalent(__lhs, __rhs.value()));  }

我们再次看不懂了怎么办呢?

那么我们就来解析一下class error_code和class error_condition:

 

class error_code(GCC 6.3 Debian):

struct error_code  {   //default-constructor!    error_code() noexcept    : _M_value(0), _M_cat(&system_category()) { }    //construct form a int value and a category!    error_code(int __v, const error_category& __cat) noexcept    : _M_value(__v), _M_cat(&__cat) { }    //constructor from a enum class value.    template
::value>::type> error_code(_ErrorCodeEnum __e) noexcept { *this = make_error_code(__e); } //接受一个int值和一个category. void assign(int __v, const error_category& __cat) noexcept { _M_value = __v; _M_cat = &__cat; } //设置为默认构造函数构造后的状态. void clear() noexcept { assign(0, system_category()); } //!!!!!!!!!!!!!!!!!!!!!!!!!!! //这里需要特别的注意: 这是一个template operator=(俗称: 泛型拷贝赋值运算符) //它并不会影响编译器合成 std::error_code::operator=(const std::error_code& other)noexcept. template
typename enable_if
<_ErrorCodeEnum>::value, error_code&>::type operator=(_ErrorCodeEnum __e) noexcept { return *this = make_error_code(__e); } int value() const noexcept { return _M_value; } const error_category& category() const noexcept { return *_M_cat; } error_condition error_code::default_error_condition() const noexcept { return category().default_error_condition(value()); } string message() const { return category().message(value()); } explicit operator bool() const noexcept { return _M_value != 0; } // DR 804. private: int _M_value; const error_category* _M_cat; };

 

class error_condition(GCC 6.3 Debian):

struct error_condition   {    //default-constructor!    error_condition() noexcept    : _M_value(0), _M_cat(&generic_category()) { }    //construct form a int value and a category(or inherit form std::error_category).     error_condition(int __v, const error_category& __cat) noexcept    : _M_value(__v), _M_cat(&__cat) { }    //construct form enum class.    template
::value>::type> error_condition(_ErrorConditionEnum __e) noexcept { *this = make_error_condition(__e); } void assign(int __v, const error_category& __cat) noexcept { _M_value = __v; _M_cat = &__cat; } //!!!!!!!!!!!!!!!!!!! //这里的: 泛化operator=跟上面的 std::error_code一样 //不会影响编译器产生std::error_condition::operator=(const std::error_condition& other)noexcept; template
typename enable_if
<_ErrorConditionEnum>::value, error_condition&>::type operator=(_ErrorConditionEnum __e) noexcept { return *this = make_error_condition(__e); } void clear() noexcept { assign(0, generic_category()); } // 19.4.3.4 observers int value() const noexcept { return _M_value; } const error_category& category() const noexcept { return *_M_cat; } _GLIBCXX_DEFAULT_ABI_TAG string message() const { return category().message(value()); } explicit operator bool() const noexcept { return _M_value != 0; } // DR 804. private: int _M_value; const error_category* _M_cat; };

 

class error_category(GCC 6.3 Debian):

namespace std{class error_category  {  public:    constexpr error_category() noexcept = default;    virtual ~error_category();    error_category(const error_category&) = delete;    error_category& operator=(const error_category&) = delete;    //pure-virtual function!    virtual const char*     name() const noexcept = 0;        //pure-virtual function!    virtual string    message(int) const = 0;     //virtual function  virtual  error_condition default_error_condition(int __i) const noexcept   {    if (*this == system_category())      return error_condition(__i, system_category());    return error_condition(__i, generic_category());  }  //virtual function  virtual  bool equivalent(int __i, const error_condition& __cond) const noexcept  { return default_error_condition(__i) == __cond; }  //virtual function  virtual  bool equivalent(const error_code& __code, int __i) const noexcept  {    if (*this == system_category()        && __code.category() == system_category())      return __code.value() == __i;    if (*this == generic_category()        && __code.category() == generic_category())      return __code.value() == __i;    return false;  }    bool     operator<(const error_category& __other) const noexcept    { return less
()(this, &__other); } bool operator==(const error_category& __other) const noexcept { return this == &__other; } bool operator!=(const error_category& __other) const noexcept { return this != &__other; } };}

 

 

结合上面的:

std::error_code ec;create_directory("/some/path", ec);if (ec == std::errc::file_exists)  ...

我们假设调用careate_directory("/some/path", ec)的时候:

ec.assign(static_cast<int>(std::errc::file_exists),  generic_category());

 

这里的std::errc::file_exists转为了:

std::error_condition{std::error::file_exists};

 

最终的的调用为:

inline bool  operator==(const error_code& __lhs, const error_condition& __rhs) noexcept  {    return (__lhs.category().equivalent(__lhs.value(), __rhs)	    || __rhs.category().equivalent(__lhs, __rhs.value()));  }

其中在_lhs.category().equivalent(_lsh.value(), rhs);调用的时候内部映射为:

//step1:  //virtual function  virtual  bool equivalent(int __i, const error_condition& __cond) const noexcept  { return default_error_condition(__i) == __cond; }//step2: virtual  error_condition default_error_condition(int __i) const noexcept   {    if (this == &(system_category()))//notice that: compare two pointers.      return error_condition(__i, system_category());    return error_condition(__i, generic_category());  }//step3:  inline bool  operator==(const error_condition& __lhs,	     const error_condition& __rhs) noexcept  {    return (&(__lhs.category()) == &(__rhs.category()) //notice that: compare two pointers.	    && __lhs.value() == __rhs.value());  }

 

转载于:https://my.oschina.net/SHIHUAMarryMe/blog/1073890

你可能感兴趣的文章
POJ 2513 Colored Sticks【欧拉通路】
查看>>
Java.lang的研究(分析包含的重要类和接口)
查看>>
node搭建本地服务器后端解决跨域问题
查看>>
UpdatePanel的用法详解
查看>>
twitter storm源码走读之4 -- worker进程中线程的分类及用途
查看>>
elasticsearch之节点重启
查看>>
免费的高分辨率图库——re:splashed 可用做网页背景、设计或桌面壁纸
查看>>
如何获取最新的代码?
查看>>
X上面有一道横线,怎么打出来?
查看>>
gitlab服务部署及使用
查看>>
NPOI将数据导出到Excel中
查看>>
c#基本函数
查看>>
跟我一起学XNA(1)让物体动起来①(附源码)
查看>>
There is no accident
查看>>
springboot初学---rabbitmq的使用
查看>>
QTreeWidgetItem和QTreeWidgetItemIterator
查看>>
DevOps
查看>>
vim的纵向编辑(高级用法)
查看>>
再见,OI
查看>>
延时并自动关闭MessageBox
查看>>