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.
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