diff options
Diffstat (limited to 'libbutl/diagnostics.hxx')
-rw-r--r-- | libbutl/diagnostics.hxx | 121 |
1 files changed, 115 insertions, 6 deletions
diff --git a/libbutl/diagnostics.hxx b/libbutl/diagnostics.hxx index 712de0c..c6db34b 100644 --- a/libbutl/diagnostics.hxx +++ b/libbutl/diagnostics.hxx @@ -27,8 +27,11 @@ namespace butl LIBBUTL_SYMEXPORT extern std::ostream* diag_stream; // Acquire the diagnostics exclusive access mutex in ctor, release in dtor. - // An object of the type must be created prior to writing to diag_stream (see - // above). + // An object of the type must be created prior to writing to diag_stream + // (see above). + // + // Note that this class also manages the interaction with the progress + // printing (see below). // struct LIBBUTL_SYMEXPORT diag_stream_lock { @@ -74,13 +77,26 @@ namespace butl ~diag_progress_lock (); }; + // Diagnostic record and marks (error, warn, etc). // + // There are two ways to use this facility in a project: simple, where we + // just alias the types in our namespace, and complex, where instead we + // derive from them and "override" (hide, really) operator<< (and a few + // other functions) in order to make ADL look in our namespace rather than + // butl. In the simple case we may have to resort to defining some + // operator<< overloads in namespace std in order to satisfy ADL. This is + // usually not an acceptable approach for libraries, which is where the + // complex case comes in (see libbuild2 for a "canonical" example of the + // complex case). Note also that it doesn't seem worth templatazing epilogue + // so the complex case may also need to do a few casts but those should be + // limited to the diagnostics infrastructure. // struct diag_record; template <typename> struct diag_prologue; template <typename> struct diag_mark; - using diag_epilogue = void (const diag_record&); + using diag_writer = void (const diag_record&); + using diag_epilogue = void (const diag_record&, diag_writer*); struct LIBBUTL_SYMEXPORT diag_record { @@ -117,7 +133,7 @@ namespace butl full () const {return !empty_;} void - flush () const; + flush (diag_writer* = nullptr) const; void append (const char* indent, diag_epilogue* e) const @@ -150,7 +166,7 @@ namespace butl #endif empty_ (r.empty_), epilogue_ (r.epilogue_), - os (std::move (r.os)) + os (std::move (r.os)) // Note: can throw. { if (!empty_) { @@ -168,7 +184,7 @@ namespace butl // Diagnostics writer. The default implementation writes the record text // to diag_stream. If it is NULL, then the record text is ignored. // - static void (*writer) (const diag_record&); + static diag_writer* writer; protected: #ifdef __cpp_lib_uncaught_exceptions @@ -263,4 +279,97 @@ namespace butl e.B::operator() (r); } }; + + // Diagnostics stack. Each frame is "applied" to the diag record. + // + // Unfortunately most of our use-cases don't fit into the 2-pointer small + // object optimization of std::function. So we have to complicate things + // a bit here. + // + struct LIBBUTL_SYMEXPORT diag_frame + { + explicit + diag_frame (void (*f) (const diag_frame&, const diag_record&)) + : func_ (f) + { + if (func_ != nullptr) + prev_ = stack (this); + } + + diag_frame (diag_frame&& x) + : func_ (x.func_) + { + if (func_ != nullptr) + { + prev_ = x.prev_; + stack (this); + + x.func_ = nullptr; + } + } + + diag_frame& operator= (diag_frame&&) = delete; + + diag_frame (const diag_frame&) = delete; + diag_frame& operator= (const diag_frame&) = delete; + + ~diag_frame () + { + if (func_ != nullptr ) + stack (prev_); + } + + // Normally passed as an epilogue. Writer is not used. + // + static void + apply (const diag_record& r, diag_writer* = nullptr) + { + for (const diag_frame* f (stack ()); f != nullptr; f = f->prev_) + f->func_ (*f, r); + } + + // Tip of the stack. + // + static const diag_frame* + stack () noexcept; + + // Set the new and return the previous tip of the stack. + // + static const diag_frame* + stack (const diag_frame*) noexcept; + + struct stack_guard + { + explicit stack_guard (const diag_frame* s): s_ (stack (s)) {} + ~stack_guard () {stack (s_);} + const diag_frame* s_; + }; + + private: + void (*func_) (const diag_frame&, const diag_record&); + const diag_frame* prev_; + }; + + template <typename F> + struct diag_frame_impl: diag_frame + { + explicit + diag_frame_impl (F f): diag_frame (&thunk), func_ (move (f)) {} + + private: + static void + thunk (const diag_frame& f, const diag_record& r) + { + static_cast<const diag_frame_impl&> (f).func_ (r); + } + + const F func_; + }; + + template <typename F> + inline diag_frame_impl<F> + make_diag_frame (F f) + { + return diag_frame_impl<F> (move (f)); + } } |