C++ Logo

std-proposals

Advanced search

[std-proposals] Catch exception thrown from constructor of global object

From: Frederick Virchanza Gotham <cauldwell.thomas_at_[hidden]>
Date: Thu, 12 Jan 2023 10:23:45 +0000
Let's say we have three global objects like in the following program:

      SomeClass0 g_objA;
      SomeClass1 g_objB;
      SomeClass2 g_objC;

      int main(void)
      {

      }

Well the program's screwed if either of those 3 constructors throw an exception.

When I'm starting to write a new program nowadays, I always do:

      #include <exception> // set_terminate
      #include <optional> // optional
      using std::optional;
      optional<SomeClass0> g_objA;
      optional<SomeClass1> g_objB;
      optional<SomeClass2> g_objC;

      int main(void)
      {
          std::set_terminate( my_terminate_handler);

          try
          {
              g_objA->emplace();
              g_objB->emplace();
              g_objC->emplace();
          }
          catch(...) { /* deal with problems in here */ }
      }

The benefits to this strategy are as follows:
(1) My terminate handler is set before the global objects are constructed
(2) I can catch the exceptions thrown when the global objects are constructed

Now if you were to ask me . . . . I would almost go so far as to
propose that C++26 should deprecate defining a global object that
doesn't satisfy 'is_trivial' so that all such objects would be wrapped
in an std::optional<T> and emplaced within the body of 'main', but of
course I know that I won't get any support on that. So I can just
maintain my own personal programming policy and be content with that.

But is there any other little change we can make that would make the
situation a little better for the 98% of programmers who don't adopt
my own personal policy? At the very least, maybe we should provide a
way of ensuring that the terminate handler is set before the
construction of a global object takes place? Perhaps something like:

      void *my_handler(void)
      {
          // Save debugging info
      }

      int main(void) : set_terminate(my_handler)
      {

      }

I've seen people try to pull this off already in C++ 20 programs by
doing the likes of:

      auto const installed{ std::set_terminate(&handler) };

      int main(void)
      {
            // Do stuff
      }

But there are two problems with this:
Maybe some other global object will be constructed before 'installed',
and maybe the constructor of that other global object will throw
(2) I haven't checked the fine print in the Standard on whether or not
the terminate handler is guaranteed to be called if a global object
fails to construct -- I read the following on cppreference.com:
"std::terminate is called if the constructor or the destructor of a
static or thread-local (since C++11) object throws an exception"
but I'm not sure if the 'static' here includes global variables.

I do think though that there should be more solid sensibility in the
Standard surrounding the failure to construct global objects.

Received on 2023-01-12 10:23:58