aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--butl/buildfile3
-rw-r--r--butl/diagnostics198
-rw-r--r--butl/diagnostics.cxx41
-rw-r--r--butl/utility47
-rw-r--r--butl/utility.cxx10
5 files changed, 295 insertions, 4 deletions
diff --git a/butl/buildfile b/butl/buildfile
index fcb5f86..1607e7d 100644
--- a/butl/buildfile
+++ b/butl/buildfile
@@ -5,6 +5,7 @@
lib{butl}: \
{hxx cxx}{ base64 } \
{hxx cxx}{ char-scanner } \
+{hxx cxx}{ diagnostics } \
{hxx }{ export } \
{hxx ixx cxx}{ fdstream } \
{hxx ixx cxx}{ filesystem } \
@@ -23,7 +24,7 @@ lib{butl}: \
{hxx txx }{ string-table } \
{hxx cxx}{ timestamp } \
{hxx cxx}{ triplet } \
-{hxx ixx }{ utility } \
+{hxx ixx cxx}{ utility } \
{hxx }{ vector-view } \
{hxx }{ version }
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
diff --git a/butl/diagnostics.cxx b/butl/diagnostics.cxx
new file mode 100644
index 0000000..e4c22dc
--- /dev/null
+++ b/butl/diagnostics.cxx
@@ -0,0 +1,41 @@
+// file : butl/diagnostics.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <butl/diagnostics>
+
+#include <iostream> // cerr
+
+using namespace std;
+
+namespace butl
+{
+ ostream* diag_stream = &cerr;
+
+ void diag_record::
+ flush () const
+ {
+ if (!empty_)
+ {
+ *diag_stream << os.str () << endl;
+ empty_ = true;
+
+ if (epilogue_ != nullptr)
+ epilogue_ (*this); // Can throw.
+ }
+ }
+
+ diag_record::
+ ~diag_record () noexcept (false)
+ {
+ // Don't flush the record if this destructor was called as part of
+ // the stack unwinding. Right now this means we cannot use this
+ // mechanism in destructors, which is not a big deal, except for
+ // one place: exception_guard. So for now we are going to have
+ // this ugly special check which we will be able to get rid of
+ // once C++17 uncaught_exceptions() becomes available.
+ //
+ if (!std::uncaught_exception () || exception_unwinding_dtor)
+ flush ();
+ }
+}
diff --git a/butl/utility b/butl/utility
index a513325..6155207 100644
--- a/butl/utility
+++ b/butl/utility
@@ -6,9 +6,10 @@
#define BUTL_UTILITY
#include <string>
-#include <cstddef> // size_t
-#include <utility> // forward()
-#include <cstring> // strcmp(), strlen()
+#include <cstddef> // size_t
+#include <utility> // move(), forward()
+#include <cstring> // strcmp(), strlen()
+#include <exception> // uncaught_exception()
//#include <functional> // hash
namespace butl
@@ -146,6 +147,46 @@ namespace butl
template <typename T>
inline reverse_range<T>
reverse_iterate (T&& x) {return reverse_range<T> (std::forward<T> (x));}
+
+ // Call a function if there is an exception.
+ //
+
+ // True means we are in the body of a destructor that is being called as
+ // part of the exception stack unwindining. Used to compensate for the
+ // deficiencies of uncaught_exception() until C++17 uncaught_exceptions()
+ // becomes available.
+ //
+ // @@ MT: will have to be TLS.
+ //
+ extern bool exception_unwinding_dtor;
+
+ template <typename F>
+ struct exception_guard;
+
+ template <typename F>
+ inline exception_guard<F>
+ make_exception_guard (F f)
+ {
+ return exception_guard<F> (std::move (f));
+ }
+
+ template <typename F>
+ struct exception_guard
+ {
+ exception_guard (F f): f_ (std::move (f)) {}
+ ~exception_guard ()
+ {
+ if (std::uncaught_exception ())
+ {
+ exception_unwinding_dtor = true;
+ f_ ();
+ exception_unwinding_dtor = false;
+ }
+ }
+
+ private:
+ F f_;
+ };
}
#include <butl/utility.ixx>
diff --git a/butl/utility.cxx b/butl/utility.cxx
new file mode 100644
index 0000000..d6f418c
--- /dev/null
+++ b/butl/utility.cxx
@@ -0,0 +1,10 @@
+// file : butl/utility.cxx -*- C++ -*-
+// copyright : Copyright (c) 2014-2016 Code Synthesis Ltd
+// license : MIT; see accompanying LICENSE file
+
+#include <butl/utility>
+
+namespace butl
+{
+ bool exception_unwinding_dtor = false;
+}