c++26新功能—可观测检查点
一、介绍
开发者们应用C++在开发时,经常遇到一些致命性的问题或异常,导致程序Crash。一般来说,这种情况下,堆栈的上下文往往难以保全(特别在复杂的程序中)。那么这种情况下,一般会对dump文件进行分析,比如在Linux环境中可以使用gdb对core文件进行分析,虽然不至于非常准确清晰的定位到崩溃的地点,但大多数情况下还是可以给一个较为准确的位置的。但是使用dump是一种事后的补救的方法,能否可以预先在认为可能出现异常的位置埋下“钩子”呢?
本文就介绍在C++26中的可观测检查点的相关技术来实现类似的功能。
二、可观测的检查点
C++的开发者们,在遇到UB行为后,一般是不能一下子定位UB的位置的,因为UB行为可能导致整个应用的异常,使得整个应用进入一种无法预测的情况。能不能够有一种方法,使程序在遇到UB行为时,仅影响UB行为后的操作呢?如果有的话,那么这个点其实就可以认为是一个可观测观查点。
一般情况下,可观测的检查点,由编译器在错误操作前插入,从而保证错误异常状态的可捕获以及程序退出前的必要的资源回收。从目前的提案来看,可观测的检查点分为显式和隐式两种,隐式的由编译器可自动实现插入;而显式的则需要使用std::observable函数接口来实现。不过看来看去,对隐式和显示的细节,还没有弄明白,大家可以一起来研究一下。
三、应用
可观测的查检点有什么作用?大家是不是很容易的想到在调试中的作用。遇到检查点,程序可以断点在此处,更方便的进行context及相关的状态的显示,从而准确的定位异常的原因。这也和前面分析的debugging库,紧密的钩连在了一起。
另外在契约编程中,可观测检查点同样起着重要的作用,通过其可以实现契约断言语义的功能。即观察契约的违背并保证在违背时处理结果的一致性。
最后,它可以与C++26中的相关错误行为一起共同处理相关的错误问题。
不过,使用检查点可能限制编译器的优化并增加运行的时间成本,这一点也需要开发者引起重视。
四、例程
看一下相关的例程:
//下面a,b两个函数为显式消除死代码提供了相同的机会,即代码是等价的
void a(int &r, int *p) {if (!p) std::fprintf(stderr, "count: %d\n", ++r);if (!p) std::abort(); // henceforth, p is known to be non-nullif (!p) std::fprintf(stderr, "p is null\n");
}
void b(int &r, int *p) {if (!p) std::fprintf(stderr, "count: %d\n", ++r);std::observable();if (!p) std::fprintf(stderr, "p is null\n");*p += r; // p may be assumed non-null
}//注意,契约编程中会自动插入隐式的检查点而不需要下面的方法,此处只是一种举例
void f(int *p) pre(p) pre((std::observable(), *p<5));
代码仅供参考,真正的实践需要等编译器完整支持后才有实用性。
五、总结
可观测的检查点,其实就是C++提高整体的安全性的一个重要的环节,为以后实现Safe C++提前铺路。通过可观测的检查点增强了调试、异常处理和契约违背的检测能力,提高了应用程序的健壮性。这也算是对计算机语言对安全性要求越来越高的一个回应吧。