gpt4 book ai didi

c++ - 在调用 std::rethrow_exception 可移植后,是否销毁指向异常的最后一个 std::exception_ptr?

转载 作者:塔克拉玛干 更新时间:2023-11-03 07:09:41 27 4
gpt4 key购买 nike

我有一个关于异常生命周期的可移植性问题。在下面的代码中,异常在一个线程 (mySlave) 中抛出并使用 std::exception_ptr 转移到另一个线程 (myMaster) . myMaster 总是通过 std::condition_variable 等待不同的事件. mySlave 中的一个异常(exception)就是这样的事件。在myMaster中等待的谓词函数中,我检查异常指针是否为空。如果在mySlave中抛出异常,我将异常指针复制到myMaster中的一个临时变量中,将原来的异常指针设置为null并在myMaster中重新抛出。这样,一旦程序从异常中恢复,原始异常指针就可以在谓词函数中使用。

这在 VC14 上运行良好,但最终的软件很可能在未来移植到其他平台。在我的代码中,所有 exception_ptr重新抛出后对异常的引用将超出范围,因此原始异常将被销毁。我担心的是 std::rethrow_exception保证在重新抛出异常时总是生成异常的拷贝,或者如果它也可以使用对异常的引用,导致它在我 try catch 时不再有效 if in myMaster?

#include <mutex>
#include <thread>
#include <atomic>
#include <exception>
#include <iostream>
#include <memory>

class SomeClass
{
public:
/*...*/
void MaseterFunction();
void SlaveFunction();

private:
/*...*/
std::mutex mutex_gotEvent;
std::condition_variable condVar_gotEvent;
std::exception_ptr slaveLoopException;
/*...*/
std::atomic<bool> running = true;
};

class MyException : public std::runtime_error
{
public:
MyException() : std::runtime_error("Ooops") {}
};

void SomeClass::SlaveFunction()
{
try
{
throw MyException();
}catch(const std::exception& e)
{
std::unique_lock<std::mutex> lock(mutex_gotEvent);
slaveLoopException = std::current_exception();
condVar_gotEvent.notify_all();
}
}


void SomeClass::MaseterFunction()
{
while (running)
{
try
{
{
/*Wait for something interesting to happen*/
std::unique_lock<std::mutex> lock(mutex_gotEvent);
condVar_gotEvent.wait(lock, [=]()->bool {
return !(slaveLoopException == nullptr); // Real code waits for several events
});
}

/*Care for events*/
/*...*/
if (slaveLoopException)
{
std::exception_ptr temp_ptr = slaveLoopException;
slaveLoopException = nullptr;

std::rethrow_exception(temp_ptr);
}
}
catch (const MyException& e)
{
std::cout << e.what();
running = false;
}
}
}

int main()
{
std::shared_ptr<SomeClass> someClass = std::make_shared<SomeClass>();
std::thread myMaster([someClass]() {someClass->MaseterFunction(); });
std::thread mySlave([someClass]() {someClass->SlaveFunction(); });

std::cin.ignore();

if (myMaster.joinable())
{
myMaster.join();
}
if (mySlave.joinable())
{
mySlave.join();
}
return 0;
}

我考虑过声明 temp_ptr在类(class)或使用 std::atomic<bool>除了要在谓词函数中使用的异常指针之外的变量。但是,这两种解决方案都会在不再使用异常后使异常保持事件状态,这对我来说似乎不是很优雅。也可以在 myMaster 的每个 catch block 中将异常指针设置为 null,但我认为这可能会在稍后添加新异常时引入错误,而程序员忘记将异常指针设为 null。

编辑:

我发现了关于这个主题的以下陈述:

Statement 1 :

The exception object referenced by an std::exception_ptr remains valid as long as there remains at least one std::exception_ptr that is referencing it

Statement 2 :

The VS implementation of rethrow_exception appears to make a copy of the exception. Clang and gcc do not make copies.

Statement 3

The exception object is destroyed after either the last remaining active handler for the exception exits by any means other than rethrowing, or the last object of type std::exception_ptr (§18.8.5) that refers to the exception object is destroyed, whichever is later.

从 (1) 我希望过早销毁异常。从 (2) 开始,我希望在使用 VC 作为拷贝时这不会有任何效果。从 3 开始,我不知道这是否可以在使用 gcc 或 clang 时拯救我。我的意思是,退出是通过重新抛出发生的,但它不是从处理程序中重新抛出的。当临时指针被销毁时,新的处理程序是否已经被认为是事件的,或者指针是否首先被销毁并随之产生异常,从而使后续的 catch block 留下无效的异常引用?

最佳答案

CppRef 指出 rethrow_exception -- 抛出先前捕获的异常对象。我自己没有检查标准(但见下文),但是:

猜测

在我看来,从你重新抛出异常的那一刻起,异常处理“恢复正常”,即实现的工作是保留抛出的对象,语言不关心你是否通过正常的 throw throw 或通过 rethrow_exception .

换句话说:exception_ptr 需要在 rethrow_exception 点有效,在此之后,抛出的异常与任何其他异常相同,无论它是否与原始异常指针共享都无关紧要。


OP提供了很好的link用标准报价:

4/ The memory for the exception object is allocated in an unspecified way, except as noted in §3.7.4.1. If a handler exits by rethrowing, control is passed to another handler for the same exception. The exception object is destroyed after either the last remaining active handler for the exception exits by any means other than rethrowing, or the last object of type std::exception_ptr (§18.8.5) that refers to the exception object is destroyed, whichever is later. In the former case, the destruction occurs when the handler exits, immediately after the destruction of the object declared in the exception-declaration in the handler, if any. In the latter case, the destruction occurs before the destructor of std::exception_ptr returns. The implementation may then deallocate the memory for the exception object; any such deallocation is done in an unspecified way. [ Note: a thrown exception does not propagate to other threads unless caught, stored, and rethrown using appropriate library functions; see §18.8.5 and §30.6. —end note ]

这似乎暗示了我上面写的:异常对象只要需要就可以存在: whichever is later 部分引用似乎明确指出了这一点。

更新:我错过了 OP 不会从处理程序中重新抛出,所以我也不确定这里应该发生什么。

确实相信在rethrow_exception之后飞行中的(新)异常应该被视为好像它是由正常的 throw 生成的一样表达式 - 其他任何内容都没有意义。

我在这里推测的方式是甚至在 std::exception_ptr 之前每个实现都有一个类似于 std::exception_ptr 的内部机制因为它必须使抛出的异常对象(复制或不复制)在正常堆栈帧外部保持事件状态,直到堆栈上的任何处理程序不再需要它为止。所以我可以推测 clang 或 gcc 不会进行复制,而是在您调用 throw/rethrow_exception 时立即保存一个“内部 exception_ptr”。

关于c++ - 在调用 std::rethrow_exception 可移植后,是否销毁指向异常的最后一个 std::exception_ptr?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43799790/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com