diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index e331edd655..e75ca4afa4 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -31,6 +31,8 @@ sigslot wpiutil/src/main/native/include/wpi/Signal.h wpiutil/src/test/native/cpp/sigslot/ tcpsockets wpiutil/src/main/native/cpp/TCP{Stream,Connector,Acceptor}.cpp wpiutil/src/main/native/include/wpi/TCP*.h +Optional wpiutil/src/main/native/include/wpi/optional.h + wpiutil/src/test/native/cpp/test_optional.cpp ============================================================================== @@ -212,3 +214,33 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. + + +============================================================================== +Optional License +============================================================================== +Copyright (C) 2011 - 2017 Andrzej Krzemienski. + +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/wpiutil/.styleguide b/wpiutil/.styleguide index 150527487f..04d87906f3 100644 --- a/wpiutil/.styleguide +++ b/wpiutil/.styleguide @@ -26,8 +26,6 @@ generatedFileExclude { src/main/native/include/wpi/MapVector\.h$ src/main/native/include/wpi/MathExtras\.h$ src/main/native/include/wpi/NativeFormatting\.h$ - src/main/native/include/wpi/None\.h$ - src/main/native/include/wpi/LLVMOptional\.h$ src/main/native/include/wpi/Path\.h$ src/main/native/include/wpi/PointerLikeTypeTraits\.h$ src/main/native/include/wpi/STLExtras\.h$ @@ -53,6 +51,8 @@ generatedFileExclude { src/main/native/include/uv\.h$ src/main/native/include/uv/ src/main/native/libuv/ + src/main/native/include/wpi/optional\.h$ + src/test/native/cpp/test_optional\.cpp$ } licenseUpdateExclude { diff --git a/wpiutil/src/main/native/cpp/llvm/NativeFormatting.cpp b/wpiutil/src/main/native/cpp/llvm/NativeFormatting.cpp index 075f31e90f..41c43e2435 100644 --- a/wpiutil/src/main/native/cpp/llvm/NativeFormatting.cpp +++ b/wpiutil/src/main/native/cpp/llvm/NativeFormatting.cpp @@ -132,10 +132,10 @@ void wpi::write_integer(raw_ostream &S, long long N, size_t MinDigits, } void wpi::write_hex(raw_ostream &S, uint64_t N, HexPrintStyle Style, - Optional Width) { + optional Width) { const size_t kMaxWidth = 128u; - size_t W = std::min(kMaxWidth, Width.getValueOr(0u)); + size_t W = std::min(kMaxWidth, Width.value_or(0u)); unsigned Nibbles = (64 - countLeadingZeros(N) + 3) / 4; bool Prefix = (Style == HexPrintStyle::PrefixLower || @@ -162,8 +162,8 @@ void wpi::write_hex(raw_ostream &S, uint64_t N, HexPrintStyle Style, } void wpi::write_double(raw_ostream &S, double N, FloatStyle Style, - Optional Precision) { - size_t Prec = Precision.getValueOr(getDefaultPrecision(Style)); + optional Precision) { + size_t Prec = Precision.value_or(getDefaultPrecision(Style)); if (std::isnan(N)) { S << "nan"; diff --git a/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp b/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp index 97cd83d889..04bae65306 100644 --- a/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp +++ b/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp @@ -372,7 +372,7 @@ raw_ostream &raw_ostream::operator<<(const FormattedBytes &FB) { const size_t Size = Bytes.size(); HexPrintStyle HPS = FB.Upper ? HexPrintStyle::Upper : HexPrintStyle::Lower; uint64_t OffsetWidth = 0; - if (FB.FirstByteOffset.hasValue()) { + if (FB.FirstByteOffset.has_value()) { // Figure out how many nibbles are needed to print the largest offset // represented by this data set, so that we can align the offset field // to the right width. @@ -392,8 +392,8 @@ raw_ostream &raw_ostream::operator<<(const FormattedBytes &FB) { while (!Bytes.empty()) { indent(FB.IndentLevel); - if (FB.FirstByteOffset.hasValue()) { - uint64_t Offset = FB.FirstByteOffset.getValue(); + if (FB.FirstByteOffset.has_value()) { + uint64_t Offset = FB.FirstByteOffset.value(); wpi::write_hex(*this, Offset + LineIndex, HPS, OffsetWidth); *this << ": "; } diff --git a/wpiutil/src/main/native/include/llvm/None.h b/wpiutil/src/main/native/include/llvm/None.h deleted file mode 100644 index 5f4c1229da..0000000000 --- a/wpiutil/src/main/native/include/llvm/None.h +++ /dev/null @@ -1,20 +0,0 @@ -/*----------------------------------------------------------------------------*/ -/* Copyright (c) 2016-2018 FIRST. All Rights Reserved. */ -/* Open Source Software - may be modified and shared by FRC teams. The code */ -/* must be accompanied by the FIRST BSD license file in the root directory of */ -/* the project. */ -/*----------------------------------------------------------------------------*/ - -#pragma once - -// clang-format off -#ifdef _MSC_VER -#pragma message "warning: llvm/None.h is deprecated; include wpi/None.h instead" -#else -#warning "llvm/None.h is deprecated; include wpi/None.h instead" -#endif -// clang-format on - -#include "wpi/None.h" - -namespace llvm = wpi; diff --git a/wpiutil/src/main/native/include/wpi/ArrayRef.h b/wpiutil/src/main/native/include/wpi/ArrayRef.h index 1a84311e0c..08f413db23 100644 --- a/wpiutil/src/main/native/include/wpi/ArrayRef.h +++ b/wpiutil/src/main/native/include/wpi/ArrayRef.h @@ -11,7 +11,7 @@ #define WPIUTIL_WPI_ARRAYREF_H #include "wpi/Hashing.h" -#include "wpi/None.h" +#include "wpi/optional.h" #include "wpi/SmallVector.h" #include "wpi/STLExtras.h" #include "wpi/Compiler.h" @@ -60,8 +60,8 @@ namespace wpi { /// Construct an empty ArrayRef. /*implicit*/ ArrayRef() = default; - /// Construct an empty ArrayRef from None. - /*implicit*/ ArrayRef(NoneType) {} + /// Construct an empty ArrayRef from nullopt. + /*implicit*/ ArrayRef(nullopt_t) {} /// Construct an ArrayRef from a single element. /*implicit*/ ArrayRef(const T &OneElt) @@ -296,8 +296,8 @@ namespace wpi { /// Construct an empty MutableArrayRef. /*implicit*/ MutableArrayRef() = default; - /// Construct an empty MutableArrayRef from None. - /*implicit*/ MutableArrayRef(NoneType) : ArrayRef() {} + /// Construct an empty MutableArrayRef from nullopt. + /*implicit*/ MutableArrayRef(nullopt_t) : ArrayRef() {} /// Construct an MutableArrayRef from a single element. /*implicit*/ MutableArrayRef(T &OneElt) : ArrayRef(OneElt) {} diff --git a/wpiutil/src/main/native/include/wpi/Format.h b/wpiutil/src/main/native/include/wpi/Format.h index a96a709d12..34dd8d88e4 100644 --- a/wpiutil/src/main/native/include/wpi/Format.h +++ b/wpiutil/src/main/native/include/wpi/Format.h @@ -222,8 +222,8 @@ inline FormattedNumber format_decimal(int64_t N, unsigned Width) { class FormattedBytes { ArrayRef Bytes; - // If not None, display offsets for each line relative to starting value. - Optional FirstByteOffset; + // If not nullopt, display offsets for each line relative to starting value. + optional FirstByteOffset; uint32_t IndentLevel; // Number of characters to indent each line. uint32_t NumPerLine; // Number of bytes to show per line. uint8_t ByteGroupSize; // How many hex bytes are grouped without spaces @@ -232,7 +232,7 @@ class FormattedBytes { friend class raw_ostream; public: - FormattedBytes(ArrayRef B, uint32_t IL, Optional O, + FormattedBytes(ArrayRef B, uint32_t IL, optional O, uint32_t NPL, uint8_t BGS, bool U, bool A) : Bytes(B), FirstByteOffset(O), IndentLevel(IL), NumPerLine(NPL), ByteGroupSize(BGS), Upper(U), ASCII(A) { @@ -243,7 +243,7 @@ public: }; inline FormattedBytes -format_bytes(ArrayRef Bytes, Optional FirstByteOffset = None, +format_bytes(ArrayRef Bytes, optional FirstByteOffset = nullopt, uint32_t NumPerLine = 16, uint8_t ByteGroupSize = 4, uint32_t IndentLevel = 0, bool Upper = false) { return FormattedBytes(Bytes, IndentLevel, FirstByteOffset, NumPerLine, @@ -252,7 +252,7 @@ format_bytes(ArrayRef Bytes, Optional FirstByteOffset = None, inline FormattedBytes format_bytes_with_ascii(ArrayRef Bytes, - Optional FirstByteOffset = None, + optional FirstByteOffset = nullopt, uint32_t NumPerLine = 16, uint8_t ByteGroupSize = 4, uint32_t IndentLevel = 0, bool Upper = false) { return FormattedBytes(Bytes, IndentLevel, FirstByteOffset, NumPerLine, diff --git a/wpiutil/src/main/native/include/wpi/LLVMOptional.h b/wpiutil/src/main/native/include/wpi/LLVMOptional.h deleted file mode 100644 index 30c8bbdace..0000000000 --- a/wpiutil/src/main/native/include/wpi/LLVMOptional.h +++ /dev/null @@ -1,346 +0,0 @@ -//===- Optional.h - Simple variant for passing optional values --*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file provides Optional, a template class modeled in the spirit of -// OCaml's 'opt' variant. The idea is to strongly type whether or not -// a value can be optional. -// -//===----------------------------------------------------------------------===// - -#ifndef WPIUTIL_WPI_OPTIONAL_H -#define WPIUTIL_WPI_OPTIONAL_H - -#include "wpi/None.h" -#include "wpi/AlignOf.h" -#include "wpi/Compiler.h" -#include "wpi/type_traits.h" -#include -#include -#include -#include - -namespace wpi { - -namespace optional_detail { -/// Storage for any type. -template struct OptionalStorage { - AlignedCharArrayUnion storage; - bool hasVal = false; - - OptionalStorage() = default; - - OptionalStorage(const T &y) : hasVal(true) { new (storage.buffer) T(y); } - OptionalStorage(const OptionalStorage &O) : hasVal(O.hasVal) { - if (hasVal) - new (storage.buffer) T(*O.getPointer()); - } - OptionalStorage(T &&y) : hasVal(true) { - new (storage.buffer) T(std::forward(y)); - } - OptionalStorage(OptionalStorage &&O) : hasVal(O.hasVal) { - if (O.hasVal) { - new (storage.buffer) T(std::move(*O.getPointer())); - } - } - - OptionalStorage &operator=(T &&y) { - if (hasVal) - *getPointer() = std::move(y); - else { - new (storage.buffer) T(std::move(y)); - hasVal = true; - } - return *this; - } - OptionalStorage &operator=(OptionalStorage &&O) { - if (!O.hasVal) - reset(); - else { - *this = std::move(*O.getPointer()); - } - return *this; - } - - // FIXME: these assignments (& the equivalent const T&/const Optional& ctors) - // could be made more efficient by passing by value, possibly unifying them - // with the rvalue versions above - but this could place a different set of - // requirements (notably: the existence of a default ctor) when implemented - // in that way. Careful SFINAE to avoid such pitfalls would be required. - OptionalStorage &operator=(const T &y) { - if (hasVal) - *getPointer() = y; - else { - new (storage.buffer) T(y); - hasVal = true; - } - return *this; - } - OptionalStorage &operator=(const OptionalStorage &O) { - if (!O.hasVal) - reset(); - else - *this = *O.getPointer(); - return *this; - } - - ~OptionalStorage() { reset(); } - - void reset() { - if (hasVal) { - (*getPointer()).~T(); - hasVal = false; - } - } - - T *getPointer() { - assert(hasVal); - return reinterpret_cast(storage.buffer); - } - const T *getPointer() const { - assert(hasVal); - return reinterpret_cast(storage.buffer); - } -}; - -#if !defined(__GNUC__) || defined(__clang__) // GCC up to GCC7 miscompiles this. -/// Storage for trivially copyable types only. -template struct OptionalStorage { - AlignedCharArrayUnion storage; - bool hasVal = false; - - OptionalStorage() = default; - - OptionalStorage(const T &y) : hasVal(true) { new (storage.buffer) T(y); } - OptionalStorage &operator=(const T &y) { - *reinterpret_cast(storage.buffer) = y; - hasVal = true; - return *this; - } - - void reset() { hasVal = false; } -}; -#endif -} // namespace optional_detail - -template class Optional { - optional_detail::OptionalStorage::value> Storage; - -public: - using value_type = T; - - constexpr Optional() {} - constexpr Optional(NoneType) {} - - Optional(const T &y) : Storage(y) {} - Optional(const Optional &O) = default; - - Optional(T &&y) : Storage(std::forward(y)) {} - Optional(Optional &&O) = default; - - Optional &operator=(T &&y) { - Storage = std::move(y); - return *this; - } - Optional &operator=(Optional &&O) = default; - - /// Create a new object by constructing it in place with the given arguments. - template void emplace(ArgTypes &&... Args) { - reset(); - Storage.hasVal = true; - new (getPointer()) T(std::forward(Args)...); - } - - static inline Optional create(const T *y) { - return y ? Optional(*y) : Optional(); - } - - Optional &operator=(const T &y) { - Storage = y; - return *this; - } - Optional &operator=(const Optional &O) = default; - - void reset() { Storage.reset(); } - - const T *getPointer() const { - assert(Storage.hasVal); - return reinterpret_cast(Storage.storage.buffer); - } - T *getPointer() { - assert(Storage.hasVal); - return reinterpret_cast(Storage.storage.buffer); - } - const T &getValue() const LLVM_LVALUE_FUNCTION { return *getPointer(); } - T &getValue() LLVM_LVALUE_FUNCTION { return *getPointer(); } - - explicit operator bool() const { return Storage.hasVal; } - bool hasValue() const { return Storage.hasVal; } - const T *operator->() const { return getPointer(); } - T *operator->() { return getPointer(); } - const T &operator*() const LLVM_LVALUE_FUNCTION { return *getPointer(); } - T &operator*() LLVM_LVALUE_FUNCTION { return *getPointer(); } - - template - constexpr T getValueOr(U &&value) const LLVM_LVALUE_FUNCTION { - return hasValue() ? getValue() : std::forward(value); - } - -#if LLVM_HAS_RVALUE_REFERENCE_THIS - T &&getValue() && { return std::move(*getPointer()); } - T &&operator*() && { return std::move(*getPointer()); } - - template - T getValueOr(U &&value) && { - return hasValue() ? std::move(getValue()) : std::forward(value); - } -#endif -}; - -template struct isPodLike> { - // An Optional is pod-like if T is. - static const bool value = isPodLike::value; -}; - -template -bool operator==(const Optional &X, const Optional &Y) { - if (X && Y) - return *X == *Y; - return X.hasValue() == Y.hasValue(); -} - -template -bool operator!=(const Optional &X, const Optional &Y) { - return !(X == Y); -} - -template -bool operator<(const Optional &X, const Optional &Y) { - if (X && Y) - return *X < *Y; - return X.hasValue() < Y.hasValue(); -} - -template -bool operator<=(const Optional &X, const Optional &Y) { - return !(Y < X); -} - -template -bool operator>(const Optional &X, const Optional &Y) { - return Y < X; -} - -template -bool operator>=(const Optional &X, const Optional &Y) { - return !(X < Y); -} - -template -bool operator==(const Optional &X, NoneType) { - return !X; -} - -template -bool operator==(NoneType, const Optional &X) { - return X == None; -} - -template -bool operator!=(const Optional &X, NoneType) { - return !(X == None); -} - -template -bool operator!=(NoneType, const Optional &X) { - return X != None; -} - -template bool operator<(const Optional &X, NoneType) { - return false; -} - -template bool operator<(NoneType, const Optional &X) { - return X.hasValue(); -} - -template bool operator<=(const Optional &X, NoneType) { - return !(None < X); -} - -template bool operator<=(NoneType, const Optional &X) { - return !(X < None); -} - -template bool operator>(const Optional &X, NoneType) { - return None < X; -} - -template bool operator>(NoneType, const Optional &X) { - return X < None; -} - -template bool operator>=(const Optional &X, NoneType) { - return None <= X; -} - -template bool operator>=(NoneType, const Optional &X) { - return X <= None; -} - -template bool operator==(const Optional &X, const T &Y) { - return X && *X == Y; -} - -template bool operator==(const T &X, const Optional &Y) { - return Y && X == *Y; -} - -template bool operator!=(const Optional &X, const T &Y) { - return !(X == Y); -} - -template bool operator!=(const T &X, const Optional &Y) { - return !(X == Y); -} - -template bool operator<(const Optional &X, const T &Y) { - return !X || *X < Y; -} - -template bool operator<(const T &X, const Optional &Y) { - return Y && X < *Y; -} - -template bool operator<=(const Optional &X, const T &Y) { - return !(Y < X); -} - -template bool operator<=(const T &X, const Optional &Y) { - return !(Y < X); -} - -template bool operator>(const Optional &X, const T &Y) { - return Y < X; -} - -template bool operator>(const T &X, const Optional &Y) { - return Y < X; -} - -template bool operator>=(const Optional &X, const T &Y) { - return !(X < Y); -} - -template bool operator>=(const T &X, const Optional &Y) { - return !(X < Y); -} - -} // end wpi namespace - -#endif diff --git a/wpiutil/src/main/native/include/wpi/NativeFormatting.h b/wpiutil/src/main/native/include/wpi/NativeFormatting.h index baeb724ca5..3407e75fa7 100644 --- a/wpiutil/src/main/native/include/wpi/NativeFormatting.h +++ b/wpiutil/src/main/native/include/wpi/NativeFormatting.h @@ -10,7 +10,7 @@ #ifndef WPIUTIL_WPI_NATIVE_FORMATTING_H #define WPIUTIL_WPI_NATIVE_FORMATTING_H -#include "wpi/LLVMOptional.h" +#include "wpi/optional.h" #include "wpi/raw_ostream.h" #include @@ -40,9 +40,9 @@ void write_integer(raw_ostream &S, long long N, size_t MinDigits, IntegerStyle Style); void write_hex(raw_ostream &S, uint64_t N, HexPrintStyle Style, - Optional Width = None); + optional Width = nullopt); void write_double(raw_ostream &S, double D, FloatStyle Style, - Optional Precision = None); + optional Precision = nullopt); } #endif diff --git a/wpiutil/src/main/native/include/wpi/None.h b/wpiutil/src/main/native/include/wpi/None.h deleted file mode 100644 index 9cefac9f29..0000000000 --- a/wpiutil/src/main/native/include/wpi/None.h +++ /dev/null @@ -1,27 +0,0 @@ -//===-- None.h - Simple null value for implicit construction ------*- C++ -*-=// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file provides None, an enumerator for use in implicit constructors -// of various (usually templated) types to make such construction more -// terse. -// -//===----------------------------------------------------------------------===// - -#ifndef WPIUTIL_WPI_NONE_H -#define WPIUTIL_WPI_NONE_H - -namespace wpi { -/// A simple null object to allow implicit construction of Optional -/// and similar types without having to spell out the specialization's name. -// (constant value 1 in an attempt to workaround MSVC build issue... ) -enum class NoneType { None = 1 }; -const NoneType None = NoneType::None; -} - -#endif diff --git a/wpiutil/src/main/native/include/wpi/STLExtras.h b/wpiutil/src/main/native/include/wpi/STLExtras.h index 5fc6d32a03..b020222f6e 100644 --- a/wpiutil/src/main/native/include/wpi/STLExtras.h +++ b/wpiutil/src/main/native/include/wpi/STLExtras.h @@ -17,10 +17,10 @@ #ifndef WPIUTIL_WPI_STLEXTRAS_H #define WPIUTIL_WPI_STLEXTRAS_H -#include "wpi/LLVMOptional.h" #include "wpi/SmallVector.h" #include "wpi/iterator.h" #include "wpi/iterator_range.h" +#include "wpi/optional.h" #include #include #include diff --git a/wpiutil/src/main/native/include/wpi/SmallSet.h b/wpiutil/src/main/native/include/wpi/SmallSet.h index 35657899c8..87fd287be3 100644 --- a/wpiutil/src/main/native/include/wpi/SmallSet.h +++ b/wpiutil/src/main/native/include/wpi/SmallSet.h @@ -14,7 +14,7 @@ #ifndef WPIUTIL_WPI_SMALLSET_H #define WPIUTIL_WPI_SMALLSET_H -#include "wpi/None.h" +#include "wpi/optional.h" #include "wpi/SmallPtrSet.h" #include "wpi/SmallVector.h" #include "wpi/Compiler.h" @@ -78,16 +78,16 @@ public: /// concept. // FIXME: Add iterators that abstract over the small and large form, and then // return those here. - std::pair insert(const T &V) { + std::pair insert(const T &V) { if (!isSmall()) - return std::make_pair(None, Set.insert(V).second); + return std::make_pair(nullopt, Set.insert(V).second); VIterator I = vfind(V); if (I != Vector.end()) // Don't reinsert if it already exists. - return std::make_pair(None, false); + return std::make_pair(nullopt, false); if (Vector.size() < N) { Vector.push_back(V); - return std::make_pair(None, true); + return std::make_pair(nullopt, true); } // Otherwise, grow from vector to set. @@ -96,7 +96,7 @@ public: Vector.pop_back(); } Set.insert(V); - return std::make_pair(None, true); + return std::make_pair(nullopt, true); } template diff --git a/wpiutil/src/main/native/include/wpi/optional.h b/wpiutil/src/main/native/include/wpi/optional.h new file mode 100644 index 0000000000..ed14b45961 --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/optional.h @@ -0,0 +1,913 @@ +// Copyright (C) 2011 - 2012 Andrzej Krzemienski. +// +// Use, modification, and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// The idea and interface is based on Boost.Optional library +// authored by Fernando Luis Cacciola Carballal + +# ifndef WPIUTIL_WPI_OPTIONAL_H +# define WPIUTIL_WPI_OPTIONAL_H + +# include +# include +# include +# include +# include +# include +# include + +# define TR2_OPTIONAL_REQUIRES(...) typename std::enable_if<__VA_ARGS__::value, bool>::type = false + +# if defined __GNUC__ || (defined _MSC_VER) && (_MSC_VER >= 1910) +# define OPTIONAL_HAS_CONSTEXPR_INIT_LIST 1 +# define OPTIONAL_CONSTEXPR_INIT_LIST constexpr +# else +# define OPTIONAL_HAS_CONSTEXPR_INIT_LIST 0 +# define OPTIONAL_CONSTEXPR_INIT_LIST +# endif + +# if defined __clang__ && (defined __cplusplus) && (__cplusplus != 201103L) +# define OPTIONAL_HAS_MOVE_ACCESSORS 1 +# else +# define OPTIONAL_HAS_MOVE_ACCESSORS 0 +# endif + +namespace wpi{ + +// 20.5.4, optional for object types +template class optional; + +// 20.5.5, optional for lvalue reference types +template class optional; + + +// workaround: std utility functions aren't constexpr yet +template inline constexpr T&& constexpr_forward(typename std::remove_reference::type& t) noexcept +{ + return static_cast(t); +} + +template inline constexpr T&& constexpr_forward(typename std::remove_reference::type&& t) noexcept +{ + static_assert(!std::is_lvalue_reference::value, "!!"); + return static_cast(t); +} + +template inline constexpr typename std::remove_reference::type&& constexpr_move(T&& t) noexcept +{ + return static_cast::type&&>(t); +} + + +#if defined NDEBUG +# define TR2_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) (EXPR) +#else +# define TR2_OPTIONAL_ASSERTED_EXPRESSION(CHECK, EXPR) ((CHECK) ? (EXPR) : ([]{assert(!#CHECK);}(), (EXPR))) +#endif + + +namespace detail_ +{ + +// static_addressof: a constexpr version of addressof +template +struct has_overloaded_addressof +{ + template + constexpr static bool has_overload(...) { return false; } + + template ().operator&()) > + constexpr static bool has_overload(bool) { return true; } + + constexpr static bool value = has_overload(true); +}; + +template )> +constexpr T* static_addressof(T& ref) +{ + return &ref; +} + +template )> +T* static_addressof(T& ref) +{ + return std::addressof(ref); +} + + +// the call to convert(b) has return type A and converts b to type A iff b decltype(b) is implicitly convertible to A +template +constexpr U convert(U v) { return v; } + + +namespace swap_ns +{ + using std::swap; + + template + void adl_swap(T& t, T& u) noexcept(noexcept(swap(t, u))) + { + swap(t, u); + } + +} // namespace swap_ns + +} // namespace detail + + +constexpr struct trivial_init_t{} trivial_init{}; + + +// 20.5.6, In-place construction +constexpr struct in_place_t{} in_place{}; + + +// 20.5.7, Disengaged state indicator +struct nullopt_t +{ + struct init{}; + constexpr explicit nullopt_t(init){} +}; +constexpr nullopt_t nullopt{nullopt_t::init()}; + + +// 20.5.8, class bad_optional_access +class bad_optional_access : public std::logic_error { +public: + explicit bad_optional_access(const std::string& what_arg) : logic_error{what_arg} {} + explicit bad_optional_access(const char* what_arg) : logic_error{what_arg} {} +}; + + +template +union storage_t +{ + unsigned char dummy_; + T value_; + + constexpr storage_t( trivial_init_t ) noexcept : dummy_() {}; + + template + constexpr storage_t( Args&&... args ) : value_(constexpr_forward(args)...) {} + + ~storage_t(){} +}; + + +template +union constexpr_storage_t +{ + unsigned char dummy_; + T value_; + + constexpr constexpr_storage_t( trivial_init_t ) noexcept : dummy_() {}; + + template + constexpr constexpr_storage_t( Args&&... args ) : value_(constexpr_forward(args)...) {} + + ~constexpr_storage_t() = default; +}; + + +template +struct optional_base +{ + bool init_; + storage_t storage_; + + constexpr optional_base() noexcept : init_(false), storage_(trivial_init) {}; + + explicit constexpr optional_base(const T& v) : init_(true), storage_(v) {} + + explicit constexpr optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {} + + template explicit optional_base(in_place_t, Args&&... args) + : init_(true), storage_(constexpr_forward(args)...) {} + + template >)> + explicit optional_base(in_place_t, std::initializer_list il, Args&&... args) + : init_(true), storage_(il, std::forward(args)...) {} + + ~optional_base() { if (init_) storage_.value_.T::~T(); } +}; + + +template +struct constexpr_optional_base +{ + bool init_; + constexpr_storage_t storage_; + + constexpr constexpr_optional_base() noexcept : init_(false), storage_(trivial_init) {}; + + explicit constexpr constexpr_optional_base(const T& v) : init_(true), storage_(v) {} + + explicit constexpr constexpr_optional_base(T&& v) : init_(true), storage_(constexpr_move(v)) {} + + template explicit constexpr constexpr_optional_base(in_place_t, Args&&... args) + : init_(true), storage_(constexpr_forward(args)...) {} + + template >)> + OPTIONAL_CONSTEXPR_INIT_LIST explicit constexpr_optional_base(in_place_t, std::initializer_list il, Args&&... args) + : init_(true), storage_(il, std::forward(args)...) {} + + ~constexpr_optional_base() = default; +}; + +template +using OptionalBase = typename std::conditional< + std::is_trivially_destructible::value, // if possible + constexpr_optional_base::type>, // use base with trivial destructor + optional_base::type> +>::type; + + + +template +class optional : private OptionalBase +{ + static_assert( !std::is_same::type, nullopt_t>::value, "bad T" ); + static_assert( !std::is_same::type, in_place_t>::value, "bad T" ); + + + constexpr bool initialized() const noexcept { return OptionalBase::init_; } + typename std::remove_const::type* dataptr() { return std::addressof(OptionalBase::storage_.value_); } + constexpr const T* dataptr() const { return detail_::static_addressof(OptionalBase::storage_.value_); } + + constexpr const T& contained_val() const& { return OptionalBase::storage_.value_; } +# if OPTIONAL_HAS_MOVE_ACCESSORS == 1 + constexpr T&& contained_val() && { return std::move(OptionalBase::storage_.value_); } + constexpr T& contained_val() & { return OptionalBase::storage_.value_; } +# else + T& contained_val() & { return OptionalBase::storage_.value_; } + T&& contained_val() && { return std::move(OptionalBase::storage_.value_); } +# endif + + void clear() noexcept { + if (initialized()) dataptr()->T::~T(); + OptionalBase::init_ = false; + } + + template + void initialize(Args&&... args) noexcept(noexcept(T(std::forward(args)...))) + { + assert(!OptionalBase::init_); + ::new (static_cast(dataptr())) T(std::forward(args)...); + OptionalBase::init_ = true; + } + + template + void initialize(std::initializer_list il, Args&&... args) noexcept(noexcept(T(il, std::forward(args)...))) + { + assert(!OptionalBase::init_); + ::new (static_cast(dataptr())) T(il, std::forward(args)...); + OptionalBase::init_ = true; + } + +public: + typedef T value_type; + + // 20.5.5.1, constructors + constexpr optional() noexcept : OptionalBase() {}; + constexpr optional(nullopt_t) noexcept : OptionalBase() {}; + + optional(const optional& rhs) + : OptionalBase() + { + if (rhs.initialized()) { + ::new (static_cast(dataptr())) T(*rhs); + OptionalBase::init_ = true; + } + } + + optional(optional&& rhs) noexcept(std::is_nothrow_move_constructible::value) + : OptionalBase() + { + if (rhs.initialized()) { + ::new (static_cast(dataptr())) T(std::move(*rhs)); + OptionalBase::init_ = true; + } + } + + constexpr optional(const T& v) : OptionalBase(v) {} + + constexpr optional(T&& v) : OptionalBase(constexpr_move(v)) {} + + template + explicit constexpr optional(in_place_t, Args&&... args) + : OptionalBase(in_place_t{}, constexpr_forward(args)...) {} + + template >)> + OPTIONAL_CONSTEXPR_INIT_LIST explicit optional(in_place_t, std::initializer_list il, Args&&... args) + : OptionalBase(in_place_t{}, il, constexpr_forward(args)...) {} + + // 20.5.4.2, Destructor + ~optional() = default; + + // 20.5.4.3, assignment + optional& operator=(nullopt_t) noexcept + { + clear(); + return *this; + } + + optional& operator=(const optional& rhs) + { + if (initialized() == true && rhs.initialized() == false) clear(); + else if (initialized() == false && rhs.initialized() == true) initialize(*rhs); + else if (initialized() == true && rhs.initialized() == true) contained_val() = *rhs; + return *this; + } + + optional& operator=(optional&& rhs) + noexcept(std::is_nothrow_move_assignable::value && std::is_nothrow_move_constructible::value) + { + if (initialized() == true && rhs.initialized() == false) clear(); + else if (initialized() == false && rhs.initialized() == true) initialize(std::move(*rhs)); + else if (initialized() == true && rhs.initialized() == true) contained_val() = std::move(*rhs); + return *this; + } + + template + auto operator=(U&& v) + -> typename std::enable_if + < + std::is_same::type, T>::value, + optional& + >::type + { + if (initialized()) { contained_val() = std::forward(v); } + else { initialize(std::forward(v)); } + return *this; + } + + + template + void emplace(Args&&... args) + { + clear(); + initialize(std::forward(args)...); + } + + template + void emplace(std::initializer_list il, Args&&... args) + { + clear(); + initialize(il, std::forward(args)...); + } + + // 20.5.4.4, Swap + void swap(optional& rhs) noexcept(std::is_nothrow_move_constructible::value + && noexcept(detail_::swap_ns::adl_swap(std::declval(), std::declval()))) + { + if (initialized() == true && rhs.initialized() == false) { rhs.initialize(std::move(**this)); clear(); } + else if (initialized() == false && rhs.initialized() == true) { initialize(std::move(*rhs)); rhs.clear(); } + else if (initialized() == true && rhs.initialized() == true) { using std::swap; swap(**this, *rhs); } + } + + // 20.5.4.5, Observers + + explicit constexpr operator bool() const noexcept { return initialized(); } + constexpr bool has_value() const noexcept { return initialized(); } + + constexpr T const* operator ->() const { + return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), dataptr()); + } + +# if OPTIONAL_HAS_MOVE_ACCESSORS == 1 + + constexpr T* operator ->() { + assert (initialized()); + return dataptr(); + } + + constexpr T const& operator *() const& { + return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val()); + } + + constexpr T& operator *() & { + assert (initialized()); + return contained_val(); + } + + constexpr T&& operator *() && { + assert (initialized()); + return constexpr_move(contained_val()); + } + + constexpr T const& value() const& { + return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val()); + } + + constexpr T& value() & { + return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val()); + } + + constexpr T&& value() && { + if (!initialized()) throw bad_optional_access("bad optional access"); + return std::move(contained_val()); + } + +# else + + T* operator ->() { + assert (initialized()); + return dataptr(); + } + + constexpr T const& operator *() const { + return TR2_OPTIONAL_ASSERTED_EXPRESSION(initialized(), contained_val()); + } + + T& operator *() { + assert (initialized()); + return contained_val(); + } + + constexpr T const& value() const { + return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val()); + } + + T& value() { + return initialized() ? contained_val() : (throw bad_optional_access("bad optional access"), contained_val()); + } + +# endif + + template + constexpr T value_or(V&& v) const& + { + return *this ? **this : detail_::convert(constexpr_forward(v)); + } + +# if OPTIONAL_HAS_MOVE_ACCESSORS == 1 + + template + constexpr T value_or(V&& v) && + { + return *this ? constexpr_move(const_cast&>(*this).contained_val()) : detail_::convert(constexpr_forward(v)); + } + +# else + + template + T value_or(V&& v) && + { + return *this ? constexpr_move(const_cast&>(*this).contained_val()) : detail_::convert(constexpr_forward(v)); + } + +# endif + + // 20.6.3.6, modifiers + void reset() noexcept { clear(); } +}; + + +template +class optional +{ + static_assert( !std::is_same::value, "bad T" ); + static_assert( !std::is_same::value, "bad T" ); + T* ref; + +public: + + // 20.5.5.1, construction/destruction + constexpr optional() noexcept : ref(nullptr) {} + + constexpr optional(nullopt_t) noexcept : ref(nullptr) {} + + constexpr optional(T& v) noexcept : ref(detail_::static_addressof(v)) {} + + optional(T&&) = delete; + + constexpr optional(const optional& rhs) noexcept : ref(rhs.ref) {} + + explicit constexpr optional(in_place_t, T& v) noexcept : ref(detail_::static_addressof(v)) {} + + explicit optional(in_place_t, T&&) = delete; + + ~optional() = default; + + // 20.5.5.2, mutation + optional& operator=(nullopt_t) noexcept { + ref = nullptr; + return *this; + } + + // optional& operator=(const optional& rhs) noexcept { + // ref = rhs.ref; + // return *this; + // } + + // optional& operator=(optional&& rhs) noexcept { + // ref = rhs.ref; + // return *this; + // } + + template + auto operator=(U&& rhs) noexcept + -> typename std::enable_if + < + std::is_same::type, optional>::value, + optional& + >::type + { + ref = rhs.ref; + return *this; + } + + template + auto operator=(U&& rhs) noexcept + -> typename std::enable_if + < + !std::is_same::type, optional>::value, + optional& + >::type + = delete; + + void emplace(T& v) noexcept { + ref = detail_::static_addressof(v); + } + + void emplace(T&&) = delete; + + + void swap(optional& rhs) noexcept + { + std::swap(ref, rhs.ref); + } + + // 20.5.5.3, observers + constexpr T* operator->() const { + return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, ref); + } + + constexpr T& operator*() const { + return TR2_OPTIONAL_ASSERTED_EXPRESSION(ref, *ref); + } + + constexpr T& value() const { + return ref ? *ref : (throw bad_optional_access("bad optional access"), *ref); + } + + explicit constexpr operator bool() const noexcept { + return ref != nullptr; + } + + constexpr bool has_value() const noexcept { + return ref != nullptr; + } + + template + constexpr typename std::decay::type value_or(V&& v) const + { + return *this ? **this : detail_::convert::type>(constexpr_forward(v)); + } + + // x.x.x.x, modifiers + void reset() noexcept { ref = nullptr; } +}; + + +template +class optional +{ + static_assert( sizeof(T) == 0, "optional rvalue references disallowed" ); +}; + + +// 20.5.8, Relational operators +template constexpr bool operator==(const optional& x, const optional& y) +{ + return bool(x) != bool(y) ? false : bool(x) == false ? true : *x == *y; +} + +template constexpr bool operator!=(const optional& x, const optional& y) +{ + return !(x == y); +} + +template constexpr bool operator<(const optional& x, const optional& y) +{ + return (!y) ? false : (!x) ? true : *x < *y; +} + +template constexpr bool operator>(const optional& x, const optional& y) +{ + return (y < x); +} + +template constexpr bool operator<=(const optional& x, const optional& y) +{ + return !(y < x); +} + +template constexpr bool operator>=(const optional& x, const optional& y) +{ + return !(x < y); +} + + +// 20.5.9, Comparison with nullopt +template constexpr bool operator==(const optional& x, nullopt_t) noexcept +{ + return (!x); +} + +template constexpr bool operator==(nullopt_t, const optional& x) noexcept +{ + return (!x); +} + +template constexpr bool operator!=(const optional& x, nullopt_t) noexcept +{ + return bool(x); +} + +template constexpr bool operator!=(nullopt_t, const optional& x) noexcept +{ + return bool(x); +} + +template constexpr bool operator<(const optional&, nullopt_t) noexcept +{ + return false; +} + +template constexpr bool operator<(nullopt_t, const optional& x) noexcept +{ + return bool(x); +} + +template constexpr bool operator<=(const optional& x, nullopt_t) noexcept +{ + return (!x); +} + +template constexpr bool operator<=(nullopt_t, const optional&) noexcept +{ + return true; +} + +template constexpr bool operator>(const optional& x, nullopt_t) noexcept +{ + return bool(x); +} + +template constexpr bool operator>(nullopt_t, const optional&) noexcept +{ + return false; +} + +template constexpr bool operator>=(const optional&, nullopt_t) noexcept +{ + return true; +} + +template constexpr bool operator>=(nullopt_t, const optional& x) noexcept +{ + return (!x); +} + + + +// 20.5.10, Comparison with T +template constexpr bool operator==(const optional& x, const T& v) +{ + return bool(x) ? *x == v : false; +} + +template constexpr bool operator==(const T& v, const optional& x) +{ + return bool(x) ? v == *x : false; +} + +template constexpr bool operator!=(const optional& x, const T& v) +{ + return bool(x) ? *x != v : true; +} + +template constexpr bool operator!=(const T& v, const optional& x) +{ + return bool(x) ? v != *x : true; +} + +template constexpr bool operator<(const optional& x, const T& v) +{ + return bool(x) ? *x < v : true; +} + +template constexpr bool operator>(const T& v, const optional& x) +{ + return bool(x) ? v > *x : true; +} + +template constexpr bool operator>(const optional& x, const T& v) +{ + return bool(x) ? *x > v : false; +} + +template constexpr bool operator<(const T& v, const optional& x) +{ + return bool(x) ? v < *x : false; +} + +template constexpr bool operator>=(const optional& x, const T& v) +{ + return bool(x) ? *x >= v : false; +} + +template constexpr bool operator<=(const T& v, const optional& x) +{ + return bool(x) ? v <= *x : false; +} + +template constexpr bool operator<=(const optional& x, const T& v) +{ + return bool(x) ? *x <= v : true; +} + +template constexpr bool operator>=(const T& v, const optional& x) +{ + return bool(x) ? v >= *x : true; +} + + +// Comparison of optional with T +template constexpr bool operator==(const optional& x, const T& v) +{ + return bool(x) ? *x == v : false; +} + +template constexpr bool operator==(const T& v, const optional& x) +{ + return bool(x) ? v == *x : false; +} + +template constexpr bool operator!=(const optional& x, const T& v) +{ + return bool(x) ? *x != v : true; +} + +template constexpr bool operator!=(const T& v, const optional& x) +{ + return bool(x) ? v != *x : true; +} + +template constexpr bool operator<(const optional& x, const T& v) +{ + return bool(x) ? *x < v : true; +} + +template constexpr bool operator>(const T& v, const optional& x) +{ + return bool(x) ? v > *x : true; +} + +template constexpr bool operator>(const optional& x, const T& v) +{ + return bool(x) ? *x > v : false; +} + +template constexpr bool operator<(const T& v, const optional& x) +{ + return bool(x) ? v < *x : false; +} + +template constexpr bool operator>=(const optional& x, const T& v) +{ + return bool(x) ? *x >= v : false; +} + +template constexpr bool operator<=(const T& v, const optional& x) +{ + return bool(x) ? v <= *x : false; +} + +template constexpr bool operator<=(const optional& x, const T& v) +{ + return bool(x) ? *x <= v : true; +} + +template constexpr bool operator>=(const T& v, const optional& x) +{ + return bool(x) ? v >= *x : true; +} + +// Comparison of optional with T +template constexpr bool operator==(const optional& x, const T& v) +{ + return bool(x) ? *x == v : false; +} + +template constexpr bool operator==(const T& v, const optional& x) +{ + return bool(x) ? v == *x : false; +} + +template constexpr bool operator!=(const optional& x, const T& v) +{ + return bool(x) ? *x != v : true; +} + +template constexpr bool operator!=(const T& v, const optional& x) +{ + return bool(x) ? v != *x : true; +} + +template constexpr bool operator<(const optional& x, const T& v) +{ + return bool(x) ? *x < v : true; +} + +template constexpr bool operator>(const T& v, const optional& x) +{ + return bool(x) ? v > *x : true; +} + +template constexpr bool operator>(const optional& x, const T& v) +{ + return bool(x) ? *x > v : false; +} + +template constexpr bool operator<(const T& v, const optional& x) +{ + return bool(x) ? v < *x : false; +} + +template constexpr bool operator>=(const optional& x, const T& v) +{ + return bool(x) ? *x >= v : false; +} + +template constexpr bool operator<=(const T& v, const optional& x) +{ + return bool(x) ? v <= *x : false; +} + +template constexpr bool operator<=(const optional& x, const T& v) +{ + return bool(x) ? *x <= v : true; +} + +template constexpr bool operator>=(const T& v, const optional& x) +{ + return bool(x) ? v >= *x : true; +} + + +// 20.5.12, Specialized algorithms +template +void swap(optional& x, optional& y) noexcept(noexcept(x.swap(y))) +{ + x.swap(y); +} + + +template +constexpr optional::type> make_optional(T&& v) +{ + return optional::type>(constexpr_forward(v)); +} + +template +constexpr optional make_optional(std::reference_wrapper v) +{ + return optional(v.get()); +} + + +} // namespace wpi + +namespace std +{ + template + struct hash> + { + typedef typename hash::result_type result_type; + typedef wpi::optional argument_type; + + constexpr result_type operator()(argument_type const& arg) const { + return arg ? std::hash{}(*arg) : result_type{}; + } + }; + + template + struct hash> + { + typedef typename hash::result_type result_type; + typedef wpi::optional argument_type; + + constexpr result_type operator()(argument_type const& arg) const { + return arg ? std::hash{}(*arg) : result_type{}; + } + }; +} + +# undef TR2_OPTIONAL_REQUIRES +# undef TR2_OPTIONAL_ASSERTED_EXPRESSION + +# endif //WPIUTIL_WPI_OPTIONAL_H diff --git a/wpiutil/src/test/native/cpp/test_optional.cpp b/wpiutil/src/test/native/cpp/test_optional.cpp new file mode 100644 index 0000000000..779cc18399 --- /dev/null +++ b/wpiutil/src/test/native/cpp/test_optional.cpp @@ -0,0 +1,1520 @@ +// Copyright (C) 2011 - 2017 Andrzej Krzemienski. +// +// Use, modification, and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// The idea and interface is based on Boost.Optional library +// authored by Fernando Luis Cacciola Carballal + +# include "wpi/optional.h" + +#include "gtest/gtest.h" + +# include +# include +# include +# include + + + +enum State +{ + sDefaultConstructed, + sValueCopyConstructed, + sValueMoveConstructed, + sCopyConstructed, + sMoveConstructed, + sMoveAssigned, + sCopyAssigned, + sValueCopyAssigned, + sValueMoveAssigned, + sMovedFrom, + sValueConstructed +}; + +struct OracleVal +{ + State s; + int i; + OracleVal(int i = 0) : s(sValueConstructed), i(i) {} +}; + +struct Oracle +{ + State s; + OracleVal val; + + Oracle() : s(sDefaultConstructed) {} + Oracle(const OracleVal& v) : s(sValueCopyConstructed), val(v) {} + Oracle(OracleVal&& v) : s(sValueMoveConstructed), val(std::move(v)) {v.s = sMovedFrom;} + Oracle(const Oracle& o) : s(sCopyConstructed), val(o.val) {} + Oracle(Oracle&& o) : s(sMoveConstructed), val(std::move(o.val)) {o.s = sMovedFrom;} + + Oracle& operator=(const OracleVal& v) { s = sValueCopyConstructed; val = v; return *this; } + Oracle& operator=(OracleVal&& v) { s = sValueMoveConstructed; val = std::move(v); v.s = sMovedFrom; return *this; } + Oracle& operator=(const Oracle& o) { s = sCopyConstructed; val = o.val; return *this; } + Oracle& operator=(Oracle&& o) { s = sMoveConstructed; val = std::move(o.val); o.s = sMovedFrom; return *this; } +}; + +struct Guard +{ + std::string val; + Guard() : val{} {} + explicit Guard(std::string s, int = 0) : val(s) {} + Guard(const Guard&) = delete; + Guard(Guard&&) = delete; + void operator=(const Guard&) = delete; + void operator=(Guard&&) = delete; +}; + +struct ExplicitStr +{ + std::string s; + explicit ExplicitStr(const char* chp) : s(chp) {}; +}; + +struct Date +{ + int i; + Date() = delete; + Date(int i) : i{i} {}; + Date(Date&& d) : i(d.i) { d.i = 0; } + Date(const Date&) = delete; + Date& operator=(const Date&) = delete; + Date& operator=(Date&& d) { i = d.i; d.i = 0; return *this;}; +}; + +bool operator==( Oracle const& a, Oracle const& b ) { return a.val.i == b.val.i; } +bool operator!=( Oracle const& a, Oracle const& b ) { return a.val.i != b.val.i; } + + +namespace tr2 = wpi; + + +TEST(Optional, disengaged_ctor) +{ + tr2::optional o1; + assert (!o1); + + tr2::optional o2 = tr2::nullopt; + assert (!o2); + + tr2::optional o3 = o2; + assert (!o3); + + assert (o1 == tr2::nullopt); + assert (o1 == tr2::optional{}); + assert (!o1); + assert (bool(o1) == false); + + assert (o2 == tr2::nullopt); + assert (o2 == tr2::optional{}); + assert (!o2); + assert (bool(o2) == false); + + assert (o3 == tr2::nullopt); + assert (o3 == tr2::optional{}); + assert (!o3); + assert (bool(o3) == false); + + assert (o1 == o2); + assert (o2 == o1); + assert (o1 == o3); + assert (o3 == o1); + assert (o2 == o3); + assert (o3 == o2); +} + + +TEST(Optional, value_ctor) +{ + OracleVal v; + tr2::optional oo1(v); + assert (oo1 != tr2::nullopt); + assert (oo1 != tr2::optional{}); + assert (oo1 == tr2::optional{v}); + assert (!!oo1); + assert (bool(oo1)); + // NA: assert (oo1->s == sValueCopyConstructed); + assert (oo1->s == sMoveConstructed); + assert (v.s == sValueConstructed); + + tr2::optional oo2(std::move(v)); + assert (oo2 != tr2::nullopt); + assert (oo2 != tr2::optional{}); + assert (oo2 == oo1); + assert (!!oo2); + assert (bool(oo2)); + // NA: assert (oo2->s == sValueMoveConstructed); + assert (oo2->s == sMoveConstructed); + assert (v.s == sMovedFrom); + + { + OracleVal v; + tr2::optional oo1{tr2::in_place, v}; + assert (oo1 != tr2::nullopt); + assert (oo1 != tr2::optional{}); + assert (oo1 == tr2::optional{v}); + assert (!!oo1); + assert (bool(oo1)); + assert (oo1->s == sValueCopyConstructed); + assert (v.s == sValueConstructed); + + tr2::optional oo2{tr2::in_place, std::move(v)}; + assert (oo2 != tr2::nullopt); + assert (oo2 != tr2::optional{}); + assert (oo2 == oo1); + assert (!!oo2); + assert (bool(oo2)); + assert (oo2->s == sValueMoveConstructed); + assert (v.s == sMovedFrom); + } +} + + +TEST(Optional, assignment) +{ + tr2::optional oi; + oi = tr2::optional{1}; + assert (*oi == 1); + + oi = tr2::nullopt; + assert (!oi); + + oi = 2; + assert (*oi == 2); + + oi = {}; + assert (!oi); +} + + +template +struct MoveAware +{ + T val; + bool moved; + MoveAware(T val) : val(val), moved(false) {} + MoveAware(MoveAware const&) = delete; + MoveAware(MoveAware&& rhs) : val(rhs.val), moved(rhs.moved) { + rhs.moved = true; + } + MoveAware& operator=(MoveAware const&) = delete; + MoveAware& operator=(MoveAware&& rhs) { + val = (rhs.val); + moved = (rhs.moved); + rhs.moved = true; + return *this; + } +}; + +TEST(Optional, moved_from_state) +{ + // first, test mock: + MoveAware i{1}, j{2}; + assert (i.val == 1); + assert (!i.moved); + assert (j.val == 2); + assert (!j.moved); + + MoveAware k = std::move(i); + assert (k.val == 1); + assert (!k.moved); + assert (i.val == 1); + assert (i.moved); + + k = std::move(j); + assert (k.val == 2); + assert (!k.moved); + assert (j.val == 2); + assert (j.moved); + + // now, test optional + tr2::optional> oi{1}, oj{2}; + assert (oi); + assert (!oi->moved); + assert (oj); + assert (!oj->moved); + + tr2::optional> ok = std::move(oi); + assert (ok); + assert (!ok->moved); + assert (oi); + assert (oi->moved); + + ok = std::move(oj); + assert (ok); + assert (!ok->moved); + assert (oj); + assert (oj->moved); +} + + +TEST(Optional, copy_move_ctor_optional_int) +{ + tr2::optional oi; + tr2::optional oj = oi; + + assert (!oj); + assert (oj == oi); + assert (oj == tr2::nullopt); + assert (!bool(oj)); + + oi = 1; + tr2::optional ok = oi; + assert (!!ok); + assert (bool(ok)); + assert (ok == oi); + assert (ok != oj); + assert (*ok == 1); + + tr2::optional ol = std::move(oi); + assert (!!ol); + assert (bool(ol)); + assert (ol == oi); + assert (ol != oj); + assert (*ol == 1); +} + + +TEST(Optional, optional_optional) +{ + tr2::optional> oi1 = tr2::nullopt; + assert (oi1 == tr2::nullopt); + assert (!oi1); + + { + tr2::optional> oi2 {tr2::in_place}; + assert (oi2 != tr2::nullopt); + assert (bool(oi2)); + assert (*oi2 == tr2::nullopt); + //assert (!(*oi2)); + //std::cout << typeid(**oi2).name() << std::endl; + } + + { + tr2::optional> oi2 {tr2::in_place, tr2::nullopt}; + assert (oi2 != tr2::nullopt); + assert (bool(oi2)); + assert (*oi2 == tr2::nullopt); + assert (!*oi2); + } + + { + tr2::optional> oi2 {tr2::optional{}}; + assert (oi2 != tr2::nullopt); + assert (bool(oi2)); + assert (*oi2 == tr2::nullopt); + assert (!*oi2); + } + + tr2::optional oi; + auto ooi = tr2::make_optional(oi); + static_assert( std::is_same>, decltype(ooi)>::value, ""); + +} + +TEST(Optional, example_guard) +{ + using namespace tr2; + //FAILS: optional ogx(Guard("res1")); + //FAILS: optional ogx = "res1"; + //FAILS: optional ogx("res1"); + optional oga; // Guard is non-copyable (and non-moveable) + optional ogb(in_place, "res1"); // initialzes the contained value with "res1" + assert (bool(ogb)); + assert (ogb->val == "res1"); + + optional ogc(in_place); // default-constructs the contained value + assert (bool(ogc)); + assert (ogc->val == ""); + + oga.emplace("res1"); // initialzes the contained value with "res1" + assert (bool(oga)); + assert (oga->val == "res1"); + + oga.emplace(); // destroys the contained value and + // default-constructs the new one + assert (bool(oga)); + assert (oga->val == ""); + + oga = nullopt; // OK: make disengaged the optional Guard + assert (!(oga)); + //FAILS: ogb = {}; // ERROR: Guard is not Moveable +} + + +void process(){} +void process(int ){} +void processNil(){} + + +TEST(Optional, example1) +{ + using namespace tr2; + optional oi; // create disengaged object + optional oj = nullopt; // alternative syntax + oi = oj; // assign disengaged object + optional ok = oj; // ok is disengaged + + if (oi) assert(false); // 'if oi is engaged...' + if (!oi) assert(true); // 'if oi is disengaged...' + + if (oi != nullopt) assert(false); // 'if oi is engaged...' + if (oi == nullopt) assert(true); // 'if oi is disengaged...' + + assert(oi == ok); // two disengaged optionals compare equal + + /////////////////////////////////////////////////////////////////////////// + optional ol{1}; // ol is engaged; its contained value is 1 + ok = 2; // ok becomes engaged; its contained value is 2 + oj = ol; // oj becomes engaged; its contained value is 1 + + assert(oi != ol); // disengaged != engaged + assert(ok != ol); // different contained values + assert(oj == ol); // same contained value + assert(oi < ol); // disengaged < engaged + assert(ol < ok); // less by contained value + + ///////////////////////////////////////////////////////////////////////////// + optional om{1}; // om is engaged; its contained value is 1 + optional on = om; // on is engaged; its contained value is 1 + om = 2; // om is engaged; its contained value is 2 + assert (on != om); // on still contains 3. They are not pointers + + ///////////////////////////////////////////////////////////////////////////// + int i = *ol; // i obtains the value contained in ol + assert (i == 1); + *ol = 9; // the object contained in ol becomes 9 + assert(*ol == 9); + assert(ol == make_optional(9)); + + /////////////////////////////////// + int p = 1; + optional op = p; + assert(*op == 1); + p = 2; + assert(*op == 1); // value contained in op is separated from p + + //////////////////////////////// + if (ol) + process(*ol); // use contained value if present + else + process(); // proceed without contained value + + if (!om) + processNil(); + else + process(*om); + + ///////////////////////////////////////// + process(ol.value_or(0)); // use 0 if ol is disengaged + + //////////////////////////////////////////// + ok = nullopt; // if ok was engaged calls T's dtor + oj = {}; // assigns a temporary disengaged optional +} + + +TEST(Optional, example_guard2) +{ + using wpi::optional; + const optional c = 4; + int i = *c; // i becomes 4 + assert (i == 4); + // FAILS: *c = i; // ERROR: cannot assign to const int& +} + + +TEST(Optional, example_ref) +{ + using namespace wpi; + int i = 1; + int j = 2; + optional ora; // disengaged optional reference to int + optional orb = i; // contained reference refers to object i + + *orb = 3; // i becomes 3 + // FAILS: ora = j; // ERROR: optional refs do not have assignment from T + // FAILS: ora = {j}; // ERROR: optional refs do not have copy/move assignment + // FAILS: ora = orb; // ERROR: no copy/move assignment + ora.emplace(j); // OK: contained reference refers to object j + ora.emplace(i); // OK: contained reference now refers to object i + + ora = nullopt; // OK: ora becomes disengaged +} + + +template +T getValue( tr2::optional newVal = tr2::nullopt, tr2::optional storeHere = tr2::nullopt ) +{ + T cached{}; + + if (newVal) { + cached = *newVal; + + if (storeHere) { + *storeHere = *newVal; // LEGAL: assigning T to T + } + } + return cached; +} + +TEST(Optional, example_optional_arg) +{ + int iii = 0; + iii = getValue(iii, iii); + iii = getValue(iii); + iii = getValue(); + + { + using namespace wpi; + optional grd1{in_place, "res1", 1}; // guard 1 initialized + optional grd2; + + grd2.emplace("res2", 2); // guard 2 initialized + grd1 = nullopt; // guard 1 released + + } // guard 2 released (in dtor) +} + + +std::tuple getStartMidEnd() { return std::tuple{Date{1}, Date{2}, Date{3}}; } +void run(Date const&, Date const&, Date const&) {} + +TEST(Optional, example_date) +{ + using namespace wpi; + optional start, mid, end; // Date doesn't have default ctor (no good default date) + + std::tie(start, mid, end) = getStartMidEnd(); + run(*start, *mid, *end); +} + + +wpi::optional readNextChar(){ return{}; } + +void run(wpi::optional) {} +void run(std::complex) {} + + +template +void assign_norebind(tr2::optional& optref, T& obj) +{ + if (optref) *optref = obj; + else optref.emplace(obj); +} + +template void unused(T&&) {} + +TEST(Optional, example_conceptual_model) +{ + using namespace wpi; + + optional oi = 0; + optional oj = 1; + optional ok = nullopt; + + oi = 1; + oj = nullopt; + ok = 0; + + unused(oi == nullopt); + unused(oj == 0); + unused(ok == 1); +} + +TEST(Optional, example_rationale) +{ + using namespace wpi; + if (optional ch = readNextChar()) { + unused(ch); + // ... + } + + ////////////////////////////////// + optional opt1 = nullopt; + optional opt2 = {}; + + opt1 = nullopt; + opt2 = {}; + + if (opt1 == nullopt) {} + if (!opt2) {} + if (opt2 == optional{}) {} + + + + //////////////////////////////// + + run(nullopt); // pick the second overload + // FAILS: run({}); // ambiguous + + if (opt1 == nullopt) {} // fine + // FAILS: if (opt2 == {}) {} // ilegal + + //////////////////////////////// + assert (optional{} < optional{0}); + assert (optional{0} < optional{1}); + assert (!(optional{} < optional{}) ); + assert (!(optional{1} < optional{1})); + + assert (optional{} != optional{0}); + assert (optional{0} != optional{1}); + assert (optional{} == optional{} ); + assert (optional{0} == optional{0}); + + ///////////////////////////////// + optional o; + o = make_optional(1); // copy/move assignment + o = 1; // assignment from T + o.emplace(1); // emplacement + + //////////////////////////////////// + int isas = 0, i = 9; + optional asas = i; + assign_norebind(asas, isas); + + ///////////////////////////////////// + ////tr2::optional> ov2 = {2, 3}; + ////assert (bool(ov2)); + ////assert ((*ov2)[1] == 3); + //// + //////////////////////////////// + ////std::vector v = {1, 2, 4, 8}; + ////optional> ov = {1, 2, 4, 8}; + + ////assert (v == *ov); + //// + ////ov = {1, 2, 4, 8}; + + ////std::allocator a; + ////optional> ou { in_place, {1, 2, 4, 8}, a }; + + ////assert (ou == ov); + + ////////////////////////////// + // inconvenient syntax: + { + + tr2::optional> ov2{tr2::in_place, {2, 3}}; + + assert (bool(ov2)); + assert ((*ov2)[1] == 3); + + //////////////////////////// + + std::vector v = {1, 2, 4, 8}; + optional> ov{tr2::in_place, {1, 2, 4, 8}}; + + assert (v == *ov); + + ov.emplace({1, 2, 4, 8}); +/* + std::allocator a; + optional> ou { in_place, {1, 2, 4, 8}, a }; + + assert (ou == ov); +*/ + } + + ///////////////////////////////// + { + typedef int T; + optional> ot {in_place}; + optional> ou {in_place, nullopt}; + optional> ov {optional{}}; + + optional oi; + auto ooi = make_optional(oi); + static_assert( std::is_same>, decltype(ooi)>::value, ""); + } +} + + +bool fun(std::string , wpi::optional oi = wpi::nullopt) +{ + return bool(oi); +} + +TEST(Optional, example_converting_ctor) +{ + using namespace wpi; + + assert (true == fun("dog", 2)); + assert (false == fun("dog")); + assert (false == fun("dog", nullopt)); // just to be explicit +} + + +TEST(Optional, bad_comparison) +{ + tr2::optional oi, oj; + int i; + bool b = (oi == oj); + b = (oi >= i); + b = (oi == i); + unused(b); +} + + +//// NOT APPLICABLE ANYMORE +////TEST(Optional, perfect_ctor) +////{ +//// //tr2::optional ois = "OS"; +//// assert (*ois == "OS"); +//// +//// // FAILS: tr2::optional oes = "OS"; +//// tr2::optional oes{"OS"}; +//// assert (oes->s == "OS"); +////} + +TEST(Optional, value_or) +{ + tr2::optional oi = 1; + int i = oi.value_or(0); + assert (i == 1); + + oi = tr2::nullopt; + assert (oi.value_or(3) == 3); + + tr2::optional os{"AAA"}; + assert (os.value_or("BBB") == "AAA"); + os = {}; + assert (os.value_or("BBB") == "BBB"); +} + +TEST(Optional, reset) +{ + using namespace wpi; + optional oi {1}; + oi.reset(); + assert (!oi); + + int i = 1; + optional oir {i}; + oir.reset(); + assert (!oir); +} + +TEST(Optional, mixed_order) +{ + using namespace wpi; + + optional oN {nullopt}; + optional o0 {0}; + optional o1 {1}; + + assert ( (oN < 0)); + assert ( (oN < 1)); + assert (!(o0 < 0)); + assert ( (o0 < 1)); + assert (!(o1 < 0)); + assert (!(o1 < 1)); + + assert (!(oN >= 0)); + assert (!(oN >= 1)); + assert ( (o0 >= 0)); + assert (!(o0 >= 1)); + assert ( (o1 >= 0)); + assert ( (o1 >= 1)); + + assert (!(oN > 0)); + assert (!(oN > 1)); + assert (!(o0 > 0)); + assert (!(o0 > 1)); + assert ( (o1 > 0)); + assert (!(o1 > 1)); + + assert ( (oN <= 0)); + assert ( (oN <= 1)); + assert ( (o0 <= 0)); + assert ( (o0 <= 1)); + assert (!(o1 <= 0)); + assert ( (o1 <= 1)); + + assert ( (0 > oN)); + assert ( (1 > oN)); + assert (!(0 > o0)); + assert ( (1 > o0)); + assert (!(0 > o1)); + assert (!(1 > o1)); + + assert (!(0 <= oN)); + assert (!(1 <= oN)); + assert ( (0 <= o0)); + assert (!(1 <= o0)); + assert ( (0 <= o1)); + assert ( (1 <= o1)); + + assert (!(0 < oN)); + assert (!(1 < oN)); + assert (!(0 < o0)); + assert (!(1 < o0)); + assert ( (0 < o1)); + assert (!(1 < o1)); + + assert ( (0 >= oN)); + assert ( (1 >= oN)); + assert ( (0 >= o0)); + assert ( (1 >= o0)); + assert (!(0 >= o1)); + assert ( (1 >= o1)); +} + +struct BadRelops +{ + int i; +}; + +constexpr bool operator<(BadRelops a, BadRelops b) { return a.i < b.i; } +constexpr bool operator>(BadRelops a, BadRelops b) { return a.i < b.i; } // intentional error! + +TEST(Optional, bad_relops) +{ + using namespace wpi; + BadRelops a{1}, b{2}; + assert (a < b); + assert (a > b); + + optional oa = a, ob = b; + assert (oa < ob); + assert (!(oa > ob)); + + assert (oa < b); + assert (oa > b); + + optional ra = a, rb = b; + assert (ra < rb); + assert (!(ra > rb)); + + assert (ra < b); + assert (ra > b); +} + + +TEST(Optional, mixed_equality) +{ + using namespace wpi; + + assert (make_optional(0) == 0); + assert (make_optional(1) == 1); + assert (make_optional(0) != 1); + assert (make_optional(1) != 0); + + optional oN {nullopt}; + optional o0 {0}; + optional o1 {1}; + + assert (o0 == 0); + assert ( 0 == o0); + assert (o1 == 1); + assert ( 1 == o1); + assert (o1 != 0); + assert ( 0 != o1); + assert (o0 != 1); + assert ( 1 != o0); + + assert ( 1 != oN); + assert ( 0 != oN); + assert (oN != 1); + assert (oN != 0); + assert (!( 1 == oN)); + assert (!( 0 == oN)); + assert (!(oN == 1)); + assert (!(oN == 0)); + + std::string cat{"cat"}, dog{"dog"}; + optional oNil{}, oDog{"dog"}, oCat{"cat"}; + + assert (oCat == cat); + assert ( cat == oCat); + assert (oDog == dog); + assert ( dog == oDog); + assert (oDog != cat); + assert ( cat != oDog); + assert (oCat != dog); + assert ( dog != oCat); + + assert ( dog != oNil); + assert ( cat != oNil); + assert (oNil != dog); + assert (oNil != cat); + assert (!( dog == oNil)); + assert (!( cat == oNil)); + assert (!(oNil == dog)); + assert (!(oNil == cat)); +} + +TEST(Optional, const_propagation) +{ + using namespace wpi; + + optional mmi{0}; + static_assert(std::is_same::value, "WTF"); + + const optional cmi{0}; + static_assert(std::is_same::value, "WTF"); + + optional mci{0}; + static_assert(std::is_same::value, "WTF"); + + optional cci{0}; + static_assert(std::is_same::value, "WTF"); +} + + +static_assert(std::is_base_of::value, ""); + +TEST(Optional, safe_value) +{ + using namespace wpi; + + try { + optional ovN{}, ov1{1}; + + int& r1 = ov1.value(); + assert (r1 == 1); + + try { + ovN.value(); + assert (false); + } + catch (bad_optional_access const&) { + } + + { // ref variant + int i1 = 1; + optional orN{}, or1{i1}; + + int& r2 = or1.value(); + assert (r2 == 1); + + try { + orN.value(); + assert (false); + } + catch (bad_optional_access const&) { + } + } + } + catch(...) { + assert (false); + } +} + +TEST(Optional, optional_ref) +{ + using namespace tr2; + // FAILS: optional orr; + // FAILS: optional on; + int i = 8; + optional ori; + assert (!ori); + ori.emplace(i); + assert (bool(ori)); + assert (*ori == 8); + assert (&*ori == &i); + *ori = 9; + assert (i == 9); + + // FAILS: int& ir = ori.value_or(i); + int ii = ori.value_or(i); + assert (ii == 9); + ii = 7; + assert (*ori == 9); + + int j = 22; + auto&& oj = make_optional(std::ref(j)); + *oj = 23; + assert (&*oj == &j); + assert (j == 23); +} + +TEST(Optional, optional_ref_const_propagation) +{ + using namespace wpi; + + int i = 9; + const optional mi = i; + int& r = *mi; + optional ci = i; + static_assert(std::is_same::value, "WTF"); + static_assert(std::is_same::value, "WTF"); + + unused(r); +} + +TEST(Optional, optional_ref_assign) +{ + using namespace wpi; + + int i = 9; + optional ori = i; + + int j = 1; + ori = optional{j}; + ori = {j}; + // FAILS: ori = j; + + optional orx = ori; + ori = orx; + + optional orj = j; + + assert (ori); + assert (*ori == 1); + assert (ori == orj); + assert (i == 9); + + *ori = 2; + assert (*ori == 2); + assert (ori == 2); + assert (2 == ori); + assert (ori != 3); + + assert (ori == orj); + assert (j == 2); + assert (i == 9); + + ori = {}; + assert (!ori); + assert (ori != orj); + assert (j == 2); + assert (i == 9); +} + +TEST(Optional, optional_swap) +{ + namespace tr2 = wpi; + tr2::optional oi {1}, oj {}; + swap(oi, oj); + assert (oj); + assert (*oj == 1); + assert (!oi); + static_assert(noexcept(swap(oi, oj)), "swap() is not noexcept"); +} + + +TEST(Optional, optional_ref_swap) +{ + using namespace wpi; + int i = 0; + int j = 1; + optional oi = i; + optional oj = j; + + assert (&*oi == &i); + assert (&*oj == &j); + + swap(oi, oj); + assert (&*oi == &j); + assert (&*oj == &i); +} + +TEST(Optional, optional_initialization) +{ + using namespace tr2; + using std::string; + string s = "STR"; + + optional os{s}; + optional ot = s; + optional ou{"STR"}; + optional ov = string{"STR"}; + +} + +#include + +TEST(Optional, optional_hashing) +{ + using namespace tr2; + using std::string; + + std::hash hi; + std::hash> hoi; + std::hash hs; + std::hash> hos; + + assert (hi(0) == hoi(optional{0})); + assert (hi(1) == hoi(optional{1})); + assert (hi(3198) == hoi(optional{3198})); + + assert (hs("") == hos(optional{""})); + assert (hs("0") == hos(optional{"0"})); + assert (hs("Qa1#") == hos(optional{"Qa1#"})); + + std::unordered_set> set; + assert(set.find({"Qa1#"}) == set.end()); + + set.insert({"0"}); + assert(set.find({"Qa1#"}) == set.end()); + + set.insert({"Qa1#"}); + assert(set.find({"Qa1#"}) != set.end()); +} + + +// optional_ref_emulation +template +struct generic +{ + typedef T type; +}; + +template +struct generic +{ + typedef std::reference_wrapper type; +}; + +template +using Generic = typename generic::type; + +template +bool generic_fun() +{ + wpi::optional> op; + return bool(op); +} + +TEST(Optional, optional_ref_emulation) +{ + using namespace wpi; + optional> oi = 1; + assert (*oi == 1); + + int i = 8; + int j = 4; + optional> ori {i}; + assert (*ori == 8); + assert ((void*)&*ori != (void*)&i); // !DIFFERENT THAN optional + + *ori = j; + assert (*ori == 4); +} + + +TEST(Optional, moved_on_value_or) +{ + using namespace tr2; + optional oo{in_place}; + + assert (oo); + assert (oo->s == sDefaultConstructed); + + Oracle o = std::move(oo).value_or( Oracle{OracleVal{}} ); + assert (oo); + assert (oo->s == sMovedFrom); + assert (o.s == sMoveConstructed); + + optional> om {in_place, 1}; + assert (om); + assert (om->moved == false); + + /*MoveAware m =*/ std::move(om).value_or( MoveAware{1} ); + assert (om); + assert (om->moved == true); + +# if OPTIONAL_HAS_MOVE_ACCESSORS == 1 + { + Date d = optional{in_place, 1}.value(); + assert (d.i); // to silence compiler warning + + Date d2 = *optional{in_place, 1}; + assert (d2.i); // to silence compiler warning + } +# endif +} + + +TEST(Optional, optional_ref_hashing) +{ + using namespace tr2; + using std::string; + + std::hash hi; + std::hash> hoi; + std::hash hs; + std::hash> hos; + + int i0 = 0; + int i1 = 1; + assert (hi(0) == hoi(optional{i0})); + assert (hi(1) == hoi(optional{i1})); + + string s{""}; + string s0{"0"}; + string sCAT{"CAT"}; + assert (hs("") == hos(optional{s})); + assert (hs("0") == hos(optional{s0})); + assert (hs("CAT") == hos(optional{sCAT})); + + std::unordered_set> set; + assert(set.find({sCAT}) == set.end()); + + set.insert({s0}); + assert(set.find({sCAT}) == set.end()); + + set.insert({sCAT}); + assert(set.find({sCAT}) != set.end()); +} + +struct Combined +{ + int m = 0; + int n = 1; + + constexpr Combined() : m{5}, n{6} {} + constexpr Combined(int m, int n) : m{m}, n{n} {} +}; + +struct Nasty +{ + int m = 0; + int n = 1; + + constexpr Nasty() : m{5}, n{6} {} + constexpr Nasty(int m, int n) : m{m}, n{n} {} + + int operator&() { return n; } + int operator&() const { return n; } +}; + +TEST(Optional, arrow_operator) +{ + using namespace wpi; + + optional oc1{in_place, 1, 2}; + assert (oc1); + assert (oc1->m == 1); + assert (oc1->n == 2); + + optional on{in_place, 1, 2}; + assert (on); + assert (on->m == 1); + assert (on->n == 2); +} + +TEST(Optional, arrow_wit_optional_ref) +{ + using namespace wpi; + + Combined c{1, 2}; + optional oc = c; + assert (oc); + assert (oc->m == 1); + assert (oc->n == 2); + + Nasty n{1, 2}; + Nasty m{3, 4}; + Nasty p{5, 6}; + + optional on{n}; + assert (on); + assert (on->m == 1); + assert (on->n == 2); + + on = {m}; + assert (on); + assert (on->m == 3); + assert (on->n == 4); + + on.emplace(p); + assert (on); + assert (on->m == 5); + assert (on->n == 6); + + optional om{in_place, n}; + assert (om); + assert (om->m == 1); + assert (om->n == 2); +} + +TEST(Optional, no_dangling_reference_in_value) +{ + // this mostly tests compiler warnings + using namespace wpi; + optional oi {2}; + unused (oi.value()); + const optional coi {3}; + unused (coi.value()); +} + +struct CountedObject +{ + static int _counter; + bool _throw; + CountedObject(bool b) : _throw(b) { ++_counter; } + CountedObject(CountedObject const& rhs) : _throw(rhs._throw) { if (_throw) throw int(); } + ~CountedObject() { --_counter; } +}; + +int CountedObject::_counter = 0; + +TEST(Optional, exception_safety) +{ + using namespace wpi; + try { + optional oo(in_place, true); // throw + optional o1(oo); + } + catch(...) + { + // + } + assert(CountedObject::_counter == 0); + + try { + optional oo(in_place, true); // throw + optional o1(std::move(oo)); // now move + } + catch(...) + { + // + } + assert(CountedObject::_counter == 0); +} + +TEST(Optional, nested_optional) +{ + using namespace wpi; + + optional>> o1 {nullopt}; + assert (!o1); + + optional>> o2 {in_place, nullopt}; + assert (o2); + assert (!*o2); + + optional>> o3 (in_place, in_place, nullopt); + assert (o3); + assert (*o3); + assert (!**o3); +} + +TEST(Optional, three_ways_of_having_value) +{ + using namespace wpi; + optional oN, o1 (1); + + assert (!oN); + assert (!oN.has_value()); + assert (oN == nullopt); + + assert (o1); + assert (o1.has_value()); + assert (o1 != nullopt); + + assert (bool(oN) == oN.has_value()); + assert (bool(o1) == o1.has_value()); + + int i = 1; + optional rN, r1 (i); + + assert (!rN); + assert (!rN.has_value()); + assert (rN == nullopt); + + assert (r1); + assert (r1.has_value()); + assert (r1 != nullopt); + + assert (bool(rN) == rN.has_value()); + assert (bool(r1) == r1.has_value()); +} + +//// constexpr tests + +// these 4 classes have different noexcept signatures in move operations +struct NothrowBoth { + NothrowBoth(NothrowBoth&&) noexcept(true) {}; + void operator=(NothrowBoth&&) noexcept(true) {}; +}; +struct NothrowCtor { + NothrowCtor(NothrowCtor&&) noexcept(true) {}; + void operator=(NothrowCtor&&) noexcept(false) {}; +}; +struct NothrowAssign { + NothrowAssign(NothrowAssign&&) noexcept(false) {}; + void operator=(NothrowAssign&&) noexcept(true) {}; +}; +struct NothrowNone { + NothrowNone(NothrowNone&&) noexcept(false) {}; + void operator=(NothrowNone&&) noexcept(false) {}; +}; + +void test_noexcept() +{ + { + tr2::optional b1, b2; + static_assert(noexcept(tr2::optional{tr2::constexpr_move(b1)}), "bad noexcept!"); + static_assert(noexcept(b1 = tr2::constexpr_move(b2)), "bad noexcept!"); + } + { + tr2::optional c1, c2; + static_assert(noexcept(tr2::optional{tr2::constexpr_move(c1)}), "bad noexcept!"); + static_assert(!noexcept(c1 = tr2::constexpr_move(c2)), "bad noexcept!"); + } + { + tr2::optional a1, a2; + static_assert(!noexcept(tr2::optional{tr2::constexpr_move(a1)}), "bad noexcept!"); + static_assert(!noexcept(a1 = tr2::constexpr_move(a2)), "bad noexcept!"); + } + { + tr2::optional n1, n2; + static_assert(!noexcept(tr2::optional{tr2::constexpr_move(n1)}), "bad noexcept!"); + static_assert(!noexcept(n1 = tr2::constexpr_move(n2)), "bad noexcept!"); + } +} + + +void constexpr_test_disengaged() +{ + constexpr tr2::optional g0{}; + constexpr tr2::optional g1{tr2::nullopt}; + static_assert( !g0, "initialized!" ); + static_assert( !g1, "initialized!" ); + + static_assert( bool(g1) == bool(g0), "ne!" ); + + static_assert( g1 == g0, "ne!" ); + static_assert( !(g1 != g0), "ne!" ); + static_assert( g1 >= g0, "ne!" ); + static_assert( !(g1 > g0), "ne!" ); + static_assert( g1 <= g0, "ne!" ); + static_assert( !(g1 = tr2::nullopt, "!" ); + static_assert( !(g1 > tr2::nullopt), "!" ); + + static_assert( (tr2::nullopt == g0), "!" ); + static_assert( !(tr2::nullopt != g0), "!" ); + static_assert( (tr2::nullopt >= g0), "!" ); + static_assert( !(tr2::nullopt > g0), "!" ); + static_assert( (tr2::nullopt <= g0), "!" ); + static_assert( !(tr2::nullopt < g0), "!" ); + + static_assert( (g1 != tr2::optional(1)), "!" ); + static_assert( !(g1 == tr2::optional(1)), "!" ); + static_assert( (g1 < tr2::optional(1)), "!" ); + static_assert( (g1 <= tr2::optional(1)), "!" ); + static_assert( !(g1 > tr2::optional(1)), "!" ); + static_assert( !(g1 > tr2::optional(1)), "!" ); +} + + +constexpr tr2::optional g0{}; +constexpr tr2::optional g2{2}; +static_assert( g2, "not initialized!" ); +static_assert( *g2 == 2, "not 2!" ); +static_assert( g2 == tr2::optional(2), "not 2!" ); +static_assert( g2 != g0, "eq!" ); + +# if OPTIONAL_HAS_MOVE_ACCESSORS == 1 +static_assert( *tr2::optional{3} == 3, "WTF!" ); +static_assert( tr2::optional{3}.value() == 3, "WTF!" ); +static_assert( tr2::optional{3}.value_or(1) == 3, "WTF!" ); +static_assert( tr2::optional{}.value_or(4) == 4, "WTF!" ); +# endif + +constexpr tr2::optional gc0{tr2::in_place}; +static_assert(gc0->n == 6, "WTF!"); + +// optional refs +int gi = 0; +constexpr tr2::optional gori = gi; +constexpr tr2::optional gorn{}; +constexpr int& gri = *gori; +static_assert(gori, "WTF"); +static_assert(!gorn, "WTF"); +static_assert(gori != tr2::nullopt, "WTF"); +static_assert(gorn == tr2::nullopt, "WTF"); +static_assert(&gri == &*gori, "WTF"); + +constexpr int gci = 1; +constexpr tr2::optional gorci = gci; +constexpr tr2::optional gorcn{}; + +static_assert(gorcn < gorci, "WTF"); +static_assert(gorcn <= gorci, "WTF"); +static_assert(gorci == gorci, "WTF"); +static_assert(*gorci == 1, "WTF"); +static_assert(gorci == gci, "WTF"); + +namespace constexpr_optional_ref_and_arrow +{ + using namespace wpi; + constexpr Combined c{1, 2}; + constexpr optional oc = c; + static_assert(oc, "WTF!"); + static_assert(oc->m == 1, "WTF!"); + static_assert(oc->n == 2, "WTF!"); +} + +#if OPTIONAL_HAS_CONSTEXPR_INIT_LIST + +namespace InitList +{ + using namespace wpi; + + struct ConstInitLister + { + template + constexpr ConstInitLister(std::initializer_list il) : len (il.size()) {} + size_t len; + }; + + constexpr ConstInitLister CIL {2, 3, 4}; + static_assert(CIL.len == 3, "WTF!"); + + constexpr optional oil {in_place, {4, 5, 6, 7}}; + static_assert(oil, "WTF!"); + static_assert(oil->len == 4, "WTF!"); +} + +#endif // OPTIONAL_HAS_CONSTEXPR_INIT_LIST + +// end constexpr tests + + +#include + + +struct VEC +{ + std::vector v; + template + VEC( X&&...x) : v(std::forward(x)...) {} + + template + VEC(std::initializer_list il, X&&...x) : v(il, std::forward(x)...) {} +}; + + + +TEST(Optional, Vector) { + tr2::optional oi = 1; + assert (bool(oi)); + oi.operator=({}); + assert (!oi); + + VEC v = {5, 6}; + + if (OPTIONAL_HAS_CONSTEXPR_INIT_LIST) + std::cout << "Optional has constexpr initializer_list" << std::endl; + else + std::cout << "Optional doesn't have constexpr initializer_list" << std::endl; + + if (OPTIONAL_HAS_MOVE_ACCESSORS) + std::cout << "Optional has constexpr move accessors" << std::endl; + else + std::cout << "Optional doesn't have constexpr move accessors" << std::endl; +} +