Pirx un[blog]ged

Mike is reading four blogs. This is none of them.

A unique singleton

A singleton is a class that can be instatiated only once. In most cases global access is desirable. A typical use case is a logger class, that can be accessed from everywhere and is routing all messages to a single data sink. To implement a singleton in C++ is quit easy. The static attribute comes to help. A factory() method and a private constructor is all we need:

class singleton
{
public:
    static singleton* factory()
    {
        static singleton s;
        return &s;
    }

    //  implement interface here:

private:
    singleton()
    {}
    ~singleton()
    {}
};

The only way to get a singleton is the factory method. And it's guaranteed that these method returns the one and only instance every time. This is good enough for the most cases. (Note that the factory method is not threadsafe.)

I came across another use case for singletons: The implementation of a signal handler. So I decided to push this topic a little harder.

When implementing a signal handler, it's not important to have a single instance of a class all the time. It's important to have a static method that can be called from the system and a static object (the singleton) that do some work with this signal. After that the singleton can be removed. But how can we get rid of the singleton?

In the example above the singleton constructor comes in use with the first call of the factory method. Thereafter, the singleton remains until the program terminates. Even after the return from the main function, it still exists. Only the runtime environment has really control about the lifetime of static objects. To overcome this restriction a static pointer is very handy. And it makes sense to use a std::unique_ptr<> to retain ownership.

Let's try this. First we define a global method to catch process signals:

void handle_signal(int sig);

Second we define a class to catch and process signals (demo code for a linux system, untested):

struct signal_handler
{
    std::promise< int > result_;
    signal_handler()
    : result_()
    {   //  CTRL-C
        std::signal(SIGINT, &handle_signal);
    }
    ~signal_handler()
    {   //  remove handler
        std::signal(SIGINT, SIG_DFL);
    }

    int wait()
    {
        auto f = result_.get_future();
        return f.get();
    }
        
    void send_signal(int sig)
    {
        result_.set_value(sig);
    }
};

So far, everything is fine. But the code is still useless. We need a manager to define a static (unique) pointer and to control it's lifetime:

struct signal_mgr 
{
    signal_mgr()
    {
        BOOST_ASSERT_MSG(!impl_, "signal handler already installed");
        impl_.reset(new signal_handler());
    }

    virtual ~signal_mgr()
    {
        impl_.release();
    }
    
    int wait()
    {
        return impl_->wait();
    }

    static std::unique_ptr< signal_handler >    impl_;
};

//  static initialization
std::unique_ptr< signal_handler > signal_mgr ::impl_;

Now we can fill up the handler function:

void handle_signal(int sig)
{
    BOOST_ASSERT_MSG(signal_mgr ::impl_, "signal handler not initialized");
    if (signal_mgr ::impl_)
    {
        signal_mgr ::impl_->send_signal(sig);
    }
}

And now it's utterly simple to install a signal handler an to wait:

signal_mgr signal;
signal.wait();

You can find a complete implementation here.

Conclusion: It makes sense to use a static std::unique_ptr<> to implement a temporary singleton for the following reasons:

  • automatic management of lifetime
  • explicit operator bool() const available
  • retains sole ownership
  • providing exception safety

Enjoy the healing power of C++.


comments powered by Disqus