Boost C++库

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

Boost库需求和指南

引言

此页面描述了提交到Boost的库内容的需求和指南。

有关所涉及流程的描述,请参阅Boost库提交流程页面。

需求

为了避免因提议的库被拒绝而导致的挫折和时间浪费,必须满足以下需求。

作者在提交之前没有义务阅读邮件列表一段时间。但是,已经注意到,那些以“我刚开始阅读这个邮件列表……”开头提交的库似乎常常失败,而且往往令人尴尬。

许可证需求

满足许可证需求的首选方法是使用Boost软件许可证。请参阅许可证信息。如果由于任何原因您不打算使用Boost软件许可证,请先在Boost 开发者邮件列表上讨论相关问题。

许可证需求

  • 必须易于阅读和理解。
  • 必须无偿授予复制、使用和修改软件以用于任何用途(商业和非商业)的许可。
  • 必须要求许可证出现在所有软件源代码副本中。
  • 不要求许可证与可执行文件或库的其他二进制用途一起出现。
  • 不要求可执行文件或库的其他二进制用途需要提供源代码。
  • 可以限制将库的名称和描述的使用范围限制在Boost网站上找到的标准版本。

可移植性需求

  • 库的接口必须可移植,并且不限于特定的编译器或操作系统。
  • 如果可能,库的实现必须可移植,并且不限于特定的编译器或操作系统。如果无法实现可移植的实现,如果非可移植的构造很容易移植到其他环境,并且至少为两个流行的操作系统(例如UNIX和Windows)提供了实现,则这些构造是可以接受的。
  • 库至少可以在两个实现最新ISO C++标准的C++编译器上运行。
  • 库不需要在不符合ISO标准的C++编译器上运行。
  • 库不需要在任何特定的C++编译器上运行。Boost贡献者经常尝试确保他们的库与流行的编译器一起工作。boost/config.hpp 配置头文件是解决编译器缺陷的首选机制。

由于没有绝对的方法来证明可移植性,许多Boost提交通过使用两个不同的C++编译器(通常在不同的操作系统下)正确编译和执行来证明实际的可移植性。否则,审阅者可能会怀疑移植实际上是否可行。

所有权

您确定拥有您打算提交的库吗?MJ Salone的“如何对软件进行版权保护”,Nolo出版社,1990年版指出:

在您自己的时间里所做的工作与您在公司时间里为雇主所做的编程工作非常相似,这可能会引发严重的法律问题。在这种情况下,最好事先获得雇主的书面许可。

在您提交的所有重要文件中放置版权声明。Boost不会接受没有明确版权信息的库。

组织结构

Boost库的质量不仅在于API和代码设计,还在于向库的整体用户呈现一致的视图。接受后,库必须遵守此目录和文件结构。

Boost标准库组织结构
子目录或文件 内容 必需
build 库构建文件,例如Jamfile、IDE项目、Makefile、Cmake文件等。 如果库有需要构建的源代码,则必需。
config 用于构建时配置检查的文件。此目录可能包含在构建库、测试或示例时使用的源文件和构建系统脚本,以检查目标系统是否满足某些条件。例如,检查可以测试编译器是否实现了某个特性,或者目标系统是否支持某个API。 可选。
doc 用于构建和生成的库文档的源代码。如果库需要从非HTML文件构建文档,则此位置必须可以使用Boost Build进行构建。 所有库都必需。
doc/html 文档(HTML)文件。 所有具有预生成文档的库都必需。并且生成的文档必须在此处生成。
example 示例程序文件。 如果库有示例文件,则必需。强烈推荐。
index.html 重定向到HTML文档。有关此文件的模板,请参阅"重定向" 所有库都必需。
include/boost/library 库的头文件。 所有库都必需。
meta 有关库的元数据。 所有库都必需。
meta/libraries.json 包含有关库信息的JSON文件,用于为Boost C++库集合生成网站和文档。 所有库都必需。
meta/explicit-failures-markup.xml 描述预期测试失败的XML文件,用于生成测试报告。 可选
src 必须编译才能构建库的源文件。 如果库有需要构建的源文件,则必需。
test 回归测试或其他测试程序或脚本。这是自动测试唯一考虑的位置。如果您有需要作为自动测试一部分的其他位置,则需要此位置引用其他测试位置。 所有库都必需。
tools 库使用或提供的工具。其中的结构取决于库,但建议使用与常规Boost库或工具类似的结构。 对于具有可运行工具的库,必需。

