// // Cforall Version 1.0.0 Copyright (C) 2015 University of Waterloo // // The contents of this file are covered under the licence agreement in the // file "LICENCE" distributed with Cforall. // // option.h -- // // Author : Aaron B. Moss // Created On : Wed Jul 03 15:41:00 2018 // Last Modified By : Aaron B. Moss // Last Modified On : Wed Jul 03 15:41:00 2018 // Update Count : 1 // #pragma once #include #include #include #include using std::move; /// A class that may or may not hold a value of type T /// Similar interface to the C++17 std::optional template class option { typename std::aligned_storage::type storage; bool filled; /// gets the underlying value (filled must be true) T& get() { return reinterpret_cast(storage); } const T& get() const { return reinterpret_cast(storage); } /// copy-initializes from a value void init( const T& x ) { new(&storage) T(x); filled = true; } /// move-initializes from a value void init( T&& x ) { new(&storage) T( move(x) ); filled = true; } /// destroys the underlying value (must be filled), sets filled to false void destroy() { get().~T(); filled = false; } public: option() : filled(false) {} option( const T& x ) { init( x ); } option( T&& x ) { init( move(x) ); } option( const option& o ) : filled(o.filled) { if ( filled ) init( o.get() ); } option( option&& o ) : filled(o.filled) { if ( filled ) init( move(o.get()) ); } option& operator= ( const T& x ) { if ( filled ) { get() = x; } else { init( x ); } return *this; } option& operator= ( T&& x ) { if ( filled ) { get() = move(x); } else { init( move(x) ); } return *this; } option& operator= ( const option& o ) { if ( filled ) { if ( o.filled ) { get() = o.get(); } else { destroy(); } } else if ( o.filled ) { init( o.get() ); } return *this; } option& operator= ( option&& o ) { if ( filled ) { if ( o.filled ) { get() = move(o.get()); } else { destroy(); } } else if ( o.filled ) { init( move(o.get()) ); } return *this; } ~option() { if ( filled ) get().~T(); } void swap( option& o ) { if ( filled ) { if ( o.filled ) { std::swap( get(), o.get() ); } else { o.init( move(get()) ); destroy(); } } else if ( o.filled ) { init( move(o.get()) ); o.destroy(); } } /// Get contained value (unchecked) T* operator->() { return &get(); } const T* operator->() const { return &get(); } T& operator*() & { return get(); } const T& operator*() const& { return get(); } T&& operator*() && { return move(get()); } const T&& operator*() const&& { return move(get()); } /// Check if option is filled constexpr explicit operator bool() const { return filled; } constexpr bool has_value() const { return filled; } /// Get contained value (checked) T& value() & { assertf(filled, "checked get failed"); return get(); } const T& value() const& { assertf(filled, "checked get failed"); return get(); } T&& value() && { assertf(filled, "checked get failed"); return move(get()); } const T&& value() const&& { assertf(filled, "checked get failed"); return move(get()); } /// Get contained or default value template T value_or( U&& y ) const& { return filled ? get() : static_cast(std::forward(y)); } template T value_or( U&& y ) && { return filled ? move(get()) : static_cast(std::forward(y)); } /// Unset value if set void reset() { if ( filled ) destroy(); } /// Construct value in-place (destroys existing value if needed) template void emplace( Args&&... args ) { if ( filled ) { destroy(); } new(&storage) T{ std::forward(args)... }; filled = true; } }; template bool operator== ( const option& a, const option& b ) { return a ? (b && *a == *b) : !b; } template bool operator!= ( const option& a, const option& b ) { return !(a == b); } template bool operator< ( const option& a, const option& b ) { return b && (!a || *a < *b); } template bool operator<= ( const option& a, const option& b ) { return !a || (b && *a <= *b); } template bool operator> ( const option& a, const option& b ) { return b < a; } template bool operator>= ( const option& a, const option& b ) { return b <= a; } template bool operator== ( const option& a, const T& x ) { return a && *a == x; } template bool operator!= ( const option& a, const T& x ) { return !(a == x); } template bool operator< ( const option& a, const T& x ) { return !a || *a < x; } template bool operator<= ( const option& a, const T& x ) { return !a || *a <= x; } template bool operator> ( const option& a, const T& x ) { return a && *a > x; } template bool operator>= ( const option& a, const T& x ) { return a && *a >= x; } template bool operator== ( const T& x, const option& b ) { return b == x; } template bool operator!= ( const T& x, const option& b ) { return !(b == x); } template bool operator< ( const T& x, const option& b ) { return b > x; } template bool operator<= ( const T& x, const option& b ) { return b >= x; } template bool operator> ( const T& x, const option& b ) { return b < x; } template bool operator>= ( const T& x, const option& b ) { return b <= x; } template option make_option( Args&&... args ) { option o; o.emplace( std::forward(args)... ); return o; } namespace std { template void swap( option& a, option& b ) { a.swap(b); } template struct hash< option > { size_t operator() ( const option& a ) { return a ? hash{}( *a ) : size_t(1); } }; }