From 524322f78775dc14c61d33cbdb719b8330c2ad5c Mon Sep 17 00:00:00 2001 From: Boris Kolpackov Date: Wed, 27 Jun 2018 14:55:27 +0200 Subject: Reimplement optional not to require default-constructible value types --- libbutl/optional.ixx | 210 ++++++++++++++++++++++++++++++++++++++++++++++++++ libbutl/optional.mxx | 147 ++++++++++++++++++++++++++++------- libbutl/timestamp.cxx | 86 +++++++++++---------- 3 files changed, 376 insertions(+), 67 deletions(-) create mode 100644 libbutl/optional.ixx diff --git a/libbutl/optional.ixx b/libbutl/optional.ixx new file mode 100644 index 0000000..2c40d30 --- /dev/null +++ b/libbutl/optional.ixx @@ -0,0 +1,210 @@ +// file : libbutl/optional.ixx -*- C++ -*- +// copyright : Copyright (c) 2014-2018 Code Synthesis Ltd +// license : MIT; see accompanying LICENSE file + +namespace butl +{ + namespace details + { + // optional_data + // + + template + inline optional_data& optional_data:: + operator= (nullopt_t) + { + if (v_) + { + d_.~T (); + v_ = false; + } + + return *this; + } + + template + inline optional_data& optional_data:: + operator= (const T& v) + { + if (v_) + { + d_.~T (); + v_ = false; + } + + new (&d_) T (v); + v_ = true; + + return *this; + } + + template + inline optional_data& optional_data:: + operator= (T&& v) + { + if (v_) + { + d_.~T (); + v_ = false; + } + + new (&d_) T (std::move (v)); + v_ = true; + + return *this; + } + + template + inline optional_data:: + optional_data (const optional_data& o) + : v_ (o.v_) + { + if (v_) + new (&d_) T (o.d_); + } + + template + inline optional_data:: + optional_data (optional_data&& o) + : v_ (o.v_) + { + if (v_) + new (&d_) T (std::move (o.d_)); + } + + template + inline optional_data& optional_data:: + operator= (const optional_data& o) + { + if (v_) + { + d_.~T (); + v_ = false; + } + + if (o.v_) + { + new (&d_) T (o.d_); + v_ = true; + } + + return *this; + } + + template + inline optional_data& optional_data:: + operator= (optional_data&& o) + { + if (v_) + { + d_.~T (); + v_ = false; + } + + if (o.v_) + { + new (&d_) T (std::move (o.d_)); + v_ = true; + } + + return *this; + } + + template + inline optional_data:: + ~optional_data () + { + if (v_) + d_.~T (); + } + + // optional_data + // + + template + inline optional_data& optional_data:: + operator= (nullopt_t) + { + if (v_) + v_ = false; + + return *this; + } + + template + inline optional_data& optional_data:: + operator= (const T& v) + { + if (v_) + v_ = false; + + new (&d_) T (v); + v_ = true; + + return *this; + } + + template + inline optional_data& optional_data:: + operator= (T&& v) + { + if (v_) + v_ = false; + + new (&d_) T (std::move (v)); + v_ = true; + + return *this; + } + + template + inline optional_data:: + optional_data (const optional_data& o) + : v_ (o.v_) + { + if (v_) + new (&d_) T (o.d_); + } + + template + inline optional_data:: + optional_data (optional_data&& o) + : v_ (o.v_) + { + if (v_) + new (&d_) T (std::move (o.d_)); + } + + template + inline optional_data& optional_data:: + operator= (const optional_data& o) + { + if (v_) + v_ = false; + + if (o.v_) + { + new (&d_) T (o.d_); + v_ = true; + } + + return *this; + } + + template + inline optional_data& optional_data:: + operator= (optional_data&& o) + { + if (v_) + v_ = false; + + if (o.v_) + { + new (&d_) T (std::move (o.d_)); + v_ = true; + } + + return *this; + } + } +} diff --git a/libbutl/optional.mxx b/libbutl/optional.mxx index cddf59a..ae72399 100644 --- a/libbutl/optional.mxx +++ b/libbutl/optional.mxx @@ -9,8 +9,9 @@ // C includes. #ifndef __cpp_lib_modules -#include // move() -#include // hash +#include // move() +#include // hash +#include // is_trivially_destructible #endif // Other includes. @@ -34,55 +35,145 @@ LIBBUTL_MODEXPORT namespace butl #endif constexpr nullopt_t nullopt (1); + namespace details + { + template ::value> + struct optional_data; + + template + struct optional_data + { + struct empty {}; + + union + { + empty e_; + T d_; + }; + bool v_; + +#if !defined(_MSC_VER) || _MSC_VER > 1900 + constexpr optional_data (): e_ (), v_ (false) {} + constexpr optional_data (nullopt_t): e_ (), v_ (false) {} + constexpr optional_data (const T& v): d_ (v), v_ (true) {} + constexpr optional_data (T&& v): d_ (std::move (v)), v_ (true) {} +#else + optional_data (): e_ (), v_ (false) {} + optional_data (nullopt_t): e_ (), v_ (false) {} + optional_data (const T& v): d_ (v), v_ (true) {} + optional_data (T&& v): d_ (std::move (v)), v_ (true) {} +#endif + + optional_data& operator= (nullopt_t); + optional_data& operator= (const T&); + optional_data& operator= (T&&); + + optional_data (const optional_data&); + optional_data (optional_data&&); + + optional_data& operator= (const optional_data&); + optional_data& operator= (optional_data&&); + + ~optional_data (); + }; + + template + struct optional_data + { + struct empty {}; + + union + { + empty e_; + T d_; + }; + bool v_; + +#if !defined(_MSC_VER) || _MSC_VER > 1900 + constexpr optional_data (): e_ (), v_ (false) {} + constexpr optional_data (nullopt_t): e_ (), v_ (false) {} + constexpr optional_data (const T& v): d_ (v), v_ (true) {} + constexpr optional_data (T&& v): d_ (std::move (v)), v_ (true) {} +#else + optional_data (): e_ (), v_ (false) {} + optional_data (nullopt_t): e_ (), v_ (false) {} + optional_data (const T& v): d_ (v), v_ (true) {} + optional_data (T&& v): d_ (std::move (v)), v_ (true) {} +#endif + + optional_data& operator= (nullopt_t); + optional_data& operator= (const T&); + optional_data& operator= (T&&); + + optional_data (const optional_data&); + optional_data (optional_data&&); + + optional_data& operator= (const optional_data&); + optional_data& operator= (optional_data&&); + }; + } + template - class optional + class optional: private details::optional_data { + using base = details::optional_data; + public: - typedef T value_type; + using value_type = T; + +#if !defined(_MSC_VER) || _MSC_VER > 1900 + constexpr optional () {} + constexpr optional (nullopt_t) {} + constexpr optional (const T& v): base (v) {} + constexpr optional (T&& v): base (std::move (v)) {} +#else + optional () {} + optional (nullopt_t) {} + optional (const T& v): base (v) {} + optional (T&& v): base (std::move (v)) {} +#endif - constexpr optional (): value_ (), null_ (true) {} // VC14 needs value_(). - constexpr optional (nullopt_t): value_ (), null_ (true) {} - constexpr optional (const T& v): value_ (v), null_ (false) {} - constexpr optional (T&& v): value_ (std::move (v)), null_ (false) {} + optional& operator= (nullopt_t v) {static_cast (*this) = v; return *this;} + optional& operator= (const T& v) {static_cast (*this) = v; return *this;} + optional& operator= (T&& v) {static_cast (*this) = std::move (v); return *this;} - optional& operator= (nullopt_t) {value_ = T (); null_ = true; return *this;} - optional& operator= (const T& v) {value_ = v; null_ = false; return *this;} - optional& operator= (T&& v) {value_ = std::move (v); null_ = false; return *this;} + T& value () {return this->d_;} + const T& value () const {return this->d_;} - T& value () {return value_;} - const T& value () const {return value_;} + T* operator-> () {return &value ();} + const T* operator-> () const {return &value ();} - T* operator-> () {return &value_;} - const T* operator-> () const {return &value_;} + T& operator* () {return value ();} + const T& operator* () const {return value ();} - T& operator* () {return value_;} - const T& operator* () const {return value_;} + bool has_value () const {return this->v_;} + explicit operator bool () const {return this->v_;} - bool has_value () const {return !null_;} - explicit operator bool () const {return !null_;} + optional (const optional&) = default; + optional (optional&&) = default; - private: - T value_; - bool null_; + optional& operator= (const optional&) = default; + optional& operator= (optional&&) = default; }; template inline auto - operator== (const optional& x, const optional& y) -> decltype (*x == *y) + operator== (const optional& x, const optional& y) { - return static_cast (x) == static_cast (y) && (!x || *x == *y); + bool px (x), py (y); + return px == py && (!px || *x == *y); } template inline auto - operator!= (const optional& x, const optional& y) -> decltype (x == y) + operator!= (const optional& x, const optional& y) { return !(x == y); } template inline auto - operator< (const optional& x, const optional& y) -> decltype (*x < *y) + operator< (const optional& x, const optional& y) { bool px (x), py (y); return px < py || (px && py && *x < *y); @@ -90,7 +181,7 @@ LIBBUTL_MODEXPORT namespace butl template inline auto - operator> (const optional& x, const optional& y) -> decltype (*x > *y) + operator> (const optional& x, const optional& y) { return y < x; } @@ -111,3 +202,5 @@ namespace std } }; } + +#include diff --git a/libbutl/timestamp.cxx b/libbutl/timestamp.cxx index 437ea52..b0bb9ad 100644 --- a/libbutl/timestamp.cxx +++ b/libbutl/timestamp.cxx @@ -89,33 +89,36 @@ using namespace std; // of the std::tm argument. // #ifdef __GLIBCXX__ -namespace details +namespace butl { - struct put_time_data + namespace details { - const std::tm* tm; - const char* fmt; - }; + struct put_time_data + { + const std::tm* tm; + const char* fmt; + }; - inline put_time_data - put_time (const std::tm* tm, const char* fmt) - { - return put_time_data {tm, fmt}; - } + inline put_time_data + put_time (const std::tm* tm, const char* fmt) + { + return put_time_data {tm, fmt}; + } - inline ostream& - operator<< (ostream& os, const put_time_data& d) - { - char buf[256]; - if (strftime (buf, sizeof (buf), d.fmt, d.tm) != 0) - os << buf; - else - os.setstate (ostream::badbit); - return os; + inline ostream& + operator<< (ostream& os, const put_time_data& d) + { + char buf[256]; + if (strftime (buf, sizeof (buf), d.fmt, d.tm) != 0) + os << buf; + else + os.setstate (ostream::badbit); + return os; + } } } -using namespace details; +using namespace butl::details; #endif // Thread-safe implementations of gmtime() and localtime(). @@ -133,36 +136,39 @@ using namespace details; // one common tm structure per thread for the conversion", which mean that they // are thread-safe. // -namespace details +namespace butl { - static tm* - gmtime (const time_t* t, tm* r) + namespace details { + static tm* + gmtime (const time_t* t, tm* r) + { #ifdef _WIN32 - const tm* gt (::gmtime (t)); - if (gt == nullptr) - return nullptr; + const tm* gt (::gmtime (t)); + if (gt == nullptr) + return nullptr; - *r = *gt; - return r; + *r = *gt; + return r; #else - return gmtime_r (t, r); + return gmtime_r (t, r); #endif - } + } - static tm* - localtime (const time_t* t, tm* r) - { + static tm* + localtime (const time_t* t, tm* r) + { #ifdef _WIN32 - const tm* lt (::localtime (t)); - if (lt == nullptr) - return nullptr; + const tm* lt (::localtime (t)); + if (lt == nullptr) + return nullptr; - *r = *lt; - return r; + *r = *lt; + return r; #else - return localtime_r (t, r); + return localtime_r (t, r); #endif + } } } @@ -219,7 +225,7 @@ timegm (tm* ctm) // offset is effectively the time difference between MSK and GMT time zones. // tm gtm; - if (details::gmtime (&t, >m) == nullptr) + if (butl::details::gmtime (&t, >m) == nullptr) return e; // gmtime() being called for the timepoint t returns 6 AM. So now we have -- cgit v1.1