From c09cd7512491cee1e82c1ad8128ce9fd4bc3f79b Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Fri, 22 Sep 2017 23:32:28 +0200 Subject: Initial modularization with both Clang and VC hacks Note: gave up on VC about half way though. --- libbutl/diagnostics.mxx | 275 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 275 insertions(+) create mode 100644 libbutl/diagnostics.mxx (limited to 'libbutl/diagnostics.mxx') diff --git a/libbutl/diagnostics.mxx b/libbutl/diagnostics.mxx new file mode 100644 index 0000000..af74956 --- /dev/null +++ b/libbutl/diagnostics.mxx @@ -0,0 +1,275 @@ +// file : libbutl/diagnostics.mxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef __cpp_modules +#pragma once +#endif + +#include + +#ifndef __cpp_lib_modules +#include +#include +#include // move(), forward() +#include // uncaught_exception[s]() +#endif + +#include // uncaught_exceptions + +#ifdef __cpp_modules +export module butl.diagnostics; +#ifdef __cpp_lib_modules +import std.core; +import std.io; +#endif +#endif + +#include + +LIBBUTL_MODEXPORT namespace butl +{ + // Diagnostic facility base infrastructure. + // + + // Diagnostics destination stream (std::cerr by default). Note that its + // modification is not MT-safe. Also note that concurrent writing to the + // stream from multiple threads can result in interleaved characters. To + // prevent this an object of diag_stream_lock type (see below) must be + // created prior to write operation. + // + 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). + // + struct LIBBUTL_SYMEXPORT diag_stream_lock + { + diag_stream_lock (); + ~diag_stream_lock (); + + // Support for one-liners, for example: + // + // diag_stream_lock () << "Hello, World!" << endl; + // + template + std::ostream& + operator<< (const T& x) const + { + return *diag_stream << x; + } + }; + + // Progress line facility. + // + // The idea is to keep a progress line at the bottom of the terminal with + // other output scrolling above it. For a non-terminal STDERR the progress + // line is printed as a regular one terminated with the newline character. + // The printing of the progress line is integrated into diag_stream_lock and + // diag_progress_lock. To print or update the progress acquire + // diag_progress_lock and update the diag_progress string. To remove the + // progress line, set this string to empty. For better readability start the + // progress line with a space (which is where the cursor will be parked). + // Should only be used if diag_stream points to std::cerr. + // + // Note that child processes writing to the same stream may not completely + // overwrite the progress line so in this case it makes sense to keep the + // line as short as possible. + // + // To restore the progress line being overwritten by an independent writer + // (such as a child process), create and destroy the diag_progress_lock. + // + LIBBUTL_SYMEXPORT extern std::string diag_progress; + + struct LIBBUTL_SYMEXPORT diag_progress_lock + { + diag_progress_lock (); + ~diag_progress_lock (); + }; + + // + // + struct diag_record; + template struct diag_prologue; + template struct diag_mark; + + using diag_epilogue = void (const diag_record&); + + struct LIBBUTL_SYMEXPORT diag_record + { + template + const diag_record& + operator<< (const T& x) const + { + os << x; + return *this; + } + + diag_record () + : +#ifdef __cpp_lib_uncaught_exceptions + uncaught_ (std::uncaught_exceptions ()), +#endif + empty_ (true), + epilogue_ (nullptr) {} + + template + explicit + diag_record (const diag_prologue& p): diag_record () { *this << p;} + + template + explicit + diag_record (const diag_mark& m): diag_record () { *this << m;} + + ~diag_record () noexcept (false); + + bool + empty () const {return empty_;} + + bool + full () const {return !empty_;} + + void + flush () const; + + void + append (const char* indent, diag_epilogue* e) const + { + // Ignore subsequent epilogues (e.g., from nested marks, etc). + // + if (empty_) + { + epilogue_ = e; + empty_ = false; + } + else if (indent != nullptr) + os << indent; + } + + // 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) + : +#ifdef __cpp_lib_uncaught_exceptions + uncaught_ (r.uncaught_), +#endif + empty_ (r.empty_), + epilogue_ (r.epilogue_), + os (std::move (r.os)) + { + if (!empty_) + { + 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; + + protected: +#ifdef __cpp_lib_uncaught_exceptions + const int uncaught_; +#endif + mutable bool empty_; + mutable diag_epilogue* epilogue_; + + public: + mutable std::ostringstream os; + }; + + template + struct diag_prologue: B + { + const char* indent; + diag_epilogue* epilogue; + + diag_prologue (const char* i = "\n ", diag_epilogue* e = nullptr) + : B (), indent (i), epilogue (e) {} + + template + diag_prologue (A&&... a) + : B (std::forward (a)...), indent ("\n "), epilogue (nullptr) {} + + template + diag_prologue (diag_epilogue* e, A&&... a) + : B (std::forward (a)...), indent ("\n "), epilogue (e) {} + + template + diag_prologue (const char* i, diag_epilogue* e, A&&... a) + : B (std::forward (a)...), indent (i), epilogue (e) {} + + template + diag_record + operator<< (const T& x) const + { + diag_record r; + r.append (indent, epilogue); + B::operator() (r); + r << x; + return r; + } + + friend const diag_record& + operator<< (const diag_record& r, const diag_prologue& p) + { + r.append (p.indent, p.epilogue); + p (r); + return r; + } + }; + + template + struct diag_mark: B + { + diag_mark (): B () {} + + template + diag_mark (A&&... a): B (std::forward (a)...) {} + + template + 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 + struct diag_noreturn_end: B + { + diag_noreturn_end (): B () {} + + template + diag_noreturn_end (A&&... a): B (std::forward (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); + } + }; +} -- cgit v1.1