契约式设计的概念最初是作为 Bertrand Meyer 的 Eiffel 语言的一部分开发的,它围绕库用户和实现者之间的契约制定展开,其中第一个要求在调用库的方法时传递的值要遵守一些前提条件,而实现者则保证结果满足某些约束(后置条件),以及遵守指定的内部一致性规则,称为不变式。 Eiffel 通过分别使用 require
、ensure
和 invariant
结构来原生支持刚才描述的契约的三个部分。
C++ 不直接支持契约式设计技术:这些通常作为断言代码实现,并且出于性能原因经常在发布模式下关闭。 遵循这种方法,Boost.MultiIndex 提供了两种不同的调试模式
将前提条件检查工具添加到 STL 作为调试辅助的想法最初是由 Cay S. Horstmann 在他的 安全 STL 库中引入的,后来被 STLport 调试模式 采用。类似地,Boost.MultiIndex 具有所谓的安全模式,在处理库的迭代器和函数时,会检查各种前提条件。
Boost.MultiIndex 安全模式通过全局定义宏 BOOST_MULTI_INDEX_ENABLE_SAFE_MODE
来设置。错误条件通过宏 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT
进行检查,该宏默认解析为对 BOOST_ASSERT
的调用。
如果用户决定定义自己的 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT
版本,它必须采用以下形式
BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(expr,error_code)
其中 expr
是检查的条件,error_code
是 safe_mode::error_code
枚举的一个值
namespace boost{ namespace multi_index{ namespace safe_mode{ enum error_code { invalid_iterator, // vg. default cted or pointing to erased element not_dereferenceable_iterator, // iterator is not dereferenceable not_incrementable_iterator, // iterator points to end of sequence not_decrementable_iterator, // iterator points to beginning of sequence not_owner, // iterator does not belong to the container not_same_owner, // iterators belong to different containers invalid_range, // last not reachable from first inside_range, // iterator lies within a range (and it mustn't) out_of_bounds, // move attempted beyond container limits same_container, // containers ought to be different unequal_allocators // allocators ought to be equal }; } // namespace multi_index::safe_mode } // namespace multi_index } // namespace boost
例如,以下 BOOST_MULTI_INDEX_SAFE_MODE_ASSERT
的替换会抛出异常而不是断言
#include <boost/multi_index_container/safe_mode_errors.hpp> struct safe_mode_exception { safe_mode_exception(boost::multi_index::safe_mode::error_code error_code): error_code(error_code) {} boost::multi_index::safe_mode::error_code error_code; }; #define BOOST_MULTI_INDEX_SAFE_MODE_ASSERT(expr,error_code) \ if(!(expr)){throw safe_mode_exception(error_code);} // This has to go before the inclusion of any header from Boost.MultiIndex, // except possibly safe_error_codes.hpp.
其他可能性,例如输出到日志或触发某种警报,也是可以实现的。
警告:安全模式会在空间和时间方面给程序增加非常重要的开销,因此通常不应为 NDEBUG
构建设置它。 此外,此模式仅用作调试辅助,程序不得将其作为正常执行流程的一部分依赖于它:特别是,不保证所有可能的前提条件错误都会被诊断出来,或者检查在库的不同版本中保持稳定。
从存档恢复的迭代器不受安全模式检查的约束。 这是因为不可能仅从序列化信息中自动知道迭代器关联的 multi_index_container
。 但是,如果需要,可以使用以下解决方法将恢复的迭代器转换为已检查的值
employee_set es; employee_set::nth_index<1>::iterator it; // restore es and it from an archive ar ar>>es; ar>>it; // it won't benefit from safe mode checks // Turn it into a checked value by providing Boost.MultiIndex // with info about the associated container. // This statement has virtually zero cost if safe mode is turned off. it=es.project<1>(it);
可以通过全局定义宏 BOOST_MULTI_INDEX_ENABLE_INVARIANT_CHECKING
来设置 Boost.MultiIndex 的所谓不变式检查模式。 当此模式生效时,Boost.MultiIndex 的所有公共函数都将执行旨在确保所管理数据结构的基本内部不变式得到维护的执行后测试。
如果不变式测试失败,Boost.MultiIndex 将通过一元宏 BOOST_MULTI_INDEX_INVARIANT_ASSERT
指示失败。 除非用户为此宏提供定义,否则它默认为 BOOST_ASSERT
。 此类断言原则上应被视为库中的错误。 请将此类问题以及尽可能多的上下文信息报告给库的维护人员。
建议 Boost.MultiIndex 的用户始终在调试版本中设置不变式检查模式。
修订于 2020 年 5 月 9 日
© 版权所有 2003-2020 Joaquín M López Muñoz。根据 Boost 软件许可证,版本 1.0 分发。(请参阅随附文件 LICENSE_1_0.txt 或复制于 https://boost.ac.cn/LICENSE_1_0.txt)