Boost C++ 库

...世界上最受推崇和设计最精良的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu,《C++ 编码标准

PrevUpHomeNext

第 44 章。Boost.YAP

Zach Laine

根据 Boost 软件许可协议 1.0 版分发。(请参阅随附文件 LICENSE_1_0.txt 或在 https://boost.ac.cn/LICENSE_1_0.txt 复制副本)

目录

简介
手册
表达式模板入门
YAP 之道
表达式
混合搭配表达式模板
表达式的种类
运算符
转换表达式
求值表达式
运算符宏
表达式操作数的处理方式
打印
示例
头文件组织
配置
目标代码
概念
编译器支持
依赖项
参考
头文件
原理

“我喜欢用引言开始文档。一句简洁明了的引言。”

— Eric Niebler(意译)

动机

表达式模板非常棒。它们被用于许多库中;这里仅举三个最令人印象深刻的例子

  • Boost.Spirit 允许您编写 EBNF 风格的语法,该语法被转换为 PEG 解析器。
  • Eigen 允许您使用非常自然和数学化的表达式语法进行线性代数运算,Eigen 使用该语法来深度优化您的表达式。
  • NT2 采用略微修改的 MatLab 代码,并允许将其解析并作为高度优化的 C++ 代码运行。

然而,这可能会付出高昂的代价。表达式模板的实现和维护成本很高。Eigen 和 Boost.Ublas 各自都有大量的复杂表达式模板代码,这些代码无法在其他地方重用。

借助 C++14 和 C++17 标准中可用的语言特性,现在可以轻松编写和使用表达式模板库,并且编译时间非常合理。

作为一个快速示例,假设我们正在进行一些矩阵数学运算,并且我们编写了以下语句

D = A * B + C;

其中所有变量都是矩阵。事实证明,为 A * B 创建一个临时变量,然后再为结果乘积加上 C 创建另一个临时变量是非常低效的。大多数矩阵数学库都会有一个函数一次完成它

mul_add_assign(D, A, B, C);

如果您使用的矩阵库同时提供这两种语法,您必须注意到何时应该用更高效的函数替换某些使用运算符的代码;这是乏味且容易出错的。如果库根本不提供运算符语法,而仅提供更高效的函数调用,则使用该库的代码的可写性和可读性都会大大降低。

使用 Boost.YAP,您可以编写一些库代码,使诸如 D = A * B + C 之类的表达式能够自动转换为诸如 mul_add_assign(D, A, B, C) 之类的表达式。

考虑另一个例子。我们中的许多人都使用 Unix 命令行工具来删除文件中的重复行

sort file_with_duplicates | uniq > file_without_duplicates

当然,我们可以使用标准算法做非常相似的事情

std::vector<int> v1 = {0, 2, 2, 7, 1, 3, 8};
std::sort(v1.begin(), v1.end());
auto it = std::unique(v1.begin(), v1.end());
std::vector<int> const v2(v1.begin(), it);
assert(v2 == std::vector<int>({0, 1, 2, 3, 7, 8}));

但是,如果我们的代码完全做到这一点,但使用更简洁的语法,那就更好了

std::vector<int> v1 = {0, 2, 2, 7, 1, 3, 8};
std::vector<int> const v2 = sort(v1) | unique;
assert(v2 == std::vector<int>({0, 1, 2, 3, 7, 8}));

这看起来与上面的 Unix 命令行非常相似。(让我们假设 Range-v3 还没有做到几乎完全一样的事情。)

Boost.YAP 可以用来完成这两件事,而且代码量非常少。实际上,如果您想了解第二个例子是如何实现的,您可以直接跳到 可管道化算法 示例。

特性

  • 简单的 ExpressionTemplateExpression 概念可以很容易地通过用户代码建模。可以使用紧凑的宏在 ExpressionTemplatesExpressions 上添加成员函数和非成员函数,并且存在一个模拟 ExpressionTemplate 的引用模板,用于原型设计或实验。
  • Boost.YAP 表达式的求值尽可能地匹配内置 C++ 表达式的语义。这使得对表达式求值的语义有更清晰的理解,因为定义是类型本地的。
  • 表达式可以在用户定义的方式中显式转换。这是通过转换类中重载的调用运算符来实现的,这些运算符与整个表达式中的子表达式匹配。虽然这些成员函数可以将子表达式转换为任何内容,但常见的模式是将一些子表达式转换为新的子表达式或适当的值,而将其他子表达式保持不变。这种 evaluate(transform(expr)) 惯用法预计将成为使用 Yap 操作和求值表达式的最常见方式之一。
  • 操作或创建表达式的函数。提供了(并在 Boost.YAP 中使用)操作表达式或其子表达式的函数。例如,这些简化了编写用户定义的转换的过程。

PrevUpHomeNext