集成

一旦库被接受为Boost C++库的一部分,就要求它能够正确地集成到开发、测试、文档和发布流程中。这种集成提高了所有库的最终质量,并且对于用户期望的整个Boost C++库的质量至关重要。除了上述组织结构需求之外,还需要以下集成:

构建源代码

库需要提供一个Boost Build项目,用户和顶级Boost项目可以使用该项目来构建库(如果库有需要构建的源代码)。源代码构建的Jamfile至少需要声明项目、库目标和注册要安装的目标。例如:

project boost/my_lib ;

lib boost_my_lib : a.cpp ;

boost-install boost_my_lib ;

测试

库需要提供一个Boost Build项目,用户和根Boost测试脚本可以使用该项目来构建和运行库的测试。测试构建项目必须位于project-root/test目录中,并且必须从此目录或其他目录构建(例如,b2 libs/library/test从Boost根目录必须有效)。

一个例子test/Jamfile如下所示

import testing ;

run default_constructor.cpp ;
run copy_constructor.cpp ;
compile nested_value_type.cpp ;
compile-fail invalid_conversion_1.cpp ;

警告:这是顶级测试脚本考虑的唯一测试位置。如果您想测试其他位置,则必须声明这些位置作为依赖项进行构建,或者使用build-project.

如果库需要C++一致性级别,这会阻止某些编译器或配置工作,则可以(也建议)在测试Jamfile中声明这些需求,以便不运行测试以节省测试资源,如下例所示:

import testing ;
import ../../config/checks/config : requires ;

project : requirements [ requires cxx11_variadic_templates cxx11_template_aliases ] ;

run cpp11_test.cpp ;

有关更多信息,请参阅Boost.Config文档

构建文档

库需要提供一个Boost Build项目来构建库的文档。该project-root/doc项目是顶级文档构建脚本和发布构建脚本引用的唯一位置。文档构建项目必须具有以下两个特性:

  1. 定义一个boostdoc目标。此目标可能是一个看起来大致类似的别名:
    alias boostdoc : my_boostbook_target
        : : : <implicit-dependency>my_boostbook_target ;
    
    但是,如果您的项目没有集成到全局文档手册中,您可以使用空的别名,例如:
    alias boostdoc ;
    
  2. 如果项目有任何文档,则项目必须默认为构建独立文档。发布脚本构建此默认值,以保证所有项目都具有最新的文档。

指南

请使用这些指南作为准备库提交内容的清单。并非每个指南都适用于每个库,但预计会做出合理的努力来遵守。

向后兼容性

Boost库通常拥有庞大且多样化的用户群。为了在这种情况下确保从旧API到新API的顺利过渡,鼓励库作者在引入库的重大更改时遵循以下几条准则。
  1. 非重大更改可以不受限制地进行。
  2. 可以进行小的重大更改,但应在发布更改前的几个版本中通知用户。大多数重大更改都属于此类。
  3. 对于大型重大更改从旧API到新API的迁移路径(例如boost::filesystemv2到v3),应在单独的目录/命名空间中引入新API,并应通知用户并给予他们几个版本的时间来迁移。一段时间后可以移除旧API。
  4. 对于大型重大更改没有迁移路径(例如boost::spiritv2到v3),应在单独的目录/命名空间中提供新API,并保留旧API(因为没有迁移路径)。移除API应被视为移除Boost库,这可以做到,但需要更长的弃用周期。
  5. 相当于库重新设计或重写的重大更改应视为新的库,并鼓励进行正式审查(或至少进行小型审查)。

设计与编程

首先要追求清晰性和正确性;在大多数Boost库中,优化应该只是次要考虑因素。

力求符合ISO标准C++。这意味着有效利用语言的标准特性,并避免非标准编译器扩展。这也意味着在适用时使用C++标准库。

头文件应是良好的邻居。参见头文件策略。参见命名一致性

遵循高质量的编程实践。例如,参见Scott Meyers撰写的《Effective C++》第二版和《More Effective C++》(Addison Wesley出版)。

使用C++标准库或其他Boost库,但仅当好处大于成本时才使用。不要使用C++标准库或Boost以外的库。参见库重用

阅读实现差异,了解如何提供性能、平台或其他实现差异。

浏览最佳实践手册,获取想法并链接到现有Boost库中的源代码。

阅读带有单独源代码的库指南,了解如何确保编译的链接库满足用户的期望。

