C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++ Boost Exception

C++ Boost Exception超详细讲解

作者:无水先生

Boost是为C++语言标准库提供扩展的一些C++程序库的总称。Boost库是一个可移植、提供源代码的C++库,作为标准库的后备,是C++标准化进程的开发引擎之一,是为C++语言标准库提供扩展的一些C++程序库的总称

Boost.Exception 库提供了一种新的异常类型 boost::exception,它允许您在抛出异常后将数据添加到异常中。此类型在 boost/exception/exception.hpp 中定义。由于 Boost.Exception 将其类和函数分布在多个头文件中,以下示例访问主头文件 boost/exception/all.hpp 以避免一个接一个地包含头文件。

Boost.Exception 支持 C++11 标准的机制,该机制将异常从一个线程传输到另一个线程。 boost::exception_ptr 类似于 std::exception_ptr。但是,Boost.Exception 并不能完全替代标准库中的头文件异常。例如,Boost.Exception 缺少对 std::nested_exception 类型的嵌套异常的支持。

注意

要使用 Visual C++ 2013 编译本章中的示例,请删除关键字 noexcept。此版本的 Microsoft 编译器尚不支持 noexcept。

示例 56.1。使用 boost::exception

#include <boost/exception/all.hpp>
#include <exception>
#include <new>
#include <string>
#include <algorithm>
#include <limits>
#include <iostream>
typedef boost::error_info<struct tag_errmsg, std::string> errmsg_info;
struct allocation_failed : public boost::exception, public std::exception
{
  const char *what() const noexcept { return "allocation failed"; }
};
char *allocate_memory(std::size_t size)
{
  char *c = new (std::nothrow) char[size];
  if (!c)
    throw allocation_failed{};
  return c;
}
char *write_lots_of_zeros()
{
  try
  {
    char *c = allocate_memory(std::numeric_limits<std::size_t>::max());
    std::fill_n(c, std::numeric_limits<std::size_t>::max(), 0);
    return c;
  }
  catch (boost::exception &e)
  {
    e << errmsg_info{"writing lots of zeros failed"};
    throw;
  }
}
int main()
{
  try
  {
    char *c = write_lots_of_zeros();
    delete[] c;
  }
  catch (boost::exception &e)
  {
    std::cerr << boost::diagnostic_information(e);
  }
}

Example56.1

示例 56.1 调用函数 write_lots_of_zeros(),该函数又调用 allocate_memory()。 allocate_memory() 动态分配内存。该函数将 std::nothrow 传递给 new 并检查返回值是否为 0。如果内存分配失败,则会抛出 allocation_failed 类型的异常。如果 new 分配内存失败,allocation_failed 替换默认抛出的异常 std::bad_alloc 。

write_lots_of_zeros() 调用 allocate_memory() 以尝试分配最大可能大小的内存块。这是在 std::numeric_limits 的 max() 的帮助下完成的。该示例故意尝试分配那么多内存以使分配失败。

allocation_failed 源自 boost::exception 和 std::exception。不需要从 std::exception 派生类。 allocation_failed 也可以派生自不同类层次结构的类,以便将其嵌入现有框架中。虽然示例 56.1 使用标准定义的类层次结构,但仅从 boost::exception 派生 allocation_failed 就足够了。

如果捕获到 allocation_failed 类型的异常,allocate_memory() 必须是异常的来源,因为它是唯一抛出此类异常的函数。在有许多函数调用 allocate_memory() 的程序中,知道异常的类型不再足以有效地调试程序。在这些情况下,了解哪个函数试图分配比 allocate_memory() 所能提供的更多的内存会有所帮助。

挑战在于 allocate_memory() 没有任何附加信息,例如调用者姓名,可以添加到异常中。 allocate_memory() 无法丰富异常。这只能在调用上下文中完成。

使用 Boost.Exception,可以随时将数据添加到异常中。您只需为需要添加的每一位数据定义一个基于 boost::error_info 的类型。

boost::error_info 是一个需要两个参数的模板。第一个参数是一个标签,用于唯一标识新创建的类型。这通常是具有唯一名称的结构。第二个参数是指存储在异常中的值的类型。示例 56.1 定义了一个新类型 errmsg_info——通过结构 tag_errmsg 唯一标识——它存储一个 std::string 类型的字符串。

在 write_lots_of_zeros() 的捕获处理程序中,errmsg_info 用于创建一个用字符串“写入大量零失败”初始化的对象。然后使用 operator<< 将该对象添加到 boost::exception 类型的异常中。然后异常被重新抛出。

现在,异常不仅仅表示内存分配失败。它还表示,当程序试图在函数 write_lots_of_zeros() 中写入大量零时,内存分配失败。知道哪个函数称为 allocate_memory() 可以更轻松地调试较大的程序。

要从异常中检索所有可用数据,可以在 main() 的捕获处理程序中调用函数 boost::diagnostic_information()。 boost::diagnostic_information() 为传递给它的每个异常调用成员函数 what() 并访问存储在异常中的所有附加数据。 boost::diagnostic_information() 返回一个 std::string 类型的字符串,例如,它可以写入标准错误。

