当网络边缘的构造函数抛出异常时,避免使用SIGTRAP [英] Avoid SIGTRAP when the constructor of a network edge throws

查看:118
本文介绍了当网络边缘的构造函数抛出异常时,避免使用SIGTRAP的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个像设置节点和边的网络.节点和边缘都必须是类,在这种情况下为NodeArc如本问题所述.在我的实际设置中,我正在处理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();
  }
};

在引发异常时,对象*thisself的状态是什么,以及哪些析构函数将在堆栈展开时执行?对象本身尚未完成构造,因此~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_ptrs 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_ptrs.

这篇关于当网络边缘的构造函数抛出异常时,避免使用SIGTRAP的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