From 1868e0ccf1beba675736c1d2c47579854110241e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20Mr=C3=A1zek?= Date: Sat, 6 Feb 2021 15:58:03 +0100 Subject: [PATCH] GH-3229 fix copy seed button not working for newer worlds Added the `optional-bare` library and refactored NBT reading code to support this change. --- CMakeLists.txt | 1 + COPYING.md | 28 + api/logic/CMakeLists.txt | 2 +- api/logic/minecraft/World.cpp | 204 ++++--- api/logic/minecraft/World.h | 26 +- api/logic/minecraft/WorldList.cpp | 2 +- libraries/optional-bare/CMakeLists.txt | 5 + libraries/optional-bare/LICENSE.txt | 23 + libraries/optional-bare/README.md | 5 + .../optional-bare/include/nonstd/optional | 508 ++++++++++++++++++ 10 files changed, 728 insertions(+), 76 deletions(-) create mode 100644 libraries/optional-bare/CMakeLists.txt create mode 100644 libraries/optional-bare/LICENSE.txt create mode 100644 libraries/optional-bare/README.md create mode 100644 libraries/optional-bare/include/nonstd/optional diff --git a/CMakeLists.txt b/CMakeLists.txt index b178462e0..b91da7352 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -264,6 +264,7 @@ add_subdirectory(libraries/rainbow) # Qt extension for colors add_subdirectory(libraries/iconfix) # fork of Qt's QIcon loader add_subdirectory(libraries/LocalPeer) # fork of a library from Qt solutions add_subdirectory(libraries/classparser) # google analytics library +add_subdirectory(libraries/optional-bare) ############################### Built Artifacts ############################### diff --git a/COPYING.md b/COPYING.md index 70ff7ce21..db25ec013 100644 --- a/COPYING.md +++ b/COPYING.md @@ -223,3 +223,31 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +# optional-bare + + Code from https://github.com/martinmoene/optional-bare/ + + 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/api/logic/CMakeLists.txt b/api/logic/CMakeLists.txt index 15916bb58..be4318a85 100644 --- a/api/logic/CMakeLists.txt +++ b/api/logic/CMakeLists.txt @@ -526,7 +526,7 @@ set_target_properties(MultiMC_logic PROPERTIES CXX_VISIBILITY_PRESET hidden VISI generate_export_header(MultiMC_logic) # Link -target_link_libraries(MultiMC_logic systeminfo MultiMC_quazip MultiMC_classparser ${NBT_NAME} ${ZLIB_LIBRARIES} BuildConfig) +target_link_libraries(MultiMC_logic systeminfo MultiMC_quazip MultiMC_classparser ${NBT_NAME} ${ZLIB_LIBRARIES} optional-bare BuildConfig) target_link_libraries(MultiMC_logic Qt5::Core Qt5::Xml Qt5::Network Qt5::Concurrent) # Mark and export headers diff --git a/api/logic/minecraft/World.cpp b/api/logic/minecraft/World.cpp index 06a6080a1..ad06bb83f 100644 --- a/api/logic/minecraft/World.cpp +++ b/api/logic/minecraft/World.cpp @@ -32,7 +32,36 @@ #include -QString gameTypeToString(GameType type) +#include + +using nonstd::optional; +using nonstd::nullopt; + +GameType::GameType(nonstd::optional original): + original(original) +{ + if(!original) { + return; + } + switch(*original) { + case 0: + type = GameType::Survival; + break; + case 1: + type = GameType::Creative; + break; + case 2: + type = GameType::Adventure; + break; + case 3: + type = GameType::Spectator; + break; + default: + break; + } +} + +QString GameType::toTranslatedString() const { switch (type) { @@ -47,7 +76,31 @@ QString gameTypeToString(GameType type) default: break; } - return QObject::tr("Unknown"); + if(original) { + return QCoreApplication::translate("GameType", "Unknown (%1)").arg(*original); + } + return QCoreApplication::translate("GameType", "Undefined"); +} + +QString GameType::toLogString() const +{ + switch (type) + { + case GameType::Survival: + return "Survival"; + case GameType::Creative: + return "Creative"; + case GameType::Adventure: + return "Adventure"; + case GameType::Spectator: + return "Spectator"; + default: + break; + } + if(original) { + return QString("Unknown (%1)").arg(*original); + } + return "Undefined"; } std::unique_ptr parseLevelDat(QByteArray data) @@ -58,15 +111,22 @@ std::unique_ptr parseLevelDat(QByteArray data) return nullptr; } std::istringstream foo(std::string(output.constData(), output.size())); - auto pair = nbt::io::read_compound(foo); + try { + auto pair = nbt::io::read_compound(foo); - if(pair.first != "") + if(pair.first != "") + return nullptr; + + if(pair.second == nullptr) + return nullptr; + + return std::move(pair.second); + } + catch (const nbt::io::input_error &e) + { + qWarning() << "Unable to parse level.dat:" << e.what(); return nullptr; - - if(pair.second == nullptr) - return nullptr; - - return std::move(pair.second); + } } QByteArray serializeLevelDat(nbt::tag_compound * levelInfo) @@ -288,14 +348,16 @@ bool World::rename(const QString &newName) return true; } -static QString read_string (nbt::value& parent, const char * name, const QString & fallback = QString()) +namespace { + +optional read_string (nbt::value& parent, const char * name) { try { auto &namedValue = parent.at(name); if(namedValue.get_type() != nbt::tag_type::String) { - return fallback; + return nullopt; } auto & tag_str = namedValue.as(); return QString::fromStdString(tag_str.get()); @@ -303,25 +365,25 @@ static QString read_string (nbt::value& parent, const char * name, const QString catch (const std::out_of_range &e) { // fallback for old world formats - qWarning() << "String NBT tag" << name << "could not be found. Defaulting to" << fallback; - return fallback; + qWarning() << "String NBT tag" << name << "could not be found."; + return nullopt; } catch (const std::bad_cast &e) { // type mismatch - qWarning() << "NBT tag" << name << "could not be converted to string. Defaulting to" << fallback; - return fallback; + qWarning() << "NBT tag" << name << "could not be converted to string."; + return nullopt; } } -static int64_t read_long (nbt::value& parent, const char * name, const int64_t & fallback = 0) +optional read_long (nbt::value& parent, const char * name) { try { auto &namedValue = parent.at(name); if(namedValue.get_type() != nbt::tag_type::Long) { - return fallback; + return nullopt; } auto & tag_str = namedValue.as(); return tag_str.get(); @@ -329,25 +391,25 @@ static int64_t read_long (nbt::value& parent, const char * name, const int64_t & catch (const std::out_of_range &e) { // fallback for old world formats - qWarning() << "Long NBT tag" << name << "could not be found. Defaulting to" << fallback; - return fallback; + qWarning() << "Long NBT tag" << name << "could not be found."; + return nullopt; } catch (const std::bad_cast &e) { // type mismatch - qWarning() << "NBT tag" << name << "could not be converted to long. Defaulting to" << fallback; - return fallback; + qWarning() << "NBT tag" << name << "could not be converted to long."; + return nullopt; } } -static int read_int (nbt::value& parent, const char * name, const int & fallback = 0) +optional read_int (nbt::value& parent, const char * name) { try { auto &namedValue = parent.at(name); if(namedValue.get_type() != nbt::tag_type::Int) { - return fallback; + return nullopt; } auto & tag_str = namedValue.as(); return tag_str.get(); @@ -355,62 +417,72 @@ static int read_int (nbt::value& parent, const char * name, const int & fallback catch (const std::out_of_range &e) { // fallback for old world formats - qWarning() << "Int NBT tag" << name << "could not be found. Defaulting to" << fallback; - return fallback; + qWarning() << "Int NBT tag" << name << "could not be found."; + return nullopt; } catch (const std::bad_cast &e) { // type mismatch - qWarning() << "NBT tag" << name << "could not be converted to int. Defaulting to" << fallback; - return fallback; + qWarning() << "NBT tag" << name << "could not be converted to int."; + return nullopt; } } +GameType read_gametype(nbt::value& parent, const char * name) { + return GameType(read_int(parent, name)); +} + +} + void World::loadFromLevelDat(QByteArray data) { - try + auto levelData = parseLevelDat(data); + if(!levelData) { - auto levelData = parseLevelDat(data); - if(!levelData) - { - is_valid = false; - return; - } - - auto &val = levelData->at("Data"); - is_valid = val.get_type() == nbt::tag_type::Compound; - if(!is_valid) - return; - - m_actualName = read_string(val, "LevelName", m_folderName); - - - int64_t temp = read_long(val, "LastPlayed", 0); - if(temp == 0) - { - m_lastPlayed = levelDatTime; - } - else - { - m_lastPlayed = QDateTime::fromMSecsSinceEpoch(temp); - } - - int GameType_val = read_int(val, "GameType", 0); - m_gameType = (GameType) GameType_val; - - m_randomSeed = read_long(val, "RandomSeed", 0); - - qDebug() << "World Name:" << m_actualName; - qDebug() << "Last Played:" << m_lastPlayed.toString(); - qDebug() << "Seed:" << m_randomSeed; - qDebug() << "GameMode:" << GameType_val; - } - catch (const nbt::io::input_error &e) - { - qWarning() << "Unable to load" << m_folderName << ":" << e.what(); is_valid = false; return; } + + nbt::value * valPtr = nullptr; + try { + valPtr = &levelData->at("Data"); + } + catch (const std::out_of_range &e) { + qWarning() << "Unable to read NBT tags from " << m_folderName << ":" << e.what(); + is_valid = false; + return; + } + nbt::value &val = *valPtr; + + is_valid = val.get_type() == nbt::tag_type::Compound; + if(!is_valid) + return; + + auto name = read_string(val, "LevelName"); + m_actualName = name ? *name : m_folderName; + + auto timestamp = read_long(val, "LastPlayed"); + m_lastPlayed = timestamp ? QDateTime::fromMSecsSinceEpoch(*timestamp) : levelDatTime; + + m_gameType = read_gametype(val, "GameType"); + + optional randomSeed; + try { + auto &WorldGen_val = val.at("WorldGenSettings"); + randomSeed = read_long(WorldGen_val, "seed"); + } + catch (std::out_of_range) {} + if(!randomSeed) { + randomSeed = read_long(val, "RandomSeed"); + } + m_randomSeed = randomSeed ? *randomSeed : 0; + + qDebug() << "World Name:" << m_actualName; + qDebug() << "Last Played:" << m_lastPlayed.toString(); + if(randomSeed) { + qDebug() << "Seed:" << *randomSeed; + } + qDebug() << "GameType:" << m_gameType.toLogString(); } bool World::replace(World &with) diff --git a/api/logic/minecraft/World.h b/api/logic/minecraft/World.h index d04c1040a..ba7c357c7 100644 --- a/api/logic/minecraft/World.h +++ b/api/logic/minecraft/World.h @@ -16,17 +16,27 @@ #pragma once #include #include +#include #include "multimc_logic_export.h" -enum class GameType -{ - Survival, - Creative, - Adventure, - Spectator +struct MULTIMC_LOGIC_EXPORT GameType { + GameType() = default; + GameType (nonstd::optional original); + + QString toTranslatedString() const; + QString toLogString() const; + + enum + { + Unknown = -1, + Survival = 0, + Creative, + Adventure, + Spectator + } type = Unknown; + nonstd::optional original; }; -QString MULTIMC_LOGIC_EXPORT gameTypeToString(GameType type); class MULTIMC_LOGIC_EXPORT World { @@ -98,6 +108,6 @@ protected: QDateTime levelDatTime; QDateTime m_lastPlayed; int64_t m_randomSeed = 0; - GameType m_gameType = GameType::Survival; + GameType m_gameType; bool is_valid = false; }; diff --git a/api/logic/minecraft/WorldList.cpp b/api/logic/minecraft/WorldList.cpp index 94b59da4b..7bb9ab760 100644 --- a/api/logic/minecraft/WorldList.cpp +++ b/api/logic/minecraft/WorldList.cpp @@ -175,7 +175,7 @@ QVariant WorldList::data(const QModelIndex &index, int role) const return world.name(); case GameModeColumn: - return gameTypeToString(world.gameType()); + return world.gameType().toTranslatedString(); case LastPlayedColumn: return world.lastPlayed(); diff --git a/libraries/optional-bare/CMakeLists.txt b/libraries/optional-bare/CMakeLists.txt new file mode 100644 index 000000000..b8b498c5b --- /dev/null +++ b/libraries/optional-bare/CMakeLists.txt @@ -0,0 +1,5 @@ +cmake_minimum_required(VERSION 3.1) +project(optional-bare) + +add_library(optional-bare INTERFACE) +target_include_directories(optional-bare INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}/include") diff --git a/libraries/optional-bare/LICENSE.txt b/libraries/optional-bare/LICENSE.txt new file mode 100644 index 000000000..36b7cd93c --- /dev/null +++ b/libraries/optional-bare/LICENSE.txt @@ -0,0 +1,23 @@ +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/libraries/optional-bare/README.md b/libraries/optional-bare/README.md new file mode 100644 index 000000000..e29ff7c14 --- /dev/null +++ b/libraries/optional-bare/README.md @@ -0,0 +1,5 @@ +# optional bare + +A simple single-file header-only version of a C++17-like optional for default-constructible, copyable types, for C++98 and later. + +Imported from: https://github.com/martinmoene/optional-bare/commit/0bb1d183bcee1e854c4ea196b533252c51f98b81 diff --git a/libraries/optional-bare/include/nonstd/optional b/libraries/optional-bare/include/nonstd/optional new file mode 100644 index 000000000..ecbfa030d --- /dev/null +++ b/libraries/optional-bare/include/nonstd/optional @@ -0,0 +1,508 @@ +// +// Copyright 2017-2019 by Martin Moene +// +// https://github.com/martinmoene/optional-bare +// +// Distributed under the Boost Software License, Version 1.0. +// (See accompanying file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + +#ifndef NONSTD_OPTIONAL_BARE_HPP +#define NONSTD_OPTIONAL_BARE_HPP + +#define optional_bare_MAJOR 1 +#define optional_bare_MINOR 1 +#define optional_bare_PATCH 0 + +#define optional_bare_VERSION optional_STRINGIFY(optional_bare_MAJOR) "." optional_STRINGIFY(optional_bare_MINOR) "." optional_STRINGIFY(optional_bare_PATCH) + +#define optional_STRINGIFY( x ) optional_STRINGIFY_( x ) +#define optional_STRINGIFY_( x ) #x + +// optional-bare configuration: + +#define optional_OPTIONAL_DEFAULT 0 +#define optional_OPTIONAL_NONSTD 1 +#define optional_OPTIONAL_STD 2 + +#if !defined( optional_CONFIG_SELECT_OPTIONAL ) +# define optional_CONFIG_SELECT_OPTIONAL ( optional_HAVE_STD_OPTIONAL ? optional_OPTIONAL_STD : optional_OPTIONAL_NONSTD ) +#endif + +// Control presence of exception handling (try and auto discover): + +#ifndef optional_CONFIG_NO_EXCEPTIONS +# if _MSC_VER +# include // for _HAS_EXCEPTIONS +# endif +# if _MSC_VER +# include // for _HAS_EXCEPTIONS +# endif +# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS) +# define optional_CONFIG_NO_EXCEPTIONS 0 +# else +# define optional_CONFIG_NO_EXCEPTIONS 1 +# endif +#endif + +// C++ language version detection (C++20 is speculative): +// Note: VC14.0/1900 (VS2015) lacks too much from C++14. + +#ifndef optional_CPLUSPLUS +# if defined(_MSVC_LANG ) && !defined(__clang__) +# define optional_CPLUSPLUS (_MSC_VER == 1900 ? 201103L : _MSVC_LANG ) +# else +# define optional_CPLUSPLUS __cplusplus +# endif +#endif + +#define optional_CPP98_OR_GREATER ( optional_CPLUSPLUS >= 199711L ) +#define optional_CPP11_OR_GREATER ( optional_CPLUSPLUS >= 201103L ) +#define optional_CPP14_OR_GREATER ( optional_CPLUSPLUS >= 201402L ) +#define optional_CPP17_OR_GREATER ( optional_CPLUSPLUS >= 201703L ) +#define optional_CPP20_OR_GREATER ( optional_CPLUSPLUS >= 202000L ) + +// C++ language version (represent 98 as 3): + +#define optional_CPLUSPLUS_V ( optional_CPLUSPLUS / 100 - (optional_CPLUSPLUS > 200000 ? 2000 : 1994) ) + +// Use C++17 std::optional if available and requested: + +#if optional_CPP17_OR_GREATER && defined(__has_include ) +# if __has_include( ) +# define optional_HAVE_STD_OPTIONAL 1 +# else +# define optional_HAVE_STD_OPTIONAL 0 +# endif +#else +# define optional_HAVE_STD_OPTIONAL 0 +#endif + +#define optional_USES_STD_OPTIONAL ( (optional_CONFIG_SELECT_OPTIONAL == optional_OPTIONAL_STD) || ((optional_CONFIG_SELECT_OPTIONAL == optional_OPTIONAL_DEFAULT) && optional_HAVE_STD_OPTIONAL) ) + +// +// Using std::optional: +// + +#if optional_USES_STD_OPTIONAL + +#include +#include + +namespace nonstd { + + using std::in_place; + using std::in_place_type; + using std::in_place_index; + using std::in_place_t; + using std::in_place_type_t; + using std::in_place_index_t; + + using std::optional; + using std::bad_optional_access; + using std::hash; + + using std::nullopt; + using std::nullopt_t; + + using std::operator==; + using std::operator!=; + using std::operator<; + using std::operator<=; + using std::operator>; + using std::operator>=; + using std::make_optional; + using std::swap; +} + +#else // optional_USES_STD_OPTIONAL + +#include + +#if ! optional_CONFIG_NO_EXCEPTIONS +# include +#endif + +namespace nonstd { namespace optional_bare { + +// type for nullopt + +struct nullopt_t +{ + struct init{}; + nullopt_t( init ) {} +}; + +// extra parenthesis to prevent the most vexing parse: + +const nullopt_t nullopt(( nullopt_t::init() )); + +// optional access error. + +#if ! optional_CONFIG_NO_EXCEPTIONS + +class bad_optional_access : public std::logic_error +{ +public: + explicit bad_optional_access() + : logic_error( "bad optional access" ) {} +}; + +#endif // optional_CONFIG_NO_EXCEPTIONS + +// Simplistic optional: requires T to be default constructible, copyable. + +template< typename T > +class optional +{ +private: + typedef void (optional::*safe_bool)() const; + +public: + typedef T value_type; + + optional() + : has_value_( false ) + {} + + optional( nullopt_t ) + : has_value_( false ) + {} + + optional( T const & arg ) + : has_value_( true ) + , value_ ( arg ) + {} + + template< class U > + optional( optional const & other ) + : has_value_( other.has_value() ) + , value_ ( other.value() ) + {} + + optional & operator=( nullopt_t ) + { + reset(); + return *this; + } + + template< class U > + optional & operator=( optional const & other ) + { + has_value_ = other.has_value(); + value_ = other.value(); + return *this; + } + + void swap( optional & rhs ) + { + using std::swap; + if ( has_value() == true && rhs.has_value() == true ) { swap( **this, *rhs ); } + else if ( has_value() == false && rhs.has_value() == true ) { initialize( *rhs ); rhs.reset(); } + else if ( has_value() == true && rhs.has_value() == false ) { rhs.initialize( **this ); reset(); } + } + + // observers + + value_type const * operator->() const + { + return assert( has_value() ), + &value_; + } + + value_type * operator->() + { + return assert( has_value() ), + &value_; + } + + value_type const & operator*() const + { + return assert( has_value() ), + value_; + } + + value_type & operator*() + { + return assert( has_value() ), + value_; + } + +#if optional_CPP11_OR_GREATER + explicit operator bool() const + { + return has_value(); + } +#else + operator safe_bool() const + { + return has_value() ? &optional::this_type_does_not_support_comparisons : 0; + } +#endif + + bool has_value() const + { + return has_value_; + } + + value_type const & value() const + { +#if optional_CONFIG_NO_EXCEPTIONS + assert( has_value() ); +#else + if ( ! has_value() ) + throw bad_optional_access(); +#endif + return value_; + } + + value_type & value() + { +#if optional_CONFIG_NO_EXCEPTIONS + assert( has_value() ); +#else + if ( ! has_value() ) + throw bad_optional_access(); +#endif + return value_; + } + + template< class U > + value_type value_or( U const & v ) const + { + return has_value() ? value() : static_cast( v ); + } + + // modifiers + + void reset() + { + has_value_ = false; + } + +private: + void this_type_does_not_support_comparisons() const {} + + template< typename V > + void initialize( V const & value ) + { + assert( ! has_value() ); + value_ = value; + has_value_ = true; + } + +private: + bool has_value_; + value_type value_; +}; + +// Relational operators + +template< typename T, typename U > +inline bool operator==( optional const & x, optional const & y ) +{ + return bool(x) != bool(y) ? false : bool(x) == false ? true : *x == *y; +} + +template< typename T, typename U > +inline bool operator!=( optional const & x, optional const & y ) +{ + return !(x == y); +} + +template< typename T, typename U > +inline bool operator<( optional const & x, optional const & y ) +{ + return (!y) ? false : (!x) ? true : *x < *y; +} + +template< typename T, typename U > +inline bool operator>( optional const & x, optional const & y ) +{ + return (y < x); +} + +template< typename T, typename U > +inline bool operator<=( optional const & x, optional const & y ) +{ + return !(y < x); +} + +template< typename T, typename U > +inline bool operator>=( optional const & x, optional const & y ) +{ + return !(x < y); +} + +// Comparison with nullopt + +template< typename T > +inline bool operator==( optional const & x, nullopt_t ) +{ + return (!x); +} + +template< typename T > +inline bool operator==( nullopt_t, optional const & x ) +{ + return (!x); +} + +template< typename T > +inline bool operator!=( optional const & x, nullopt_t ) +{ + return bool(x); +} + +template< typename T > +inline bool operator!=( nullopt_t, optional const & x ) +{ + return bool(x); +} + +template< typename T > +inline bool operator<( optional const &, nullopt_t ) +{ + return false; +} + +template< typename T > +inline bool operator<( nullopt_t, optional const & x ) +{ + return bool(x); +} + +template< typename T > +inline bool operator<=( optional const & x, nullopt_t ) +{ + return (!x); +} + +template< typename T > +inline bool operator<=( nullopt_t, optional const & ) +{ + return true; +} + +template< typename T > +inline bool operator>( optional const & x, nullopt_t ) +{ + return bool(x); +} + +template< typename T > +inline bool operator>( nullopt_t, optional const & ) +{ + return false; +} + +template< typename T > +inline bool operator>=( optional const &, nullopt_t ) +{ + return true; +} + +template< typename T > +inline bool operator>=( nullopt_t, optional const & x ) +{ + return (!x); +} + +// Comparison with T + +template< typename T, typename U > +inline bool operator==( optional const & x, U const & v ) +{ + return bool(x) ? *x == v : false; +} + +template< typename T, typename U > +inline bool operator==( U const & v, optional const & x ) +{ + return bool(x) ? v == *x : false; +} + +template< typename T, typename U > +inline bool operator!=( optional const & x, U const & v ) +{ + return bool(x) ? *x != v : true; +} + +template< typename T, typename U > +inline bool operator!=( U const & v, optional const & x ) +{ + return bool(x) ? v != *x : true; +} + +template< typename T, typename U > +inline bool operator<( optional const & x, U const & v ) +{ + return bool(x) ? *x < v : true; +} + +template< typename T, typename U > +inline bool operator<( U const & v, optional const & x ) +{ + return bool(x) ? v < *x : false; +} + +template< typename T, typename U > +inline bool operator<=( optional const & x, U const & v ) +{ + return bool(x) ? *x <= v : true; +} + +template< typename T, typename U > +inline bool operator<=( U const & v, optional const & x ) +{ + return bool(x) ? v <= *x : false; +} + +template< typename T, typename U > +inline bool operator>( optional const & x, U const & v ) +{ + return bool(x) ? *x > v : false; +} + +template< typename T, typename U > +inline bool operator>( U const & v, optional const & x ) +{ + return bool(x) ? v > *x : true; +} + +template< typename T, typename U > +inline bool operator>=( optional const & x, U const & v ) +{ + return bool(x) ? *x >= v : false; +} + +template< typename T, typename U > +inline bool operator>=( U const & v, optional const & x ) +{ + return bool(x) ? v >= *x : true; +} + +// Specialized algorithms + +template< typename T > +void swap( optional & x, optional & y ) +{ + x.swap( y ); +} + +// Convenience function to create an optional. + +template< typename T > +inline optional make_optional( T const & v ) +{ + return optional( v ); +} + +} // namespace optional-bare + +using namespace optional_bare; + +} // namespace nonstd + +#endif // optional_USES_STD_OPTIONAL + +#endif // NONSTD_OPTIONAL_BARE_HPP