aboutsummaryrefslogtreecommitdiff
path: root/butl/diagnostics
diff options
context:
space:
mode:
Diffstat (limited to 'butl/diagnostics')
-rw-r--r--butl/diagnostics198
1 files changed, 198 insertions, 0 deletions
diff --git a/butl/diagnostics b/butl/diagnostics
new file mode 100644
index 0000000..77f3f9c
--- /dev/null
+++ b/butl/diagnostics
@@ -0,0 +1,198 @@
+// file : butl/diagnostics -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#ifndef BUTL_DIAGNOSTICS
+#define BUTL_DIAGNOSTICS
+
+#include <cassert>
+#include <ostream>
+#include <sstream>
+#include <utility> // move(), forward()
+#include <exception> // uncaught_exception()
+
+#include <butl/utility>
+
+namespace butl
+{
+ // Diagnostic facility base infrastructure.
+ //
+ extern std::ostream* diag_stream; // std::cerr by default.
+
+ struct diag_record;
+ template <typename> struct diag_prologue;
+ template <typename> struct diag_mark;
+
+ using diag_epilogue = void (const diag_record&);
+
+ struct diag_record
+ {
+ template <typename T>
+ friend const diag_record&
+ operator<< (const diag_record& r, const T& x)
+ {
+ r.os << x;
+ return r;
+ }
+
+ diag_record (): empty_ (true), epilogue_ (nullptr) {}
+
+ template <typename B>
+ explicit
+ diag_record (const diag_prologue<B>& p)
+ : empty_ (true), epilogue_ (nullptr) { *this << p;}
+
+ template <typename B>
+ explicit
+ diag_record (const diag_mark<B>& m)
+ : empty_ (true), epilogue_ (nullptr) { *this << m;}
+
+ ~diag_record () noexcept (false);
+
+ bool
+ empty () const {return empty_;}
+
+ bool
+ full () const {return !empty_;}
+
+ void
+ flush () const;
+
+ void
+ append (diag_epilogue* e) const
+ {
+ if (e != nullptr)
+ {
+ assert (epilogue_ == nullptr); // No multiple epilogues support.
+ epilogue_ = e;
+ }
+
+ if (empty_)
+ empty_ = false;
+ else
+ os << "\n ";
+ }
+
+ // Move constructible-only type.
+ //
+ // Older versions of libstdc++ don't have the ostringstream move support
+ // and accuratly detecting its version is non-trivial. So we always use
+ // the pessimized implementation with libstdc++. Luckily, GCC doesn't seem
+ // to be needing move due to copy/move elision.
+ //
+#ifdef __GLIBCXX__
+ diag_record (diag_record&&);
+#else
+ diag_record (diag_record&& r)
+#ifndef __GLIBCXX__
+ : os (std::move (r.os))
+#endif
+ {
+ empty_ = r.empty_;
+ epilogue_ = r.epilogue_;
+
+ if (!empty_)
+ {
+#ifdef __GLIBCXX__
+ // os << r.os.str ();
+ assert (false); // No way to copy extra stream data?
+#endif
+ r.empty_ = true;
+ r.epilogue_ = nullptr;
+ }
+ }
+#endif
+
+ diag_record& operator= (diag_record&&) = delete;
+
+ diag_record (const diag_record&) = delete;
+ diag_record& operator= (const diag_record&) = delete;
+
+ public:
+ mutable std::ostringstream os;
+
+ protected:
+ mutable bool empty_;
+ mutable diag_epilogue* epilogue_;
+ };
+
+ template <typename B>
+ struct diag_prologue: B
+ {
+ diag_prologue (diag_epilogue* e = nullptr): B (), epilogue_ (e) {}
+
+ template <typename... A>
+ diag_prologue (A&&... a)
+ : B (std::forward<A> (a)...), epilogue_ (nullptr) {}
+
+ template <typename... A>
+ diag_prologue (diag_epilogue* e, A&&... a)
+ : B (std::forward<A> (a)...), epilogue_ (e) {}
+
+ template <typename T>
+ diag_record
+ operator<< (const T& x) const
+ {
+ diag_record r;
+ r.append (epilogue_);
+ B::operator() (r);
+ r << x;
+ return r;
+ }
+
+ friend const diag_record&
+ operator<< (const diag_record& r, const diag_prologue& p)
+ {
+ r.append (p.epilogue_);
+ p (r);
+ return r;
+ }
+
+ private:
+ diag_epilogue* epilogue_;
+ };
+
+ template <typename B>
+ struct diag_mark: B
+ {
+ diag_mark (): B () {}
+
+ template <typename... A>
+ diag_mark (A&&... a): B (std::forward<A> (a)...) {}
+
+ template <typename T>
+ diag_record
+ operator<< (const T& x) const
+ {
+ return B::operator() () << x;
+ }
+
+ friend const diag_record&
+ operator<< (const diag_record& r, const diag_mark& m)
+ {
+ return r << m ();
+ }
+ };
+
+ template <typename B>
+ struct diag_noreturn_end: B
+ {
+ diag_noreturn_end (): B () {}
+
+ template <typename... A>
+ diag_noreturn_end (A&&... a): B (std::forward<A> (a)...) {}
+
+ [[noreturn]] friend void
+ operator<< (const diag_record& r, const diag_noreturn_end& e)
+ {
+ // We said that we never return which means this end mark cannot be used
+ // to "maybe not return". And not returning without any diagnostics is
+ // probably a mistake.
+ //
+ assert (r.full ());
+ e.B::operator() (r);
+ }
+ };
+}
+
+#endif // BUTL_DIAGNOSTICS