当网络边缘的构造函数抛出异常时,避免使用SIGTRAP [英] Avoid SIGTRAP when the constructor of a network edge throws
问题描述
我有一个像设置节点和边的网络.节点和边缘都必须是类,在这种情况下为Node
或Arc
,如本问题所述.在我的实际设置中,我正在处理Node和Arc的许多子类.对于内存管理,我使用以上问题的答案.
I have got a network like setting with nodes and edges. Both nodes and edges need to be classes, in this case Node
or Arc
, as in this question. In my real setup I am dealing with quite a number of subclasses of both Node and Arc. For memory management, I use this answer to the question above.
当构造函数引发异常时,Windows上带有MinGW的Visual Studio和g ++无法捕获它,但是退出时没有错误处理(g ++/MinGW报告SIGTRAP信号),而Linux上的g ++和clang ++可以正确处理该异常.如果创建的Arc没有异常Arc(n1, n2, false)
,则所有编译器都可以正常工作.在所有情况下,都没有相关的编译器警告(使用/W4或-Wall)有人可以向我解释一下,为什么这在Windows上不起作用?甚至提供解决方法?
When the constructor throws an exception, Visual Studio and g++ with MinGW on Windows cannot catch it, but exit without error handling (g++/MinGW reporting a SIGTRAP signal) while g++ and clang++ on Linux handle the exception correctly. If the Arc is created without exception Arc(n1, n2, false)
, all compilers work fine. In all cases, there are no relevant compiler warnings (using /W4 resp. -Wall) Can someone explain me, why this is not working on Windows? Or even give a workaround?
#include <iostream>
#include <stdexcept>
#include <vector>
#include <memory>
struct Node;
struct Arc {
Node *left,*right;
private:
// shared pointer to self, manages the lifetime.
std::shared_ptr<Arc> skyhook{this};
public:
// c'tor of Arc, registers Arc with its nodes (as weak pointers of skyhook)
explicit Arc(Node* a_, Node* b_, bool throw_exc);
// resets skyhook to kill it self
void free() {
std::cout << " Arc::free();\n" << std::flush;
skyhook.reset();
}
virtual ~Arc() {
std::cout << " Arc::~Arc();\n" << std::flush;
}
};
struct Node {
explicit Node() {
std::cout << " Node::Node()\n" << std::flush;
}
std::vector<std::weak_ptr<Arc> > arcs;
~Node() {
std::cout << " Node::~Node();\n" << std::flush;
for(const auto &w : arcs) {
if(const auto a=w.lock()) {
a->free();
}
}
}
};
Arc::Arc(Node *a_, Node *b_, bool throw_exc) : left(a_), right(b_) {
std::cout << " Arc::Arc()\n" << std::flush;
if (throw_exc) {
throw std::runtime_error("throw in Arc::Arc(...)");
}
a_->arcs.push_back(skyhook);
b_->arcs.push_back(skyhook);
}
int main(int argc, char* argv[]) {
std::cout << "n1=new Node()\n" << std::flush;
Node *n1 = new Node();
std::cout << "n2=new Node()\n" << std::flush;
Node *n2 = new Node();
std::cout << "try a=new Arc()\n" << std::flush;
try {
Arc *a = new Arc(n1, n2, true);
} catch (const std::runtime_error &e) {
std::cout << "Failed to build Arc: " << e.what() << "\n" << std::flush;
}
std::cout << "delete n1\n" << std::flush;
delete n1;
std::cout << "delete n2\n" << std::flush;
delete n2;
}
输出
这就是我在Linux和Windows上都能得到的
Output
This is what I get both on Linux as well on Windows
n1=new Node()
Node::Node()
n2=new Node()
Node::Node()
try a=new Arc()
Arc::Arc()
在Linux上使用g ++(7.4.0和8.3.0)或clang ++(6.0.0)...
它按预期工作:
With g++ (7.4.0 and 8.3.0) or clang++ (6.0.0) on Linux ...
it works as expected:
Arc::~Arc();
Failed to build Arc: throw in Arc::Arc(...)
delete n1
Node::~Node();
delete n2
Node::~Node();
使用VC ++(2017)...
坏了
Arc::~Arc()
运行终止,退出代码-1073740940(0xC0000374)
and the run terminates with exit code -1073740940 (0xC0000374)
它坏了,但是报告了信号
it breaks, but reports the signal
Signal: SIGTRAP (Trace/breakpoint trap)
Arc::~Arc();
并以退出代码1结尾
推荐答案
tl; dr:从std::enable_shared_from_this
继承并使用weak_from_this()
.
tl;dr: inherit from std::enable_shared_from_this
and use weak_from_this()
.
请考虑以下结构,该结构与您的结构类似( https://godbolt.org/z/vHh3ME ):
Consider the following structure, which is similar to yours (https://godbolt.org/z/vHh3ME):
struct thing
{
std::shared_ptr<thing> self{this};
thing()
{
throw std::exception();
}
};
在引发异常时,对象*this
和self
的状态是什么,以及哪些析构函数将在堆栈展开时执行?对象本身尚未完成构造,因此~thing()
将不会(并且一定不能)执行.另一方面,self
是完全构造的(成员在进入构造函数体之前被初始化).因此,将执行~std::shared_ptr<thing>()
,该调用将在未完全构造的对象上调用~thing()
.
What is the state of the objects *this
and self
at the moment the exception is thrown, and which destructors are going to be executed as part of stack unwinding? The object itself has not yet finished constructing, and therefore ~thing()
will not (and must not) be executed. On the other hand, self
is fully constructed (members are initialized before the constructor body is entered). Therefore, ~std::shared_ptr<thing>()
will execute, which will call ~thing()
on an object which is not fully constructed.
从std::enable_shared_from_this
继承不会出现此问题(因为weak_from_this()
在这里是您的朋友),因为它只保存一个std::weak_ptr
( https://godbolt.org/z/TGiw2Z );在构造函数的末尾初始化shared_ptr
的变体也不起作用( https://godbolt.org/z/0MkwUa ),但这在您的案例中并不容易,因为您要在构造器中放弃共享/弱指针.
Inheriting from std::enable_shared_from_this
does not exhibit this problem assuming no actual shared_ptr
s are created before the constructor finishes executing and/or throws (weak_from_this()
would be your friend here), since it only holds a std::weak_ptr
(https://godbolt.org/z/TGiw2Z); neither does a variant where your shared_ptr
is initialized at the end of the constructor (https://godbolt.org/z/0MkwUa), but that's not trivial to incorporate in your case since you're giving shared/weak pointers away in the constructor.
话虽这么说,您仍然有所有权问题.实际上没有人拥有您的Arc
;唯一的外部引用是weak_ptr
.
That being said, you still have an ownership problem. Nobody actually owns your Arc
; the only outside references to it are weak_ptr
s.
这篇关于当网络边缘的构造函数抛出异常时,避免使用SIGTRAP的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!