aboutsummaryrefslogtreecommitdiff
path: root/libbutl/diagnostics.hxx
diff options
context:
space:
mode:
Diffstat (limited to 'libbutl/diagnostics.hxx')
-rw-r--r--libbutl/diagnostics.hxx121
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));
+ }
}