Boost C++ 库

……世界上备受推崇且设计精湛的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ Coding Standards

介绍 - Boost C++ 函数库
PrevUpHomeNext

引言

该库旨在作为所有内置整数类型的“即插即用”替代品,适用于任何必须

  • 可证明且可验证正确的程序。

  • 检测所有用户错误,例如输入、赋值等。

  • 在上述约束条件下尽可能高效。

问题

C/C++ 中的算术运算**不保证**产生正确的数学结果。此功能继承自 C 的早期。 `int`、`unsigned int` 等类型的行为旨在密切匹配底层硬件。计算机硬件将这些类型实现为固定数量的位。当算术运算的结果超出此位数时,结果将不是算术上正确的。以下示例仅说明了导致此类问题的一个示例。

int f(int x, int y){
    // this returns an invalid result for some legal values of x and y !
    return x + y;
}

C/C++ 程序员有责任确保此行为不会导致程序运行不正确或意外。没有语言设施可以实现此类保证。程序员需要单独检查每个表达式,才能知道他的程序不会返回无效结果。有多种方法可以做到这一点。在上例中,[INT32-C] 似乎推荐以下方法

int f(int x, int y){
  if (((y > 0) && (x > (INT_MAX - y))) 
  || ((y < 0) && (x < (INT_MIN - y)))) {
    /* Handle error */
  }
  return x + y;
}

这确实会捕获错误。但是,程序员以这种方式修改代码将是乏味且费力的。对所有算术运算以这种方式修改代码可能会使代码难以阅读,并增加潜在编程错误的另一个来源。当表达式稍微复杂一点时,如以下示例所示,此方法显然是不可行的。

int f(int x, int y, int z){
    // this returns an invalid result for some legal values of x and y !
    return x + y * z;
}

此示例仅解决了应用于 `int` 类型(int)的加法运算溢出相关的未定义/错误行为的问题。其他内置整数类型(如 `unsigned`、`long` 等)也存在类似问题。其他运算(如减法、乘法等)也存在类似问题。C/C++ 在实现二进制运算的过程中,经常会自动且静默地将某些整数类型转换为其他类型。有时,此类转换会静默更改算术值,从而引入错误。C/C++ 标准将右移负数等行为指定为“实现定义的行为”。如今,大多数机器会按照程序员的预期执行——但这种行为并未得到保证。依赖这种行为将创建一个无法保证可移植性的程序。然后还有未定义行为。在这种情况下,编译器编写者没有义务做任何特定的事情。有时这会意外破坏程序。至少,该程序将失去可移植性。最后,还有一些行为从一开始在算术上就是错误的——例如除以零。一些运行时环境会直接终止程序,另一些可能会抛出某种异常。无论哪种情况,执行都已以无法恢复的方式失败。

以上所有条件都阻碍了创建永不失败的程序的创建。Safe Numerics Library 解决了所有这些条件,至少在整数运算方面是如此。

由于问题及其解决方案相似,我们将当前讨论仅限于上面显示的示例。

解决方案

该库实现了 `int`、`unsigned` 等类型的特殊版本,其行为与原始类型完全相同,**除了**这些操作的结果保证是算术正确**或**会引发错误。使用该库,上述示例将呈现为

#include <boost/safe_numerics/safe_integer.hpp>
using namespace boost::safe_numerics;
safe<int> f(safe<int> x, safe<int> y){
  return x + y; // throw exception if correct result cannot be returned
}
[Note] 注意

本文档中的库代码驻留在 `boost::safe_numerics` 命名空间中。为了提高文本的可读性,该命名空间通常已从文本、代码和示例中删除。

在运行时(如果可能)或在编译时检查加法表达式,以捕获导致算术行为不正确的任何可能错误。算术表达式不会产生错误的结果。相反,保证只发生以下情况之一。

  • 表达式将产生正确的数学结果

  • 表达式将发出编译错误。

  • 表达式将调用运行时异常。

换句话说,**该库绝对保证任何整数算术表达式都不会产生错误的结果**。

工作原理

该库实现了 `int`、`unsigned` 等类型的特殊版本,名为 `safe`、`safe` 等。它们的操作与底层类型完全相同,**除了**使用这些类型的表达式满足上述保证。这些“安全”类型旨在成为同名内置类型的“即插即用”替代品。因此,像将 `signed` 值赋给 `unsigned` 值这样合法的事情,不会在编译时被捕获,因为它们是合法的 C/C++ 代码。相反,它们将在运行时进行检查,以捕获该(合法)操作会导致算术结果不正确的案例。

请注意,该库处理由直接 C/C++ 表达式产生的算术错误。其中一些算术错误被定义为符合 C/C++ 标准,而另一些则不符合。因此,将此库仅描述为处理 C/C++ 数值表达式的未定义行为将是误导性的。

利用 C++14 特有的功能来最大程度地减少任何运行时开销。在许多情况下,根本没有运行时开销。在其他情况下,使用该库的程序可以稍作修改,以在没有任何运行时开销的情况下实现上述保证。

附加功能

安全类型的操作由模板参数决定,这些参数指定一对策略类,用于指定类型提升和错误处理的行为。除了用作标准整数类型的“即插即用”替代品外,用户还可以

  • 选择或定义异常策略类来指定异常的处理方式。

    • 在运行时抛出异常,如果可能,则在编译时捕获。

    • 在编译时捕获所有可能在运行时失败的操作。

    • 指定在运行时检测到错误时应调用的自定义函数。

  • 选择或定义类型提升策略类来更改 C/C++ 类型提升规则。这可用于

    • 使用 C/C++ 本机类型提升规则,这样,除了由于产生不正确算术行为的操作而导致抛出/捕获异常外,程序在使用/不使用安全类型时将运行相同。如果仅在调试和测试期间启用安全类型,则可能会使用此方法。

    • 用算术上等效但可最大程度减少运行时检查算术结果需求的规则替换 C/C++ 本机提升规则。此类策略将有效地改变 C++ 程序的语义。它不再真正是 C++ 了。程序在使用普通整数类型时,其功能可能与现在不同。

    • 用模拟其他机器架构的规则替换 C/C++ 本机提升规则。这旨在允许在开发平台上测试旨在在另一台机器上运行的 C/C++ 代码。这种情况在开发嵌入式系统代码时经常发生。

  • 使用有界整数类型强制执行其他程序要求。该库包含范围和字面量的类型。违反这些要求的操作将在编译时或运行时被捕获,而不会静默返回无效值。这些类型可用于提高程序的正确性和性能。

要求

该库完全由 C++ 头文件组成。它需要一个与 C++14 标准兼容的编译器。

要使用此库,必须安装以下 Boost 库:

  • mp11

  • integer

  • config

  • tribool

  • enable_if

  • type_traits

Safe Numerics 库随附一套全面的测试程序。

范围

该库目前仅适用于内置整数类型。浮点类型也存在类似的问题,但当前版本的库并未解决这些问题。用户或库定义的类型(如任意精度整数)也可能存在此问题。当前未开发将此库扩展到这些其他类型,但将来可能会解决。这也是该库名称为“safe numeric”(安全数值)而不是“safe integer”(安全整数)库的原因之一。

PrevUpHomeNext