Click here to Skip to main content
15,884,911 members
Articles / Programming Languages / C++
Tip/Trick

A Unique Singleton

Rate me:
Please Sign up or sign in to vote.
5.00/5 (2 votes)
25 May 2015Public Domain2 min read 16K   37   5   3
Proper implementation of a signal handler

The Usual Procedure

A singleton is a class that can be instantiated 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:

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

	//	implement interface here:

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

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

Handle Process Signals

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 does 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 real 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:

C++
void handle_signal(int sig);

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

C++
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 its lifetime:

C++
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:

C++
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 and to wait:

C++
signal_mgr signal;
signal.wait();

You can find a complete implementation in the download section and 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

History

  • 2015-05-24: Initial release

License

This article, along with any associated source code and files, is licensed under A Public Domain dedication


Written By
osy
Software Developer (Senior)
Switzerland Switzerland
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
SuggestionYou could have used std::call_once Pin
Espen Harlinn25-May-15 13:41
professionalEspen Harlinn25-May-15 13:41 
GeneralRe: You could have used std::call_once Pin
matt_taws26-May-15 3:04
matt_taws26-May-15 3:04 
GeneralRe: You could have used std::call_once Pin
Espen Harlinn26-May-15 12:34
professionalEspen Harlinn26-May-15 12:34 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.