Question

I'm pretty new to C++, but I want to make sure I'm not doing something wrong here before reporting a bug to Microsoft.

Here's some sample code:

#include <system_error>

using namespace std;

class Test
{
public:
    ~Test()
    {
        throw system_error(5, system_category());
    }
};

Test test;

void testfunc()
{
    throw system_error(5, system_category());
}

void main()
{
    try
    {
        testfunc();
    }
    catch ( const system_error& e)
    {
    }
}

Now, I would expect Windows to say "the runtime has requested the program to exit in an unexpected way". However, I get a "pure virtual function called" error instead. With a little debugging, I noticed that when the static class destructor gets the std::system_category reference, the ::name and ::message members are pure virtual. However, when it is constructed in testfunc(), those vtable pointers are to valid functions.

My question is, am I doing something wrong by constructing my system_error exceptions this way? I had some code that was basically doing throw system_error(GetLastError(), system_category());. This happened to execute in a static destructor, and I got the pure virtual function called error.

To throw exceptions from Windows' GetLastError() function, should I be constructing my exceptions a different way, or is this a bug in msvc11's C++ runtime?

EDIT

There's been a bit of confusion about my question. My actual code is more complicated than this example, and I actually didn't expect that one of my destructors could throw. My destructor must call a function that could possibly throw. If I change my code to:

~Test()
{
    try
    {
        callSomeFuncThatCouldThrow();
    }
    catch ( … ) { }
}

I will still get the pure virtual function call error. This is because when the system_error is constructed (in callSOmeFuncThatCouldThrow()) it tries to use the ::message member of the system_category I'm giving it, which causes the error.

Was it helpful?

Solution

Looks like a Microsoft bug. std::error_category is an abstract base class for various unnamed types, one of which is the type returned by system_category(). There's one object of that type, and all calls to system_category() return a reference to that object. What you're seeing looks like that object is being destroyed before the destructor of the test object runs. If you want to satisfy the purists, change your destructor to:

Test::~Test() {
    const std::error_category& cat = std::system_category();
    std::cout << cat.name() << '\n';
}

OTHER TIPS

This is a bug in the Visual C++ Standard Library implementation. The global error category objects are implemented using static data members of a class template. Unfortunately, within a single translation unit (a.k.a. source file), these data members will be initialized after all other namespace-scope objects in the translation unit and will be destroyed before those objects (because destruction occurs in reverse order of initialization).

My recommendation would be to avoid calling generic_category(), iostream_category(), or system_category() during initialization and termination (i.e., before all static objects have been initialized or after destruction of static objects has begun).

If this is not possible, the following workaround may work. It's worked in the few simple tests I've run, but I can't make any guarantees about its behavior in all cases (this isn't an "official" workaround; it's just a potential workaround). Add a .cpp file to your project with these contents:

// This is a workaround for a bug in the Visual C++ 2012 implementation of the
// global error category objects--generic_category(), iostream_category(), and
// system_category().
#ifdef _MSC_VER

#if _MSC_VER != 1700
#  error Please verify that this fix is still required and is still correct!
#endif

#include <system_error>

// Ensure that static objects in this translation unit get initialized "first":
#pragma warning(suppress: 4073)
#pragma init_seg(lib)

// Explicitly instantiate the global error objects in this translation unit:
template struct std::_Error_objects<int>;

#endif // _MSC_VER

The #pragma init_seg(lib) should ensure that static data members of _Error_objects<int> will be initialized before any static objects in user code. Do not put anything else in this source file: the #pragma init_seg applies to all objects in the translation unit. _Error_objects is an implementation detail, and that implementation detail may change at any time, hence the compiler version checks.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top