I have a class that provides a utility for logging errors and exceptions. This is part of a library that's integrated into other applications. This class is implemented via the Singleton pattern (Meyer's singleton to be specific). The issue arises during the destruction phase when there's no defined point in time when this class is destructed. It so happens that during destruction an error/exception is raised which tries to access this class and leads to undefined behavior. Any flag within the class to maintain its state seemed moot since accessing the flag itself is UB once the object is destructed. I can only think of having the object allocated on the heap and letting it linger till the program execution but an issue with that is instruments designed to detect memory leaks would report this. I've also tried installing an std::atexit()
handler but that too was invoked before the destruction of the object which doesn't help. How are such issues of lifetime best handled?
EDIT: Here's a short snippet to illustrate the problem
#include <iostream>
#include <memory>
#define LOG_EXCEPTION(msg) Logger::instance()->logException(msg)
#define LOG_MESSAGE(msg) Logger::instance()->logMessage(msg)
struct Logger {
Logger() = default;
~Logger() {
std::cout<<"~Logger\n";
}
int x;
public:
static const std::shared_ptr<Logger>& instance() {
static auto logger = std::make_shared<Logger>();
return logger;
}
void logException(std::string const& msg) {
++x;
std::cout<<msg<<'\n';
}
void logMessage(std::string const& msg) {
++x;
std::cout<<msg<<'\n';
}
};
struct Foo {
~Foo() {
//Something wrong, so log an exception
LOG_EXCEPTION("oops");
}
};
int main()
{
static Foo f;
LOG_MESSAGE("message"); // Just to force creation of the Logger object
}
As is evident, since Foo f
outlives the singleton instance, it is UB to access the Logger
instance from its destructor. This is just a demonstrative code but such logging of exceptions can happen at any point in the program sequence and it's difficult to reason about the ordering of the statics (the actual error isn't even from a static instance).