Skip to content
Snippets Groups Projects
debugstream.hh 12.3 KiB
Newer Older
  • Learn to ignore specific revisions
  • // -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
    // vi: set et ts=4 sw=2 sts=2:
    
    #ifndef DUNE_DEBUGSTREAM_HH
    #define DUNE_DEBUGSTREAM_HH
    
    
    Oliver Sander's avatar
    Oliver Sander committed
    /** \file
     * \brief Defines several output streams for messages of different importance
     */
    
    
    #include <dune/common/exceptions.hh>
    
    
    namespace Dune {
    
      /*! \defgroup DebugOut Debug output
    
         \ingroup Common
    
    Oliver Sander's avatar
    Oliver Sander committed
         The debug output is implemented by instances of DebugStream which
    
         provides the following features:
    
         - output-syntax in the standard ostream-notation
         - output can be totally deactivated depending on template parameters
         - streams with active output can be deactivated during runtime
         - redirecting to std::ostream or other DebugStream s during runtime
         - stack oriented state
    
         The Dune-components should use the streams explained in \ref StdStreams
         for output so that applications may redirect the output globally.
    
         Changes in runtime are provided by three sets of methods:
    
         - push()/pop() sets new activation flag or restore old setting
         - attach()/detach() redirects output to a different std::ostream or restore old stream
         - tie()/untie() redirects output through another DebugStream. If the state of the master stream changes (activation or output-stream) it is changed in the tied stream as well
    
         The first methods implement a full stack whereas tie() is a bit
         different: though a tied stream may be (de)activated via
         push()/pop() you cannot attach() or detach() an output. You'll need
         to change the master stream instead.
    
         \section DebugAppl Applications
    
         Applications using the Dune-library should create an independent set
    
    Oliver Sander's avatar
    Oliver Sander committed
         of DebugStreams so that the debug levels can be changed separately.
    
         Example:
    
         \code
         static const Dune::DebugLevel APPL_MINLEVEL = 3;
    
         Dune::DebugStream<1, APPL_MINLEVEL> myverbose;
         Dune::DebugStream<2, APPL_MINLEVEL> myinfo;
         Dune::DebugStream<3, APPL_MINLEVEL> mywarn;
         \endcode
    
         This code creates three streams of which only the last one really
         creates output. The output-routines of the other streams vanish in
         optimized executables.
    
         You can use the common_bits-Template to switch to a policy using bitflags:
    
         \code
         enum { APPL_CORE = 1, APPL_IO = 2, APPL_GRAPHICS = 4};
    
         static const Dune::DebugLevel APPL_DEBUG_MASK = APPL_CORE | APPL_GRAPHICS;
         static const Dune::DebugLevel APPL_ACTIVE_MASK = 0xff;
    
         Dune::DebugStream<APPL_CORE, APPL_DEBUG_MASK, APPL_ACTIVE_MASK, Dune::common_bits> coreout;
         Dune::DebugStream<APPL_IO, APPL_DEBUG_MASK, APPL_ACTIVE_MASK, Dune::common_bits> ioout;
         Dune::DebugStream<APPL_GRAPHICS, APPL_DEBUG_MASK, APPL_ACTIVE_MASK, Dune::common_bits> graphout;
         \endcode
    
    
    Thimo Neubauer's avatar
    Thimo Neubauer committed
         Applications that wish to redirect the \ref StdStreams through their
         private streams may use the tie()-mechanism:
    
    
         \code
         // initialize streams like above
    
         Dune::dwarn.tie(coreout);
    
         // ... Dune-output to dwarn will be directed through coreout ...
    
         Dune::dwarn.untie();
         \endcode
    
         Keep in mind to untie() a stream before the tied stream is destructed.
    
    
    Thimo Neubauer's avatar
    Thimo Neubauer committed
         An alternative is to attach() an output stream defined by the application:
    
         \code
         std::ofstream mylog("application.log");
    
         Dune::dwarn.attach(mylog);
         \endcode
    
    Markus Blatt's avatar
    Markus Blatt committed
      /**
         \addtogroup DebugOut
         \{
       */
      /*! \file
    
         This file implements the class DebugStream to support output in a
         variety of debug levels. Additionally, template parameters control
         if the output operation is really performed so that unused debug
         levels can be deactivated
    
       */
    
    
    Markus Blatt's avatar
    Markus Blatt committed
      /*! \brief Type for debug levels.
    
         Only positive values allowed
       */
    
    Markus Blatt's avatar
    Markus Blatt committed
    
         \brief Greater or equal template test.
    
    
         value is false if current is below the threshold, true otherwise
    
         This is the default struct to control the activation policy of
         DebugStream and deactivates output below the threshold
       */
      template <DebugLevel current, DebugLevel threshold>
      struct greater_or_equal {
    
        static const bool value = (current >= threshold);
    
    Markus Blatt's avatar
    Markus Blatt committed
      /*! \brief activate if current and mask have common bits switched on.
    
    
         This template implements an alternative strategy to activate or
         deactivate a DebugStream. Keep in mind to number your streams as
         powers of two if using this template
       */
      template <DebugLevel current, DebugLevel mask>
      struct common_bits {
    
    Markus Blatt's avatar
    Markus Blatt committed
      //! \brief standard exception for the debugstream
    
      class DebugStreamError : public IOError {};
    
    
      class StreamWrap {
      public:
        StreamWrap(std::ostream& _out) : out(_out) { };
        std::ostream& out;
        StreamWrap *next;
      };
    
    Markus Blatt's avatar
    Markus Blatt committed
      //! \brief Intermediate class to implement tie-operation of DebugStream
    
      class DebugStreamState {
        // !!! should be protected somehow but that won't be easy
      public:
    
    Markus Blatt's avatar
    Markus Blatt committed
        //! \brief current output stream and link to possibly pushed old output streams
    
    Markus Blatt's avatar
    Markus Blatt committed
        //! \brief flag to switch output during runtime
    
    Markus Blatt's avatar
    Markus Blatt committed
        //! \brief are we tied to another DebugStream?
    
    Markus Blatt's avatar
    Markus Blatt committed
        //! \brief how many streams are tied to this state
    
    Markus Blatt's avatar
    Markus Blatt committed
         \brief Generic class to implement debug output streams
    
    
         The main function of a DebugStream is to provide output in a
         standard ostream fashion that is fully deactivated if the level of
         the stream does not meet the current requirements. More information in \ref DebugOut
    
         \param thislevel this level
         \param dlevel level needed for any output to happen
         \param alevel level needed to switch activation flag on
         \param activator template describing the activation policy
    
         \todo Fix visibility of internal data
       */
      template <DebugLevel thislevel = 1,
    
          DebugLevel dlevel = 1,
          DebugLevel alevel = 1,
    
          template<DebugLevel, DebugLevel> class activator = greater_or_equal>
      class DebugStream : public DebugStreamState {
    
    Markus Blatt's avatar
    Markus Blatt committed
        /*! \brief Create a DebugStream and set initial output stream
    
    
           during runtime another stream can be attach()ed, however the
           initial stream may not be detach()ed.
         */
        DebugStream(std::ostream& out = std::cerr) {
          // start a new list of streams
          current = new StreamWrap(out);
          current->next = 0;
    
          // check if we are above the default activation level
          _active = activator<thislevel,alevel>::value;
    
          // we're not tied to another DebugStream
          _tied = false;
    
          // no child streams yet
          _tied_streams = 0;
        };
    
    
    Markus Blatt's avatar
    Markus Blatt committed
        /*! \brief Create a DebugStream and directly tie to another DebugStream
    
    
           The fallback is used if a DebugStream constructed via this method
           is untie()ed later. Otherwise the stream would be broken afterwards.
         */
        DebugStream (DebugStreamState& master,
                     std::ostream& fallback = std::cerr)
        {
    
          current = new StreamWrap(fallback);
    
          current->next = 0;
    
          // check if we are above the default activation level
    
          _active = activator<thislevel,alevel>::value;
          _tied_streams = 0;
    
          // tie to the provided stream
          _tied = true;
          tiedstate = &master;
          tiedstate->_tied_streams++;
    
    Markus Blatt's avatar
    Markus Blatt committed
        /*! \brief Destroy stream.
    
    
           if other streams still tie() to this stream an exception will be
           thrown. Otherwise the child streams would certainly break on the
           next output
         */
    
          // untie
          if (_tied)
            tiedstate->_tied_streams--;
          else {
            // check if somebody still ties to us...
            if (_tied_streams != 0)
              DUNE_THROW(DebugStreamError,
                         "There are streams still tied to this stream!");
          };
    
    
          // remove ostream-stack
          while (current != 0) {
    
    Markus Blatt's avatar
    Markus Blatt committed
        //! \brief Generic types are passed on to current output stream
    
        DebugStream& operator<<(const T data) {
          // remove the following code if stream wasn't compiled active
          if (activator<thislevel, dlevel>::value) {
            if (! _tied) {
              if (_active)
                current->out << data;
            } else {
              if (_active && tiedstate->_active)
                tiedstate->current->out << data;
            };
          };
    
          return *this;
        }
    
    
    Markus Blatt's avatar
    Markus Blatt committed
        /*! \brief explicit specialization so that enums can be printed
    
           Operators for built-in types follow special
    
    Carsten Gräser's avatar
    Carsten Gräser committed
           rules (§11.2.3) so that enums won't fit into the generic
    
           method above. With an existing operator<< for int however
           the enum will be automatically casted.
         */
        DebugStream& operator<<(const int data) {
    
          // remove the following code if stream wasn't compiled active
          if (activator<thislevel, dlevel>::value) {
            if (! _tied) {
              if (_active)
                current->out << data;
            } else {
              if (_active && tiedstate->_active)
                tiedstate->current->out << data;
            };
          };
    
    Markus Blatt's avatar
    Markus Blatt committed
        //! \brief pass on manipulators to underlying output stream
    
        DebugStream& operator<<(std::ostream& (*f)(std::ostream&)) {
    
          if (activator<thislevel, dlevel>::value) {
            if (! _tied) {
              if (_active)
                f(current->out);
            } else {
              if (_active && tiedstate->_active)
                f(tiedstate->current->out);
            };
          }
    
    
    Christian Engwer's avatar
    Christian Engwer committed
        //! \brief pass on flush to underlying output stream
        DebugStream& flush() {
          if (activator<thislevel, dlevel>::value) {
            if (! _tied) {
              if (_active)
                current->out.flush();
            } else {
              if (_active && tiedstate->_active)
                tiedstate->current->out.flush();
            };
          }
    
          return *this;
        };
    
    
    Markus Blatt's avatar
    Markus Blatt committed
        //! \brief set activation flag and store old value
    
          // are we at all active?
          if (activator<thislevel,alevel>::value) {
            _actstack.push(_active);
            _active = b;
          } else {
            // stay off
            _actstack.push(false);
          };
    
    Markus Blatt's avatar
    Markus Blatt committed
        //! \brief restore previously set activation flag
    
        void pop() throw(DebugStreamError) {
    
          if (_actstack.empty())
            DUNE_THROW(DebugStreamError, "No previous activation setting!");
    
          _active = _actstack.top();
          _actstack.pop();
        };
    
    
    Markus Blatt's avatar
    Markus Blatt committed
        /*! \brief reports if this stream will produce output
    
    
           a DebugStream that is deactivated because of its level will always
           return false, otherwise the state of the internal activation is
           returned
         */
    
          return activator<thislevel, dlevel>::value && _active;
    
    Markus Blatt's avatar
    Markus Blatt committed
        /*! \brief set output to a different stream.
    
           Old stream data is stored
         */
    
        void attach(std::ostream& stream) {
    
          if (_tied)
            DUNE_THROW(DebugStreamError, "Cannot attach to a tied stream!");
    
          StreamWrap* newcurr = new StreamWrap(stream);
    
          newcurr->next = current;
          current = newcurr;
        };
    
    
    Markus Blatt's avatar
    Markus Blatt committed
        //! \brief detach current output stream and restore to previous stream
    
        void detach() throw(DebugStreamError) {
    
          if (current->next == 0)
            DUNE_THROW(DebugStreamError, "Cannot detach initial stream!");
    
          if (_tied)
            DUNE_THROW(DebugStreamError, "Cannot detach a tied stream!");
    
    Markus Blatt's avatar
    Markus Blatt committed
        // \brief Tie a stream to this one.
    
        void tie(DebugStreamState& to) throw(DebugStreamError) {
          if (to._tied)
            DUNE_THROW(DebugStreamError, "Cannot tie to an already tied stream!");
          if (_tied)
            DUNE_THROW(DebugStreamError, "Stream already tied: untie first!");
    
          _tied = true;
          tiedstate = &to;
    
          // tell master class
          tiedstate->_tied_streams++;
        };
    
    Markus Blatt's avatar
    Markus Blatt committed
        //! \brief Untie stream
    
        void untie() throw(DebugStreamError) {
          if(! _tied)
            DUNE_THROW(DebugStreamError, "Cannot untie, stream is not tied!");
    
          tiedstate->_tied_streams--;
          _tied = false;
          tiedstate = 0;
    
    Markus Blatt's avatar
    Markus Blatt committed
        //! \brief pointer to data of stream we're tied to
    
    Markus Blatt's avatar
    Markus Blatt committed
        /*! \brief Activation state history.
    
           store old activation settings so that the outside code doesn't
           need to remember */
    
    Markus Blatt's avatar
    Markus Blatt committed
    
      /** /} */