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.
This commit is contained in:
parent
f8ca96a335
commit
1868e0ccf1
@ -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 ###############################
|
||||
|
||||
|
28
COPYING.md
28
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.
|
||||
|
@ -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
|
||||
|
@ -32,7 +32,36 @@
|
||||
|
||||
#include <QCoreApplication>
|
||||
|
||||
QString gameTypeToString(GameType type)
|
||||
#include <nonstd/optional>
|
||||
|
||||
using nonstd::optional;
|
||||
using nonstd::nullopt;
|
||||
|
||||
GameType::GameType(nonstd::optional<int> 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 <nbt::tag_compound> parseLevelDat(QByteArray data)
|
||||
@ -58,6 +111,7 @@ std::unique_ptr <nbt::tag_compound> parseLevelDat(QByteArray data)
|
||||
return nullptr;
|
||||
}
|
||||
std::istringstream foo(std::string(output.constData(), output.size()));
|
||||
try {
|
||||
auto pair = nbt::io::read_compound(foo);
|
||||
|
||||
if(pair.first != "")
|
||||
@ -67,6 +121,12 @@ std::unique_ptr <nbt::tag_compound> parseLevelDat(QByteArray data)
|
||||
return nullptr;
|
||||
|
||||
return std::move(pair.second);
|
||||
}
|
||||
catch (const nbt::io::input_error &e)
|
||||
{
|
||||
qWarning() << "Unable to parse level.dat:" << e.what();
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
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<QString> 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<nbt::tag_string>();
|
||||
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<int64_t> 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<nbt::tag_long>();
|
||||
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<int> 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<nbt::tag_int>();
|
||||
return tag_str.get();
|
||||
@ -355,21 +417,25 @@ 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)
|
||||
{
|
||||
@ -377,40 +443,46 @@ void World::loadFromLevelDat(QByteArray data)
|
||||
return;
|
||||
}
|
||||
|
||||
auto &val = levelData->at("Data");
|
||||
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;
|
||||
|
||||
m_actualName = read_string(val, "LevelName", m_folderName);
|
||||
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;
|
||||
|
||||
int64_t temp = read_long(val, "LastPlayed", 0);
|
||||
if(temp == 0)
|
||||
{
|
||||
m_lastPlayed = levelDatTime;
|
||||
m_gameType = read_gametype(val, "GameType");
|
||||
|
||||
optional<int64_t> randomSeed;
|
||||
try {
|
||||
auto &WorldGen_val = val.at("WorldGenSettings");
|
||||
randomSeed = read_long(WorldGen_val, "seed");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_lastPlayed = QDateTime::fromMSecsSinceEpoch(temp);
|
||||
catch (std::out_of_range) {}
|
||||
if(!randomSeed) {
|
||||
randomSeed = read_long(val, "RandomSeed");
|
||||
}
|
||||
|
||||
int GameType_val = read_int(val, "GameType", 0);
|
||||
m_gameType = (GameType) GameType_val;
|
||||
|
||||
m_randomSeed = read_long(val, "RandomSeed", 0);
|
||||
m_randomSeed = randomSeed ? *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;
|
||||
if(randomSeed) {
|
||||
qDebug() << "Seed:" << *randomSeed;
|
||||
}
|
||||
qDebug() << "GameType:" << m_gameType.toLogString();
|
||||
}
|
||||
|
||||
bool World::replace(World &with)
|
||||
|
@ -16,17 +16,27 @@
|
||||
#pragma once
|
||||
#include <QFileInfo>
|
||||
#include <QDateTime>
|
||||
#include <nonstd/optional>
|
||||
|
||||
#include "multimc_logic_export.h"
|
||||
|
||||
enum class GameType
|
||||
{
|
||||
Survival,
|
||||
struct MULTIMC_LOGIC_EXPORT GameType {
|
||||
GameType() = default;
|
||||
GameType (nonstd::optional<int> original);
|
||||
|
||||
QString toTranslatedString() const;
|
||||
QString toLogString() const;
|
||||
|
||||
enum
|
||||
{
|
||||
Unknown = -1,
|
||||
Survival = 0,
|
||||
Creative,
|
||||
Adventure,
|
||||
Spectator
|
||||
} type = Unknown;
|
||||
nonstd::optional<int> 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;
|
||||
};
|
||||
|
@ -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();
|
||||
|
5
libraries/optional-bare/CMakeLists.txt
Normal file
5
libraries/optional-bare/CMakeLists.txt
Normal file
@ -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")
|
23
libraries/optional-bare/LICENSE.txt
Normal file
23
libraries/optional-bare/LICENSE.txt
Normal file
@ -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.
|
5
libraries/optional-bare/README.md
Normal file
5
libraries/optional-bare/README.md
Normal file
@ -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
|
508
libraries/optional-bare/include/nonstd/optional
Normal file
508
libraries/optional-bare/include/nonstd/optional
Normal file
@ -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 <cstddef> // for _HAS_EXCEPTIONS
|
||||
# endif
|
||||
# if _MSC_VER
|
||||
# include <cstddef> // 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( <optional> )
|
||||
# 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 <optional>
|
||||
#include <utility>
|
||||
|
||||
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 <cassert>
|
||||
|
||||
#if ! optional_CONFIG_NO_EXCEPTIONS
|
||||
# include <stdexcept>
|
||||
#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<U> const & other )
|
||||
: has_value_( other.has_value() )
|
||||
, value_ ( other.value() )
|
||||
{}
|
||||
|
||||
optional & operator=( nullopt_t )
|
||||
{
|
||||
reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template< class U >
|
||||
optional & operator=( optional<U> 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<value_type>( 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<T> const & x, optional<U> const & y )
|
||||
{
|
||||
return bool(x) != bool(y) ? false : bool(x) == false ? true : *x == *y;
|
||||
}
|
||||
|
||||
template< typename T, typename U >
|
||||
inline bool operator!=( optional<T> const & x, optional<U> const & y )
|
||||
{
|
||||
return !(x == y);
|
||||
}
|
||||
|
||||
template< typename T, typename U >
|
||||
inline bool operator<( optional<T> const & x, optional<U> const & y )
|
||||
{
|
||||
return (!y) ? false : (!x) ? true : *x < *y;
|
||||
}
|
||||
|
||||
template< typename T, typename U >
|
||||
inline bool operator>( optional<T> const & x, optional<U> const & y )
|
||||
{
|
||||
return (y < x);
|
||||
}
|
||||
|
||||
template< typename T, typename U >
|
||||
inline bool operator<=( optional<T> const & x, optional<U> const & y )
|
||||
{
|
||||
return !(y < x);
|
||||
}
|
||||
|
||||
template< typename T, typename U >
|
||||
inline bool operator>=( optional<T> const & x, optional<U> const & y )
|
||||
{
|
||||
return !(x < y);
|
||||
}
|
||||
|
||||
// Comparison with nullopt
|
||||
|
||||
template< typename T >
|
||||
inline bool operator==( optional<T> const & x, nullopt_t )
|
||||
{
|
||||
return (!x);
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
inline bool operator==( nullopt_t, optional<T> const & x )
|
||||
{
|
||||
return (!x);
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
inline bool operator!=( optional<T> const & x, nullopt_t )
|
||||
{
|
||||
return bool(x);
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
inline bool operator!=( nullopt_t, optional<T> const & x )
|
||||
{
|
||||
return bool(x);
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
inline bool operator<( optional<T> const &, nullopt_t )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
inline bool operator<( nullopt_t, optional<T> const & x )
|
||||
{
|
||||
return bool(x);
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
inline bool operator<=( optional<T> const & x, nullopt_t )
|
||||
{
|
||||
return (!x);
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
inline bool operator<=( nullopt_t, optional<T> const & )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
inline bool operator>( optional<T> const & x, nullopt_t )
|
||||
{
|
||||
return bool(x);
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
inline bool operator>( nullopt_t, optional<T> const & )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
inline bool operator>=( optional<T> const &, nullopt_t )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template< typename T >
|
||||
inline bool operator>=( nullopt_t, optional<T> const & x )
|
||||
{
|
||||
return (!x);
|
||||
}
|
||||
|
||||
// Comparison with T
|
||||
|
||||
template< typename T, typename U >
|
||||
inline bool operator==( optional<T> const & x, U const & v )
|
||||
{
|
||||
return bool(x) ? *x == v : false;
|
||||
}
|
||||
|
||||
template< typename T, typename U >
|
||||
inline bool operator==( U const & v, optional<T> const & x )
|
||||
{
|
||||
return bool(x) ? v == *x : false;
|
||||
}
|
||||
|
||||
template< typename T, typename U >
|
||||
inline bool operator!=( optional<T> const & x, U const & v )
|
||||
{
|
||||
return bool(x) ? *x != v : true;
|
||||
}
|
||||
|
||||
template< typename T, typename U >
|
||||
inline bool operator!=( U const & v, optional<T> const & x )
|
||||
{
|
||||
return bool(x) ? v != *x : true;
|
||||
}
|
||||
|
||||
template< typename T, typename U >
|
||||
inline bool operator<( optional<T> const & x, U const & v )
|
||||
{
|
||||
return bool(x) ? *x < v : true;
|
||||
}
|
||||
|
||||
template< typename T, typename U >
|
||||
inline bool operator<( U const & v, optional<T> const & x )
|
||||
{
|
||||
return bool(x) ? v < *x : false;
|
||||
}
|
||||
|
||||
template< typename T, typename U >
|
||||
inline bool operator<=( optional<T> const & x, U const & v )
|
||||
{
|
||||
return bool(x) ? *x <= v : true;
|
||||
}
|
||||
|
||||
template< typename T, typename U >
|
||||
inline bool operator<=( U const & v, optional<T> const & x )
|
||||
{
|
||||
return bool(x) ? v <= *x : false;
|
||||
}
|
||||
|
||||
template< typename T, typename U >
|
||||
inline bool operator>( optional<T> const & x, U const & v )
|
||||
{
|
||||
return bool(x) ? *x > v : false;
|
||||
}
|
||||
|
||||
template< typename T, typename U >
|
||||
inline bool operator>( U const & v, optional<T> const & x )
|
||||
{
|
||||
return bool(x) ? v > *x : true;
|
||||
}
|
||||
|
||||
template< typename T, typename U >
|
||||
inline bool operator>=( optional<T> const & x, U const & v )
|
||||
{
|
||||
return bool(x) ? *x >= v : false;
|
||||
}
|
||||
|
||||
template< typename T, typename U >
|
||||
inline bool operator>=( U const & v, optional<T> const & x )
|
||||
{
|
||||
return bool(x) ? v >= *x : true;
|
||||
}
|
||||
|
||||
// Specialized algorithms
|
||||
|
||||
template< typename T >
|
||||
void swap( optional<T> & x, optional<T> & y )
|
||||
{
|
||||
x.swap( y );
|
||||
}
|
||||
|
||||
// Convenience function to create an optional.
|
||||
|
||||
template< typename T >
|
||||
inline optional<T> make_optional( T const & v )
|
||||
{
|
||||
return optional<T>( v );
|
||||
}
|
||||
|
||||
} // namespace optional-bare
|
||||
|
||||
using namespace optional_bare;
|
||||
|
||||
} // namespace nonstd
|
||||
|
||||
#endif // optional_USES_STD_OPTIONAL
|
||||
|
||||
#endif // NONSTD_OPTIONAL_BARE_HPP
|
Loading…
Reference in New Issue
Block a user