diff options
-rw-r--r-- | libbutl/diagnostics.cxx | 32 | ||||
-rw-r--r-- | libbutl/diagnostics.hxx | 105 |
2 files changed, 134 insertions, 3 deletions
diff --git a/libbutl/diagnostics.cxx b/libbutl/diagnostics.cxx index 8525d60..0826375 100644 --- a/libbutl/diagnostics.cxx +++ b/libbutl/diagnostics.cxx @@ -17,6 +17,8 @@ #include <cstddef> // size_t #include <iostream> // cerr +#include <libbutl/ft/lang.hxx> // thread_local + #include <libbutl/utility.hxx> #include <libbutl/optional.hxx> #include <libbutl/fdstream.hxx> @@ -131,14 +133,14 @@ namespace butl default_writer (const diag_record& r) { r.os.put ('\n'); - diag_stream_lock () << r.os.str (); + + diag_stream_lock l; + (*diag_stream) << r.os.str (); // We can endup flushing the result of several writes. The last one may // possibly be incomplete, but that's not a problem as it will also be // followed by the flush() call. // - // @@ Strange: why not just hold the lock for both write and flush? - // diag_stream->flush (); } @@ -186,4 +188,28 @@ namespace butl flush (); #endif } + + // Diagnostics stack. + // + static +#ifdef __cpp_thread_local + thread_local +#else + __thread +#endif + const diag_frame* diag_frame_stack = nullptr; + + const diag_frame* diag_frame:: + stack () noexcept + { + return diag_frame_stack; + } + + const diag_frame* diag_frame:: + stack (const diag_frame* f) noexcept + { + const diag_frame* r (diag_frame_stack); + diag_frame_stack = f; + return r; + } } diff --git a/libbutl/diagnostics.hxx b/libbutl/diagnostics.hxx index 712de0c..23aa14f 100644 --- a/libbutl/diagnostics.hxx +++ b/libbutl/diagnostics.hxx @@ -74,7 +74,19 @@ 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; @@ -263,4 +275,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. + // + static void + apply (const diag_record& r) + { + 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)); + } } |