当使用 Visual C++ 2013 编译时,示例 56.1 将显示以下消息:

Throw location unknown (consider using BOOST_THROW_EXCEPTION)
Dynamic exception type: struct allocation_failed
std::exception::what: allocation failed
[struct tag_errmsg *] = writing lots of zeros failed

该消息包含异常类型、从 what() 检索到的错误消息以及描述,包括结构名称。

boost::diagnostic_information() 在运行时检查给定异常是否源自 std::exception。 what() 只有在这种情况下才会被调用。

抛出 allocation_failed 类型异常的函数名称未知。

Boost.Exception 提供了一个宏来抛出异常,该异常不仅包含函数名,还包含文件名和行号等附加数据。

示例 56.2。更多数据与 BOOST_THROW_EXCEPTION

#include <boost/exception/all.hpp>
#include <exception>
#include <new>
#include <string>
#include <algorithm>
#include <limits>
#include <iostream>
typedef boost::error_info<struct tag_errmsg, std::string> errmsg_info;
struct allocation_failed : public std::exception
{
  const char *what() const noexcept { return "allocation failed"; }
};
char *allocate_memory(std::size_t size)
{
  char *c = new (std::nothrow) char[size];
  if (!c)
    BOOST_THROW_EXCEPTION(allocation_failed{});
  return c;
}
char *write_lots_of_zeros()
{
  try
  {
    char *c = allocate_memory(std::numeric_limits<std::size_t>::max());
    std::fill_n(c, std::numeric_limits<std::size_t>::max(), 0);
    return c;
  }
  catch (boost::exception &e)
  {
    e << errmsg_info{"writing lots of zeros failed"};
    throw;
  }
}
int main()
{
  try
  {
    char *c = write_lots_of_zeros();
    delete[] c;
  }
  catch (boost::exception &e)
  {
    std::cerr << boost::diagnostic_information(e);
  }
}

使用宏 BOOST_THROW_EXCEPTION 代替 throw,函数名、文件名和行号等数据会自动添加到异常中。但这仅在编译器支持附加数据的宏时才有效。虽然 __FILE__ 和 __LINE__ 等宏自 C++98 以来就已标准化,但获取当前函数名称的宏 __func__ 仅在 C++11 中成为标准。由于许多编译器在 C++11 之前提供了这样的宏,BOOST_THROW_EXCEPTION 会尝试识别底层编译器并使用相应的宏(如果存在)。

使用 Visual C++ 2013 编译,示例 56.2 显示以下消息:

main.cpp(20): Throw in function char *__cdecl allocate_memory(unsigned int)
Dynamic exception type: class boost::exception_detail::clone_impl<struct boost::exception_detail::error_info_injector<struct allocation_failed> >
std::exception::what: allocation failed
[struct tag_errmsg *] = writing lots of zeros failed

在示例 56.2 中,allocation_failed 不再派生自 boost::exception。 BOOST_THROW_EXCEPTION 访问函数 boost::enable_error_info(),它标识异常是否源自 boost::exception。如果不是,它会创建一个从指定类型和 boost::exception 派生的新异常类型。这就是为什么上面显示的消息包含与 allocation_failed 不同的异常类型。

示例 56.3。使用 boost::get_error_info() 有选择地访问数据

#include <boost/exception/all.hpp>
#include <exception>
#include <new>
#include <string>
#include <algorithm>
#include <limits>
#include <iostream>
typedef boost::error_info<struct tag_errmsg, std::string> errmsg_info;
struct allocation_failed : public std::exception
{
  const char *what() const noexcept { return "allocation failed"; }
};
char *allocate_memory(std::size_t size)
{
  char *c = new (std::nothrow) char[size];
  if (!c)
    BOOST_THROW_EXCEPTION(allocation_failed{});
  return c;
}
char *write_lots_of_zeros()
{
  try
  {
    char *c = allocate_memory(std::numeric_limits<std::size_t>::max());
    std::fill_n(c, std::numeric_limits<std::size_t>::max(), 0);
    return c;
  }
  catch (boost::exception &e)
  {
    e << errmsg_info{"writing lots of zeros failed"};
    throw;
  }
}
int main()
{
  try
  {
    char *c = write_lots_of_zeros();
    delete[] c;
  }
  catch (boost::exception &e)
  {
    std::cerr << *boost::get_error_info<errmsg_info>(e);
  }
}

Example56.3

示例 56.3 没有使用 boost::diagnostic_information(),它使用 boost::get_error_info() 直接访问 errmsg_info 类型的错误消息。因为 boost::get_error_info() 返回类型为 boost::shared_ptr 的智能指针,所以 operator* 用于获取错误消息。如果传递给 boost::get_error_info() 的参数不是 boost::exception 类型,则返回空指针。如果宏 BOOST_THROW_EXCEPTION 始终用于抛出异常,则异常将始终从 boost::exception 派生——在这种情况下无需检查返回的智能指针是否为 null。

到此这篇关于C++ Boost Exception超详细讲解的文章就介绍到这了,更多相关C++ Boost Exception内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文