Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Singleton with an object which throws in the ctor - accessing again?

I can use C++11 or C++14 (or even C++17). Let's suppose I have a singleton object


class MyInstance {
public:
    MyInstance() {
        throw std::runtime_exception("something went wrong"); // Ctor might throw
    }
};

MyInstance& getInstance() {
    static MyInstance obj;
    return obj;
}

Now, I made sure that every call to getInstance is wrapped in a

try {
    auto& inst = getInstance();
} catch(std::runtime_error& e) {
   // do something
}

What I'm wondering right now is: what happens if after failing an initialization in the constructor and throwing and catching and informing the user in the logs... the program passes again in a try codepath and calls getInstance again?

I made a few guesses but I have no idea if they're right:

The object has static storage so it will only be attempted to be built once I think? Will returning a reference to an unconstructed object will get me a dangling reference and undefined behavior? Would use a unique_ptr as static variable in place of obj solve this problem so I can access the pointer multiple times and also check if the object was properly constructed (if (uptr == TRUE)) ?

like image 318
Dean Avatar asked Jul 09 '19 09:07

Dean


2 Answers

If the constructor throws the object is not initialized. So if control passes through getInstance again, initialization will be performed again as well.

[stmt.dcl] (emphasis mine)

4 Dynamic initialization of a block-scope variable with static storage duration or thread storage duration is performed the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. If control enters the declaration concurrently while the variable is being initialized, the concurrent execution shall wait for completion of the initialization. If control re-enters the declaration recursively while the variable is being initialized, the behavior is undefined.

like image 191
StoryTeller - Unslander Monica Avatar answered Oct 18 '22 21:10

StoryTeller - Unslander Monica


[stmt.dcl]/4: Dynamic initialization of a block-scope variable with static storage duration or thread storage duration is performed the first time control passes through its declaration; such a variable is considered initialized upon the completion of its initialization. If the initialization exits by throwing an exception, the initialization is not complete, so it will be tried again the next time control enters the declaration. [..]

No need to "guess"; you could put a std::cout trace inside MyInstance::MyInstance() and call getInstance() twice. 😊

Also no need for smart pointers; the object either exists or it doesn't, and there's no way to proceed inside getInstance() after the declaration without the object existing, because you threw an exception!

By the way, it's std::runtime_error, not std::runtime_exception.

like image 26
Lightness Races in Orbit Avatar answered Oct 18 '22 21:10

Lightness Races in Orbit