Boost logo

Boost :

From: Ryan Gallagher (ryan.gallagher_at_[hidden])
Date: 2006-05-09 15:22:43


Gennadiy Rozental wrote:
> SIGNAL catching is optional and doesn't constitute portabiltiy issues per se,
> whether to use it's up to you.

Out of curiousity, what is a good (portable?) way of dealing with signals in
C++? C signal handling seems to force globals on you, which is a pain
especially when signal handling is a late requirement.

Doing a bit of online research recently I read that some people prefer to
translate signals to exceptions. As a co-worker of mine has pointed out this
is how python deals with it (with python exceptions that is).
>From what I've read though this is not portable, and, besides, is often not the
control flow I would desire.

Further reading suggested the use of singletons for registration
of handlers. This (glorified global) seemed to make sense to me and I came
up with the attached code for this. However, I do refer to statics here,
which also may be non-portable. It does work with msvc-7.1 though.
(Is it the case of multi-processors that makes this non-portable?)

(Perhaps this should move to comp.lang.c++.moderated? This code is welcomed
to be used by the boost community though. Distributed under boost software
licence and all that...)

Please feel free to critique in discussing this issue as I'm fairly
inexperienced with dealing with signals outside of C. If possible I'd
like to see a solution like this that works on boost's supported platforms
and released as part of boost.

-Ryan

/// Singleton class to register and dispatch to handlers for c-style signals.
/// @note Because the signal_registry::dispatch handler refers to the static
/// instance of this class, which is not of type sig_atomic_t, this
/// class is technically incurring undefined behavior. In practice
/// compiler implementation support this action (at least better so
/// that throwing an exception from the handler).
///
class signal_registry
   : boost::noncopyable
{
private:
   typedef boost::signal<void (int)> signal_type;
   typedef boost::shared_ptr<signal_type> signal_pointer;
   typedef std::map<int, signal_pointer> signal_id_to_handler_map_type;

public:
   /// Access to the singleton instance.
   /// @return The singleton instance.
   static signal_registry& instance()
   {
      static signal_registry meyers_singleton_instance;
      return meyers_singleton_instance;
   }

   /// Register a new handler to call on the given signal.
   /// When signaled, the handlers are called in the reverse order registered.
   /// @note previous handlers are not replaced.
   /// @param signal_id The id of the signal for which to register the handler
   /// (e.g. SIGINT).
   /// @param handler The callback for handling the given signal. It's
   /// argument will be the signal_id.
   boost::signals::connection register_handler(int signal_id,
      boost::function<void (int)> const& handler)
   {
      if(m_signals.count(signal_id) == 0)
      {
         std::signal(signal_id, &signal_registry::dispatch);
         m_signals[signal_id] = signal_pointer(new signal_type());
      }

      return m_signals[signal_id]->connect(handler, boost::signals::at_front);
   }

   /// Dispatch the given signal to the stored handlers, if any.
   /// @param signal_id The id of the signal raised (e.g. SIGINT).
   /// @retval true If a handler was called.
   /// @retval false Otherwise.
   static void dispatch(int signal_id)
   {
      instance().dispatch_impl(signal_id);
   }

private:
   void dispatch_impl(int signal_id) const
   {
      signal_id_to_handler_map_type::const_iterator handler_iter =
         m_signals.find(signal_id);

      if(handler_iter != m_signals.end() && !((handler_iter->second)->empty()))
      {
         (*(handler_iter->second))(signal_id);
      }
   }

   signal_id_to_handler_map_type m_signals;

   ~signal_registry() {}
};


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk