Background
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.
Problem
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?
Code
#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;
}
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()
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();
With VC++ (2017) ...
it breaks
Arc::~Arc()
and the run terminates with exit code -1073740940 (0xC0000374)
with g++ (9.1.0) MinGW 7.0
it breaks, but reports the signal
Signal: SIGTRAP (Trace/breakpoint trap)
Arc::~Arc();
And finishes with exit code 1