是否可以从std::abort中恢复
Is it possible to recover from std::abort?
我们的C代码库使用assert来检查是否满足先决条件/后置条件。
#include <cstdlib>
#include <cassert>
void Aborting_Function(int n);
int main(){
Aborting_Function(1); //good
Aborting_Function(0); //calls std::abort()
//Is it possible to recover somehow?
//And continue on...
}
void Aborting_Function(int n){
assert(n > 0);
//impl...
}
在单元测试中,我想验证函数是否正确地遵循了它们的约定
(应中止时中止)。
是否可以从std::abort中恢复
我意识到,让单元测试检查与断言应该检查的东西完全相同的东西似乎有些重复,但这会很有帮助,因为我们可以自动检查不应该工作的特定用例。
简短回答,"否"。
与其颠覆abort(),不如考虑使用谷歌测试框架。
这是DEATH_TEST(此处的文档:https://github.com/google/googletest/blob/master/googletest/docs/V1_7_AdvancedGuide.md)
本质上,它所做的是派生一个子进程,并检查语句是否导致它退出(如果它中止了,它会这样做)。
对于单元测试,可以从那里替换abort()和longjmp(),或者从那里通过C++抛出进行测试。
例如(用C++表示):
#include <cassert>
#include <csetjmp>
#include <stdexcept>
#include <iostream>
#ifndef lest_ABORT_SIGNATURE
# if _MSC_VER
# define lest_NORETURN __declspec(noreturn)
# define lest_ABORT_SIGNATURE() _ACRTIMP lest_NORETURN void __cdecl abort(void)
# else
# define lest_NORETURN [[noreturn]]
# define lest_ABORT_SIGNATURE() lest_NORETURN void __cdecl abort()
# endif
#else
# ifndef lest_NORETURN
# define lest_NORETURN
# endif
#endif
#if USE_LONGJMP
jmp_buf env;
lest_ABORT_SIGNATURE()
{
std::longjmp( env, 1 );
}
#else
struct Abort{};
lest_NORETURN void my_abort()
{
throw Abort{};
}
lest_ABORT_SIGNATURE()
{
// throw indirectly and prevent warning in VC14:
my_abort();
}
#endif
int main()
{
#if USE_LONGJMP
if ( ! setjmp( env ) )
{
std::cout << "assert(false):n";
assert( false );
}
else
{
std::cout << "Intercepted abortn";
}
#else
try
{
std::cout << "assert(false):n";
assert( false );
}
catch ( Abort const & )
{
std::cout << "Caught Abortn";
}
catch ( std::exception const & e )
{
std::cout << "Exception: " << e.what() << "n";
}
#endif
std::cout << "Endn";
}
#if 0
cl -EHsc -DUSE_LONGJMP=1 abort-own.cpp && abort-own.exe
g++ -Wall -DUSE_LONGJMP=1 -std=c++11 -o abort-own.exe abort-own.cpp && abort-own.exe
#endif
使用VC14(VS2015)编译并使用运行
cl -EHsc -DUSE_LONGJMP=1 abort-own.cpp && abort-own.exe
产生以下输出:
...
assert(false):
Assertion failed: false, file abort-own.cpp, line 45
Intercepted abort
End
使用VC14之前的编译器进行编译会产生链接错误:
LIBCMT.lib(abort.obj) : error LNK2005: _abort already defined in {file}
{exe} : fatal error LNK1169: one or more multiply defined symbols found
这可以通过以下方法治愈:
- 包括编译时的CCD_ 1
- 创建删除了CCD_ 2的运行库
使用-std=c++03
或-std=c++11
通过g++编译不会产生多重定义符号。
我正在为最新的测试框架开发上面代码的更详细版本
- 替换abort()、longjmp()
- 替换中止(),抛出
根据POSIX,
abort()函数将导致异常进程终止,除非信号SIGABRT被捕获并且信号处理程序没有返回。
这意味着,如果您捕捉到信号并且信号处理程序返回,则仍然需要终止程序(例如,通过将信号处理程序重置为默认终止行为,然后再次引发信号)。
因此,"恢复"的唯一方法是捕获信号,而不是从信号处理程序返回。因此Aborting_Function(0)
之后的线路无法到达。此外,您可能不希望您的程序在信号处理程序中度过余生,因为这样一来,所有外部变量都变得不安全,无法访问(除了无锁原子)。这不是很好。
不过在Windows上?我不知道。
有可能从
std::abort
恢复吗?
这取决于您所说的从恢复是什么意思。从…起http://en.cppreference.com/w/cpp/utility/program/abort
导致程序异常终止,除非
SIGABRT
被传递给信号的信号处理程序捕获,并且该处理程序不返回。
如果您希望能够从调用std::abort
的位置继续,那么答案是否。如果您希望能够在程序退出之前执行某些操作,那么答案是是。
我猜您希望能够从调用std::abort
的位置继续。因此,对于您的用例,答案是否。
有一种方法可以做到这一点,但有点不符合。
你可以使用HippoMocks(免责声明:我是作者)来模拟这个函数,并让它抛出一个异常,然后你可以用它来检查你的测试框架。你不能让它返回一个值,因为它被标记为noreturn,编译器将不会生成任何代码来处理它的返回
EXPECT_CALL(&abort).Throw(42);
请注意,这至少违反了C和C++中的5条不同规则,所以更大的问题是,你应该这样做吗?据我所知,你使用断言来防止出现内部不一致的情况。这些都是你不应该测试的东西。您的所有程序在有断言和没有断言的情况下都应该是同等有效的。如果你期望你想要测试的代码有任何类型的行为,那么这永远不应该是断言,因为这不是一个意外的状态(见鬼,你正在测试那个状态,所以它在这方面测试得很好)。
所以你可以,但你不应该想要。
- 用C++将哈希表写入文件并从文件中恢复
- Opencv 恢复到比我设置的更高的分辨率
- 变量在使用赋值语句赋值后恢复为以前的值
- 在信号处理程序中捕获C++未处理的异常并恢复应用程序
- 删除所有字符串后如何恢复 QStringList?
- 在macOS Mojave上尝试OpenCV视频捕获时"Abort Trap: 6"
- 当对套接字 send() 的同步调用由于连接另一端丢失而被阻止时,如何恢复?
- 如何仅使用一次固定<<设置精度(2)?或者至少恢复到默认行为?
- "co_yield"是否可以在恢复协程时从调用方返回值?
- OpenSSL C API:如何在程序exec()之后恢复TLS连接?
- 通过指针恢复对数组的引用.UB与否?
- 从不同进程中的另一个线程挂起/恢复线程或进程
- 如何在 XML 中正确存储原始字节数据并恢复它?
- ExtTextOut 文本的持续闪烁,在一段时间后,文本将恢复为默认字体
- 模板化类中运算符 + 重载的值的恢复
- C++ 线程创建/删除与线程停止/恢复
- ZMQ::send() 抛出异常并终止 QNX 进程.为什么以及如何从中恢复?
- Windows桌面程序保存您的计算机会话 - 基于程序崩溃时的恢复会话
- xSemaphoreTake 在调用 xSemaphoreGive 后不会恢复任务
- 是否可以从std::abort中恢复