Assuming you have some static and dynamic storage duration entities like this (simplified example with just one translation unit):
struct DriverStatic
{
DriverStatic(int id): _id{id} {}
~DriverStatic() { std::cout << "DriverStatic stopping: " << _id << std::endl; }
int _id;
};
static DriverStatic ds1{1}, ds2{2};
struct DriverDynamic
{
DriverDynamic(int id): _id{id} {}
~DriverDynamic() { std::cout << "DriverDynamic stopping: " << _id << std::endl; }
int _id;
};
DriverDynamic* dn1 = new DriverDynamic{1};
DriverDynamic* dn2 = new DriverDynamic{2};
You can register both (one or more) exit handler and (one) terminate handler:
int main()
{
const int atex_result = std::atexit([]() {
std::cout << "atexit\n";
delete dn1;
delete dn2;
});
if (atex_result != 0) {
std::cerr << "Registration failed\n";
return EXIT_FAILURE;
}
std::set_terminate([](){
std::cout << "Unhandled exception\n" << std::flush;
delete dn1;
delete dn2;
// not sure how to force static objects destruction
std::abort();
});
// throw std::runtime_error("abnormal exit");
return 0;
}
This way when std::exit()
is called, you will first destroy your drivers with dynamic storage duration, and then drivers with static storage duration will be destroyed in unspecified order.
In case of abnormal program termination, like uncaught exception, your terminate_handler
will fire and will attempt to clean the dynamic storage.
Demo
I am not sure however, how to ensure correct static objects cleanup in the abnormal situation. And be aware that your system might still fail to do what you want, if you invoke some undefined behaviour, like throwing from the destructors of your drivers.
In any case, if you are controlling some safety-critical hardware, don't rely solely on your program being able to stop moving elements. Provide a heartbeat mechanism, and interlock your system when the software connection is lost.