使用C++标准库的命名约定(参见命名约定理由

  • 名称(除非下文另有说明)应全部小写,单词之间用下划线分隔。
  • 缩写应视为普通名称(例如,xml_parser而不是XML_parser)。
  • 模板参数名称以大写字母开头。
  • 宏(令人吃惊!)名称全部大写,并以BOOST_开头。

选择有意义的名称——显式优于隐式,可读性很重要。强烈偏好清晰且描述性的名称,即使很长。

在适当的情况下使用异常来报告错误,并编写在异常情况下安全的代码。

避免异常规范。参见异常规范理由

提供示例程序或信心测试,以便潜在用户可以看到如何使用您的库。

提供遵循测试策略和协议的回归测试程序。

虽然一些Boost成员在自己的代码中使用比例字体、制表符和不受限制的行长,但Boost广泛分发的源代码应遵循更保守的准则。

  • 使用等宽字体。参见字体理由
  • 使用空格而不是制表符。参见制表符理由
  • 将行长限制为80个字符。

所有文档文件(HTML或其他)结尾都应包含版权声明和许可声明。有关首选格式,请参见许可信息页面。

所有源文件(包括程序、头文件、脚本等)开头应包含:

  • 描述文件内容的注释行。
  • 描述版权和许可的注释:同样,首选格式在许可信息页面中指出。
  • 请注意,开发人员允许在其库的存储库中提供许可证文本的副本,文件名为LICENSE_1_0.txtLICENSE.txtLICENSE
  • 引用Boost网站上您库的注释行。例如:
    // See https://boost.ac.cn/libs/foo for library home page.
    

    其中foo是库的目录名称(见下文)。除了帮助找到与其文档分离的Boost文件的用户外,Boost的一些自动化工具也依赖于此注释来识别哪些库头文件属于哪个库。

断言:如果您想向代码中添加运行时断言(您应该这样做!),请避免使用C的assert宏,而应使用Boost的BOOST_ASSERT宏(位于boost/assert.hpp中)。它更易于配置。在公共头文件和库源代码(对于单独编译的库)中使用BOOST_ASSERT。在示例和文档中使用C的assert宏是可以的。

确保您的代码在存在min()max()宏的情况下可以编译。某些平台头文件定义了min()max()宏,这会导致某些常见的C++结构无法编译。一些简单的技巧可以保护您的代码免受不合适的宏替换。

  • 如果您想调用std::min()std::max()
    • 如果您不需要依赖参数的查找,请使用(std::min)(a,b)
    • 如果您需要依赖参数的查找,则应
      • #include <boost/config.hpp>
      • 使用BOOST_USING_STD_MIN();std::min()引入当前作用域。
      • 使用min BOOST_PREVENT_MACRO_SUBSTITUTION (a,b);min(a,b)进行依赖参数的调用。
  • 如果您想调用std::numeric_limits<int>::max(),请使用(std::numeric_limits<int>::max)()代替。
  • 如果您想调用min()max()成员函数,请使用(obj.min)()代替obj.min()
  • 如果您想声明或定义名为minmax的函数或成员函数,则必须使用BOOST_PREVENT_MACRO_SUBSTITUTION宏。您应该编写int min BOOST_PREVENT_MACRO_SUBSTITUTION () { return 0; }而不是int min() { return 0; }。这无论函数是自由(命名空间作用域)函数、成员函数还是静态成员函数都适用,并且它适用于函数声明和函数定义。

文件名

命名要求确保文件和目录名称具有相对的可移植性,包括对ISO 9660:1999(带扩展名)和其他相对有限的文件系统。

  • 名称只能包含小写1ASCII字母('a'-'z')、数字('0'-'9')、下划线('_')、连字符('-')和句点('.')。不允许使用空格2
  • 目录名称不能包含句点('.'3
  • 文件名的第一个和最后一个字符不能是句点('.'4
  • 名称的第一个字符不能是连字符('-'5
  • 目录和文件名的最大长度为31个字符6
  • 总路径长度不能超过207个字符7

其他约定简化了沟通

  • 打算由C++编译器作为翻译单元一部分进行处理的文件应具有以“pp”结尾的三字母文件名扩展名。其他文件不应使用以“pp”结尾的扩展名。此约定使识别Boost中的所有C++源代码变得容易。
  • 所有库在其最高级别都具有一个以特定库命名的主目录。参见命名一致性。主目录可能包含子目录。

重定向

主目录应始终包含一个名为index.html的文件。作者请求这样做,以便他们可以发布形式为https://boost.ac.cn/libs/lib-name的URL,并确保文档重组不会使URL无效。Boost的内部工具也通过知道库的文档始终可以通过简化的URL访问而得到简化。

主目录index.html文件应该只自动重定向到doc/html子目录。

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en">
<head>
  <title>Boost.Name Documentation</title>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <meta http-equiv="refresh" content="0; URL=doc/html/index.html" />
</head>

<body>
  Automatic redirection failed, please go to <a href=
  "doc/index.html">doc/index.html</a>
</body>
</html>

命名一致性

随着库开发者和用户积累了Boost的使用经验,以下一致的命名方法已被视为非常有用,特别是对于需要自身头文件子目录和命名空间的大型库。

其工作方式如下。库的名称应描述库的内容。强烈不鼓励使用隐晦的缩写。遵循C++标准库的做法,名称通常是单数而不是复数。例如,处理文件系统的库可以选择名称“filesystem”,而不是“filesystems”、“fs”或“nicecode”。

  • 库的主目录(在父目录boost-root/libs中)使用相同的名称。例如,boost-root/libs/filesystem
  • 库的主头文件目录(在boost-root/libs/name/include中)使用相同的名称。例如,boost-root/libs/filesystem/boost/filesystem
  • 库的主命名空间(在父命名空间::boost中)使用相同的名称,除非存在具有该名称的组件(例如,boost::tuple),在这种情况下,命名空间名称应改为复数。例如,::boost::filesystem

在编写Boost库文档时,请遵循以下约定(另请参见本文档的下一节)

  • 库名称以罗马字体设置。
  • 库名称的首字母大写。
  • 仅当库名称后不跟“library”一词时,才在“Boost”和库名称之间使用句点(例如,Boost.Bind)。
  • “library”一词不是库名称的一部分,因此小写。

以下是一些应用这些约定的示例:

  • Boost.Bind 由 Peter Dimov 编写。
  • Boost Bind 库由 Peter Dimov 编写。
  • 我经常使用 Bind,这是一个由 Peter Dimov 编写的 Boost 库。

文档

即使是最简单的库也需要一些文档;文档的数量应与需求成正比。文档应假设读者具备 C++ 的基本知识,但不一定是专家。

文档的格式应为 HTML,并且不应需要高级浏览器或服务器端扩展。样式表是可以接受的。不鼓励使用 ECMAScript/JavaScript。文档入口点应始终是一个名为 index.html 的文件;参见 重定向

编写文档没有唯一正确的方法。HTML 文档的组织方式通常与传统的印刷文档大相径庭。面向任务的风格与面向参考的风格有所不同。最终,问题归结为:文档是否足以让传说中的“普通”C++程序员成功使用该库?

文档中通常应包含以下主题:

  • 库的总体介绍。介绍尤其需要包括:
    • 对库的用途(以及可能不适用的用途)进行非常高级别的概述,即使是那些没有预先了解问题领域的人也能理解。
    • 使用库的最简单的(“Hello World”)示例。
  • 涵盖基本用例的教程。
  • 参考文档
    • 每个类的描述。
    • 类之间的关系。
    • 对于每个函数(如适用),描述、需求(前提条件)、效果、后置条件、返回值和抛出异常。
    • 错误检测和恢复策略的讨论。
  • 如何编译和链接。
  • 如何测试。
  • 版本或修订历史。
  • 设计决策的理由。参见 理由说明
  • 鸣谢。参见 致谢说明

如果您需要更多关于如何编写文档的帮助,您可以查看关于 为 Boost 编写文档 的文章。

理由说明

以下是一些要求和指南的理由。

异常规范说明

异常规范 [ISO 15.4] 有时用于指示可能抛出的异常,或者因为程序员希望它们能够提高性能。但是,请考虑智能指针中的以下成员:

T& operator*() const throw()  { return *ptr; }

此函数不调用任何其他函数;它只操作指针等基本数据类型。因此,永远不会调用异常规范的任何运行时行为。该函数完全暴露给编译器;实际上它是内联声明的。因此,一个智能编译器可以很容易地推断出这些函数无法抛出异常,并进行与基于空异常规范所做的相同的优化。“愚笨”的编译器可能会进行各种各样的负优化。

例如,如果存在异常规范,一些编译器会关闭内联。一些编译器会添加 try/catch 块。这种负优化可能会导致性能灾难,从而使代码在实际应用中无法使用。

虽然最初很有吸引力,但异常规范往往会带来需要 **非常** 仔细思考才能理解的后果。异常规范最大的问题在于,程序员使用它们的方式好像它们具有程序员想要的效果,而不是它们实际具有的效果。

非内联函数是在某些编译器中,“不抛出任何异常”的异常规范可能具有一些好处的地方。

命名约定说明

C++标准委员会的库工作组详细讨论了这个问题,并且讨论时间很长。在早期的 Boost 帖子中也重复了这一讨论。简短总结:

  • 命名约定存在争议,尽管有几种被广泛使用,但没有一种风格占主导地位。
  • 鉴于打算将 Boost 的部分内容提议用于下一版 C++ 标准库,Boost 决定遵循标准库的约定。
  • 一旦库确定了特定的约定,绝大多数利益相关者都希望始终如一地使用这种风格。

源代码字体说明

Dave Abrahams 评论:源代码的一个重要目的(我认为是主要目的)是沟通:意图的文档化。我认为,对于 Boost 来说,这是一个双重重要的目标。使用等宽字体使我们可以通过更多方式与更多人进行沟通(图表是可能的),直接在源代码中。使用空格为等宽字体编写的代码在使用变宽字体查看时也能很好地阅读,据我所知,每个支持变宽字体的编辑器也支持等宽字体。我不认为反过来也成立。

制表符说明

由于制表符在像 Boost 这样的多开发人员项目中造成的实际问题,而不是任何原则上的厌恶,制表符被禁止。参见 邮件列表存档。问题包括程序员使用制表符和程序员使用空格维护单个源文件,以及除了“不使用制表符”之外难以执行一致的制表符策略。讨论得出结论,Boost 文件应该全部使用制表符,或者全部使用空格,因此决定坚持使用空格进行缩进。

目录和文件名说明

1. 一些旧版文件系统需要单一大小写名称。单一大小写名称在从不区分大小写到区分大小写的文件系统迁移时消除了大小写错误。

2. 这是 POSIX 可移植文件名字符集的小写部分。引用 POSIX 标准,“文件名应使用可移植文件名字符集构建,因为在某些情况下使用其他字符可能会令人困惑或模棱两可”。

3. ISO 9660:1999 的严格实现和一些旧版操作系统禁止在目录名中使用点。对这种限制的需求正在减弱,并且可能会很快被移除。

4. POSIX 对以句点开头的名称有特殊规则。Windows 禁止以句点结尾的名称。

5. 在某些情况下会过于混乱或模棱两可。

6. 我们必须在某处划清界限,因此多年前选择了现在已过时的 Apple 文件系统施加的限制。它似乎仍然是一个合理的限制,有助于人类理解。

7. ISO 9660:1999。

ECMAScript/JavaScript 说明

在 1.29.0 版本之前,两个 Boost 库添加了 ECMAScript/JavaScript 文档。随后引起了争议(参见 邮件列表存档),并要求开发人员删除 ECMAScript/JavaScript。禁止使用 ECMAScript/JavaScript 的原因包括:

  • 与一些旧版浏览器和一些基于文本的浏览器不兼容。
  • 使打印文档页面变得困难。
  • 通常会导致非常糟糕的用户界面设计。
  • “总的来说很烦人”。
  • 需要 Boost 测试网页的 ECMAScript/JavaScript 兼容性。
  • 使非原始开发人员维护文档更加困难。

如果您决定必须使用 JavaScript,请考虑这些原因。尤其请记住,Boost 社区不负责测试您对 JavaScript 的使用。因此,您有责任确保在您的用例中完全解决了上述问题。

出于上述原因,允许但不鼓励使用 ECMAScript/JavaScript。

理由说明

美国遗产词典将理由定义为“某事物的根本原因;基础”。

Beman Dawes 评论:未能为设计决策提供同时代的理由是许多软件项目中的一个主要缺陷。缺乏准确的理由会导致问题被无休止地重新审视,当维护人员更改某些内容而不意识到它是出于某种目的以某种方式完成时会导致维护错误,并缩短软件的有效寿命。

在做出决策时提供理由相当容易,但即使在很短的时间后也很难准确地恢复。

致谢说明

随着库的成熟,它几乎总是会累积其他 Boost 成员向作者建议的改进。承认此类贡献并确定提出建议的人是 boost.org 文化的一部分。主要的贡献通常会在文档中得到承认,而较小的修复通常会在代码本身的注释中提及。