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/utility.mxx | 285 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 libbutl/utility.mxx (limited to 'libbutl/utility.mxx') diff --git a/libbutl/utility.mxx b/libbutl/utility.mxx new file mode 100644 index 0000000..6a50186 --- /dev/null +++ b/libbutl/utility.mxx @@ -0,0 +1,285 @@ +// file : libbutl/utility.mxx -*- C++ -*- +// copyright : Copyright (c) 2014-2017 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +#ifndef __cpp_modules +#pragma once +#endif + +#ifndef _WIN32 +#if !defined(__cpp_lib_modules) || !defined(__clang__) //@@ MOD Clang ICE +# include // strcasecmp(), strncasecmp() +#endif +#else +# include // _stricmp(), _strnicmp() +#endif + +#ifndef __cpp_lib_modules +#include +#include // ostream +#include // size_t +#include // move(), forward() +#include // strcmp(), strlen() +#include // exception, uncaught_exception(s)() +//#include // hash + +#include // toupper(), tolower(), isalpha(), isdigit(), isalnum() +#endif + +#include // thread_local +#include // uncaught_exceptions + +#ifdef __cpp_modules +export module butl.utility; +#ifdef __cpp_lib_modules +import std.core; +import std.io; +#endif +#endif + +#include + + +LIBBUTL_MODEXPORT namespace butl +{ + // Throw std::system_error with generic_category or system_category, + // respectively. + // + // The generic version should be used for portable errno codes (those that + // are mapped to std::errc). The system version should be used for platform- + // specific codes, for example, additional errno codes on POSIX systems or + // the result of GetLastError() on Windows. + // + // See also the exception sanitization below. + // + [[noreturn]] LIBBUTL_SYMEXPORT void + throw_generic_error (int errno_code, const char* what = nullptr); + + [[noreturn]] LIBBUTL_SYMEXPORT void + throw_system_error (int system_code, int fallback_errno_code = 0); + + // Convert ASCII character/string case. If there is no upper/lower case + // counterpart, leave the character unchanged. The POSIX locale (also known + // as C locale) must be the current application locale. Otherwise the + // behavior is undefined. + // + // Note that the POSIX locale specifies behaviour on data consisting + // entirely of characters from the portable character set (subset of ASCII + // including 103 non-negative characters and English alphabet letters in + // particular) and the control character set (more about them at + // http://pubs.opengroup.org/onlinepubs/009696899/basedefs/xbd_chap06.html). + // + // Also note that according to the POSIX locale definition the case + // conversion can be applied only to [A-Z] and [a-z] character ranges being + // translated to each other (more about that at + // http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap07.html#tag_07_02) + // + char ucase (char); + std::string ucase (const char*, std::size_t = -1 /*@@ MOD VC ICE = std::string::npos*/); + + std::string ucase (const std::string&); + std::string& ucase (std::string&); + void ucase (char*, std::size_t); + + char lcase (char); + std::string lcase (const char*, std::size_t = -1 /*@@ MOD VC ICE = std::string::npos*/); + std::string lcase (const std::string&); + std::string& lcase (std::string&); + void lcase (char*, std::size_t); + + // Compare ASCII characters/strings ignoring case. Behave as if characters + // had been converted to the lower case and then byte-compared. Return a + // negative, zero or positive value if the left hand side is less, equal or + // greater than the right hand side, respectivelly. The POSIX locale (also + // known as C locale) must be the current application locale. Otherwise the + // behavior is undefined. + // + // The optional size argument specifies the maximum number of characters + // to compare. + // + int casecmp (char, char); + + int casecmp (const std::string&, const std::string&, + std::size_t = -1 /*@@ MOD VC ICE std::string::npos*/); + + int casecmp (const std::string&, const char*, + std::size_t = -1 /*@@ MOD VC ICE std::string::npos*/); + + int casecmp (const char*, const char*, std::size_t = -1 /*MOD VC ICE std::string::npos*/); + + // Case-insensitive key comparators (i.e., to be used in sets, maps, etc). + // + struct case_compare_string + { + bool operator() (const std::string& x, const std::string& y) const + { + return casecmp (x, y) < 0; + } + }; + + struct case_compare_c_string + { + bool operator() (const char* x, const char* y) const + { + return casecmp (x, y) < 0; + } + }; + + bool + alpha (char); + + bool + digit (char); + + bool + alnum (char); + + // Key comparators (i.e., to be used in sets, maps, etc). + // + struct compare_c_string + { + bool operator() (const char* x, const char* y) const noexcept + { + return std::strcmp (x, y) < 0; + } + }; + + struct compare_pointer_target + { + template + bool operator() (const P& x, const P& y) const {return *x < *y;} + }; + + //struct hash_pointer_target + //{ + // template + // std::size_t operator() (const P& x) const {return std::hash (*x);} + //}; + + // Combine one or more hash values. + // + inline std::size_t + combine_hash (std::size_t s, std::size_t h) + { + // Magic formula from boost::hash_combine(). + // + return s ^ (h + 0x9e3779b9 + (s << 6) + (s >> 2)); + } + + template + inline std::size_t + combine_hash (std::size_t s, std::size_t h, S... hs) + { + return combine_hash (combine_hash (s, h), hs...); + } + + // Support for reverse iteration using range-based for-loop: + // + // for (... : reverse_iterate (x)) ... + // + template + class reverse_range + { + T x_; + + public: + reverse_range (T&& x): x_ (std::forward (x)) {} + + auto begin () const -> decltype (this->x_.rbegin ()) {return x_.rbegin ();} + auto end () const -> decltype (this->x_.rend ()) {return x_.rend ();} + }; + + template + inline reverse_range + reverse_iterate (T&& x) {return reverse_range (std::forward (x));} + + // Call a function if there is an exception. + // + + template + struct exception_guard; + + template + inline exception_guard + make_exception_guard (F f) + { + return exception_guard (std::move (f)); + } + +#ifdef __cpp_lib_uncaught_exceptions + template + struct exception_guard + { + exception_guard (F f) + : f_ (std::move (f)), + u_ (std::uncaught_exceptions ()) {} + + ~exception_guard () + { + if (u_ != std::uncaught_exceptions ()) + f_ (); + } + + private: + F f_; + int u_; + }; +#else + // Fallback implementation using a TLS flag. + // + // True means we are in the body of a destructor that is being called as + // part of the exception stack unwindining. + // + extern +#ifdef __cpp_thread_local + thread_local +#else + __thread +#endif + bool exception_unwinding_dtor_; + + // On Windows one cannot export a thread-local variable so we have to + // use a wrapper functions. + // +#ifdef _WIN32 + LIBBUTL_SYMEXPORT bool& + exception_unwinding_dtor (); +#else + inline bool& + exception_unwinding_dtor () {return exception_unwinding_dtor_;} +#endif + + template + 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_; + }; +#endif +} + +LIBBUTL_MODEXPORT namespace std +{ + // Sanitize the exception description before printing. This includes: + // + // - stripping leading colons and spaces (see fdstream.cxx) + // - stripping trailing newlines, periods, and spaces + // - stripping system error redundant suffix (see utility.cxx) + // - lower-case the first letter if the beginning looks like a word + // + LIBBUTL_SYMEXPORT ostream& + operator<< (ostream&, const exception&); +} + +#include -- cgit v1.1