From 1695ebb8ea218b99898e46098ed131a1e9db2372 Mon Sep 17 00:00:00 2001 From: Carl Pearson Date: Mon, 30 Sep 2019 11:51:04 -0500 Subject: [PATCH] Add -n flag, change --no-aslr to --aslr, add --stdout and --stderr, chown outputs when run with sudo --- README.md | 42 +- include/perfect/result.hpp | 32 + tools/perfect.cpp | 229 +++- tools/thirdparty/nonstd/optional.hpp | 1585 ++++++++++++++++++++++++++ 4 files changed, 1851 insertions(+), 37 deletions(-) create mode 100644 tools/thirdparty/nonstd/optional.hpp diff --git a/README.md b/README.md index 750fd12..dfb383e 100644 --- a/README.md +++ b/README.md @@ -59,7 +59,46 @@ If you don't have CUDA, then you could just do g++ code_using_perfect.cpp -I perfect/include ``` -## Usage +## Tools Usage + +`perfect` provides some useful tools on Linux: + +``` +$ tools/perfect-cli -h +SYNOPSIS + ./tools/perfect-cli --no-mod [-n ] -- ... + ./tools/perfect-cli ([-u ] | [-s ]) [--no-drop-cache] [--no-max-perf] [--aslr] + [--cpu-turbo] [--stdout ] [--stderr ] [-n ] -- ... + +OPTIONS + --no-mod don't control performance + -u number of unshielded CPUs + -s number of shielded CPUs + --no-drop-cache do not drop filesystem caches + --no-max-perf do not max os perf + --aslr enable ASLR + --cpu-turbo enable CPU turbo + --stdout redirect child stdout + --stderr redirect child stderr + -n run multiple times +``` + +The basic usage is `tools/perfect-cli -- my-exe`, which will attempt to configure the system for repeatable performance before executing `my-exe`, and then restore the system to the original performance state before exiting. +Most modifications require elevated privileges. +The default behavior is to: +* disable ASLR +* drop filesystem caches +* set CPU performance to maximum +* disable CPU turbo + +Some options (all should provided before the `--` option): +* `--no-mod` flag will cause `perfect-cli` to not modify the system performance state +* `-n INT` will run the requested program `INT` times. +* `--stderr`/`--stdout` will redirect the program-under-test's stderr and stdout to the provided paths. +* `-s`/`-u`: set the number of shielded /unshielded CPUs. The program-under-test will run on the shielded CPUs. All other tasks will run on the unshielded CPUs. + + +## API Usage The `perfect` functions all return a `perfect::Result`, which is defined in [include/perfect/result.hpp]. When things are working, it will be `perfect::Result::SUCCESS`. @@ -274,3 +313,4 @@ heap: 93824994414192 ## Acks Uses [muellan/clipp](https://github.com/muellan/clipp) for cli option parsing. +Uses [martinmoene/optional-lite](https://github.com/martinmoene/optional-lite). diff --git a/include/perfect/result.hpp b/include/perfect/result.hpp index 1e57014..644432c 100644 --- a/include/perfect/result.hpp +++ b/include/perfect/result.hpp @@ -12,12 +12,17 @@ #include #endif +#ifdef __linux__ +#include +#endif + namespace perfect { enum class Result { NO_PERMISSION, NOT_SUPPORTED, NO_TASK, + NVML_NO_PERMISSION, NVML_NOT_SUPPORTED, NVML_UNINITIALIZED, @@ -39,6 +44,23 @@ Result from_nvml(nvmlReturn_t nvml) { case NVML_ERROR_INVALID_ARGUMENT: case NVML_ERROR_GPU_IS_LOST: case NVML_ERROR_UNKNOWN: + case NVML_ERROR_ALREADY_INITIALIZED: + case NVML_ERROR_NOT_FOUND: + case NVML_ERROR_INSUFFICIENT_SIZE: + case NVML_ERROR_INSUFFICIENT_POWER: + case NVML_ERROR_DRIVER_NOT_LOADED: + case NVML_ERROR_TIMEOUT: + case NVML_ERROR_IRQ_ISSUE: + case NVML_ERROR_LIBRARY_NOT_FOUND: + case NVML_ERROR_FUNCTION_NOT_FOUND: + case NVML_ERROR_CORRUPTED_INFOROM: + case NVML_ERROR_RESET_REQUIRED: + case NVML_ERROR_OPERATING_SYSTEM: + case NVML_ERROR_LIB_RM_VERSION_MISMATCH: + case NVML_ERROR_IN_USE: + case NVML_ERROR_MEMORY: + case NVML_ERROR_NO_DATA: + case NVML_ERROR_VGPU_ECC_NOT_SUPPORTED: default: assert(0 && "unhandled nvmlReturn_t"); } @@ -46,6 +68,16 @@ Result from_nvml(nvmlReturn_t nvml) { } #endif +#ifdef __linux__ +Result from_errno(int err) { + switch (err) { + default: + assert(0 && "unhandled errno"); + } + return Result::UNKNOWN; +} +#endif + const char *get_string(const Result &result) { switch (result) { case Result::SUCCESS: diff --git a/tools/perfect.cpp b/tools/perfect.cpp index 81e51fd..e52199f 100644 --- a/tools/perfect.cpp +++ b/tools/perfect.cpp @@ -4,21 +4,31 @@ #include #include +#ifdef __linux__ +#include +#include +#include #include #include #include +#else +#error "unsupported platform" +#endif #include "clipp/clipp.h" +#include "nonstd/optional.hpp" #include "perfect/aslr.hpp" #include "perfect/cpu_set.hpp" #include "perfect/cpu_turbo.hpp" -#include "perfect/os_perf.hpp" #include "perfect/detail/os/linux.hpp" #include "perfect/drop_caches.hpp" +#include "perfect/os_perf.hpp" // argv should be null-terminated -int fork_child(char *const *argv) { +// outf and errf are file descriptors to where stdout and stderr should be +// redirected write stdout to out and stderr to err, if not null +int fork_child(char *const *argv, int outf, int errf) { pid_t pid; int status; @@ -30,7 +40,73 @@ int fork_child(char *const *argv) { } else if (pid == 0) { // in the child process - // skip the first argument, which is this program + if (outf > 0) { + std::cerr << "redirecting child stdout to file\n"; + if (dup2(outf, 1)) { + std::cerr << "dup2 error: " << strerror(errno) << "\n"; + /* + + EBADF + oldfd isn't an open file descriptor, or newfd is out of the allowed + range for file descriptors. EBUSY (Linux only) This may be returned by + dup2() or dup3() during a race condition with open(2) and dup(). EINTR The + dup2() or dup3() call was interrupted by a signal; see signal(7). EINVAL + (dup3()) flags contain an invalid value. Or, oldfd was equal to newfd. + EMFILE + The process already has the maximum number of file descriptors open and + tried to open a new one. + */ + } + + if (close(outf)) { + /* + EBADF + The fildes argument is not a valid file descriptor. + EINTR + The close() function was interrupted by a signal. + + The close() function may fail if: + + EIO + An I/O error occurred while reading from or writing to the file + system. + */ + } + } + + if (errf > 0) { + std::cerr << "redirecting child stderr to file\n"; + if (dup2(errf, 2)) { + std::cerr << "dup2 error: " << strerror(errno) << "\n"; + + /* + + EBADF + oldfd isn't an open file descriptor, or newfd is out of the allowed + range for file descriptors. EBUSY (Linux only) This may be returned by + dup2() or dup3() during a race condition with open(2) and dup(). EINTR The + dup2() or dup3() call was interrupted by a signal; see signal(7). EINVAL + (dup3()) flags contain an invalid value. Or, oldfd was equal to newfd. + EMFILE + The process already has the maximum number of file descriptors open and + tried to open a new one. + */ + } + + if (close(errf)) { + /* + EBADF + The fildes argument is not a valid file descriptor. + EINTR + The close() function was interrupted by a signal. + + The close() function may fail if: + + EIO + An I/O error occurred while reading from or writing to the file system. + */ + } + } // the execv() only return if error occured. // The return value is -1 @@ -71,34 +147,104 @@ int main(int argc, char **argv) { size_t numUnshielded = 0; size_t numShielded = 0; bool aslr = false; - bool cpuTurbo = false; - bool maxOsPerf = true; + nonstd::optional cpuTurbo = false; + nonstd::optional maxOsPerf = true; bool dropCaches = true; std::vector program; + std::string stdoutPath; + std::string stderrPath; + int iters = 1; - auto shieldGroup = - ((option("-u") & - value("UNSH", numUnshielded).doc("number of unshielded CPUs")) | - (option("-s") & - value("SH", numShielded).doc("number of shielded CPUs"))); + auto shieldGroup = ((option("-u").doc("number of unshielded CPUs") & + value("INT", numUnshielded)) | + (option("-s").doc("number of shielded CPUs") & + value("INT", numShielded))); + auto noModMode = (option("--no-mod") + .doc("don't control performance") + .set(aslr, true) + .call([&]() { cpuTurbo = nonstd::nullopt; }) + .call([&]() { maxOsPerf = nonstd::nullopt; }) + .set(dropCaches, false)); - auto cli = (shieldGroup, - option("--no-drop-cache").set(dropCaches, false).doc("do not drop filesystem caches"), - option("--no-max-perf").set(maxOsPerf, false).doc("do not max os perf"), - option("--no-aslr").set(aslr, false).doc("disable ASLR"), - option("--cpu-turbo").set(cpuTurbo, true).doc("enable CPU turbo"), + auto modMode = (shieldGroup, + option("--no-drop-cache") + .set(dropCaches, false) + .doc("do not drop filesystem caches"), + option("--no-max-perf").doc("do not max os perf").call([&]() { + maxOsPerf = false; + }), + option("--aslr").set(aslr, true).doc("enable ASLR"), + option("--cpu-turbo").doc("enable CPU turbo").call([&]() { + cpuTurbo = true; + }), + (option("--stdout").doc("redirect child stdout") & + value("PATH", stdoutPath)), + (option("--stderr").doc("redirect child stderr") & + value("PATH", stderrPath))); + + auto cli = ((noModMode | modMode), + (option("-n").doc("run multiple times") & value("INT", iters)), // run everything after "--" required("--") & greedy(values("cmd", program)) ); if (!parse(argc, argv, cli)) { - std::cout << make_man_page(cli, argv[0]); + auto fmt = doc_formatting{}.doc_column(31); + std::cout << make_man_page(cli, argv[0], fmt); return -1; } + // open the redirect files, if needed + int errf = 0; + int outf = 0; + if (!stderrPath.empty()) { + std::cerr << "open " << stderrPath << "\n"; + errf = open(stderrPath.c_str(), O_WRONLY | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (-1 == errf) { + std::cerr << "error while opening " << stderrPath << ": " + << strerror(errno) << "\n"; + } + } + if (!stdoutPath.empty()) { + outf = open(stdoutPath.c_str(), O_WRONLY | O_CREAT, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); + if (-1 == outf) { + std::cerr << "error while opening " << stdoutPath << ": " + << strerror(errno) << "\n"; + } + } + + // if called with sudo, chown the files to whoever called sudo + const char *sudoUser = std::getenv("SUDO_USER"); + if (sudoUser) { + std::cerr << "called with sudo by " << sudoUser << "\n"; + uid_t uid; + gid_t gid; + struct passwd *pwd; + + pwd = getpwnam(sudoUser); + if (pwd == NULL) { + // die("Failed to get uid"); + } + uid = pwd->pw_uid; + gid = pwd->pw_gid; + + if (!stdoutPath.empty()) { + if (chown(stdoutPath.c_str(), uid, gid) == -1) { + // die("chown fail"); + } + } + if (!stderrPath.empty()) { + if (chown(stderrPath.c_str(), uid, gid) == -1) { + // die("chown fail"); + } + } + } + // exec the rest of the options std::vector args; for (auto &c : program) { @@ -154,23 +300,27 @@ int main(int argc, char **argv) { // handle CPU turbo perfect::CpuTurboState cpuTurboState; - PERFECT(perfect::get_cpu_turbo_state(&cpuTurboState)); - if (!cpuTurbo) { - std::cerr << "disabling cpu turbo\n"; - PERFECT(perfect::disable_cpu_turbo()); - } else { - std::cerr << "enabling cpu turbo\n"; - PERFECT(perfect::enable_cpu_turbo()); + if (cpuTurbo.has_value()) { + PERFECT(perfect::get_cpu_turbo_state(&cpuTurboState)); + if (false == cpuTurbo) { + std::cerr << "disabling cpu turbo\n"; + PERFECT(perfect::disable_cpu_turbo()); + } else { + std::cerr << "enabling cpu turbo\n"; + PERFECT(perfect::enable_cpu_turbo()); + } } // handle governor perfect::OsPerfState osPerfState; - if (maxOsPerf) { - std::cerr << "set max performance state\n"; + if (maxOsPerf.has_value()) { PERFECT(perfect::get_os_perf_state(osPerfState)); - for (auto cpu : perfect::cpus()) { - PERFECT(perfect::os_perf_state_maximum(cpu)); - } + if (true == maxOsPerf) { + std::cerr << "set max performance state\n"; + for (auto cpu : perfect::cpus()) { + PERFECT(perfect::os_perf_state_maximum(cpu)); + } + } } // handle file system caches @@ -180,13 +330,18 @@ int main(int argc, char **argv) { } // parent should return - std::cerr << "exec "; - for (size_t i = 0; i < args.size() - 1; ++i) { - std::cerr << args[i] << " "; + for (int runIter = 0; runIter < iters; ++runIter) { + std::cerr << "exec "; + for (size_t i = 0; i < args.size() - 1; ++i) { + std::cerr << args[i] << " "; + } + std::cerr << "\n"; + int status = fork_child(args.data(), outf, errf); + if (0 != status) { + std::cerr << "did not terminate successfully\n"; + } + std::cerr << "finished execution\n"; } - std::cerr << "\n"; - int status = fork_child(args.data()); - std::cerr << "finished execution\n"; // clean up CpuSets (if needed) if (numShielded) { @@ -196,13 +351,15 @@ int main(int argc, char **argv) { } // restore original turbo state + if (cpuTurbo.has_value()) { std::cerr << "restore CPU turbo\n"; PERFECT(perfect::set_cpu_turbo_state(cpuTurboState)); + } - if (maxOsPerf) { + if (maxOsPerf.has_value()) { std::cerr << "restore os performance state\n"; PERFECT(perfect::set_os_perf_state(osPerfState)); } - return status; + return 0; } \ No newline at end of file diff --git a/tools/thirdparty/nonstd/optional.hpp b/tools/thirdparty/nonstd/optional.hpp new file mode 100644 index 0000000..175be18 --- /dev/null +++ b/tools/thirdparty/nonstd/optional.hpp @@ -0,0 +1,1585 @@ +// +// Copyright (c) 2014-2018 Martin Moene +// +// https://github.com/martinmoene/optional-lite +// +// 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) + +#pragma once + +#ifndef NONSTD_OPTIONAL_LITE_HPP +#define NONSTD_OPTIONAL_LITE_HPP + +#define optional_lite_MAJOR 3 +#define optional_lite_MINOR 2 +#define optional_lite_PATCH 0 + +#define optional_lite_VERSION \ + optional_STRINGIFY(optional_lite_MAJOR) "." optional_STRINGIFY( \ + optional_lite_MINOR) "." optional_STRINGIFY(optional_lite_PATCH) + +#define optional_STRINGIFY(x) optional_STRINGIFY_(x) +#define optional_STRINGIFY_(x) #x + +// optional-lite 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 defined(__cpp_exceptions) || defined(__EXCEPTIONS) || defined(_CPPUNWIND) +#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_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)) + +// +// in_place: code duplicated in any-lite, expected-lite, optional-lite, +// value-ptr-lite, variant-lite: +// + +#ifndef nonstd_lite_HAVE_IN_PLACE_TYPES +#define nonstd_lite_HAVE_IN_PLACE_TYPES 1 + +// C++17 std::in_place in : + +#if optional_CPP17_OR_GREATER + +#include + +namespace nonstd { + +using std::in_place; +using std::in_place_index; +using std::in_place_index_t; +using std::in_place_t; +using std::in_place_type; +using std::in_place_type_t; + +#define nonstd_lite_in_place_t(T) std::in_place_t +#define nonstd_lite_in_place_type_t(T) std::in_place_type_t +#define nonstd_lite_in_place_index_t(K) std::in_place_index_t + +#define nonstd_lite_in_place(T) \ + std::in_place_t {} +#define nonstd_lite_in_place_type(T) \ + std::in_place_type_t {} +#define nonstd_lite_in_place_index(K) \ + std::in_place_index_t {} + +} // namespace nonstd + +#else // optional_CPP17_OR_GREATER + +#include + +namespace nonstd { +namespace detail { + +template struct in_place_type_tag {}; + +template struct in_place_index_tag {}; + +} // namespace detail + +struct in_place_t {}; + +template +inline in_place_t in_place( + detail::in_place_type_tag /*unused*/ = detail::in_place_type_tag()) { + return in_place_t(); +} + +template +inline in_place_t in_place(detail::in_place_index_tag /*unused*/ = + detail::in_place_index_tag()) { + return in_place_t(); +} + +template +inline in_place_t in_place_type( + detail::in_place_type_tag /*unused*/ = detail::in_place_type_tag()) { + return in_place_t(); +} + +template +inline in_place_t in_place_index(detail::in_place_index_tag /*unused*/ = + detail::in_place_index_tag()) { + return in_place_t(); +} + +// mimic templated typedef: + +#define nonstd_lite_in_place_t(T) \ + nonstd::in_place_t (&)(nonstd::detail::in_place_type_tag) +#define nonstd_lite_in_place_type_t(T) \ + nonstd::in_place_t (&)(nonstd::detail::in_place_type_tag) +#define nonstd_lite_in_place_index_t(K) \ + nonstd::in_place_t (&)(nonstd::detail::in_place_index_tag) + +#define nonstd_lite_in_place(T) nonstd::in_place_type +#define nonstd_lite_in_place_type(T) nonstd::in_place_type +#define nonstd_lite_in_place_index(K) nonstd::in_place_index + +} // namespace nonstd + +#endif // optional_CPP17_OR_GREATER +#endif // nonstd_lite_HAVE_IN_PLACE_TYPES + +// +// Using std::optional: +// + +#if optional_USES_STD_OPTIONAL + +#include + +namespace nonstd { + +using std::bad_optional_access; +using std::hash; +using std::optional; + +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; +} // namespace nonstd + +#else // optional_USES_STD_OPTIONAL + +#include +#include + +// optional-lite alignment configuration: + +#ifndef optional_CONFIG_MAX_ALIGN_HACK +#define optional_CONFIG_MAX_ALIGN_HACK 0 +#endif + +#ifndef optional_CONFIG_ALIGN_AS +// no default, used in #if defined() +#endif + +#ifndef optional_CONFIG_ALIGN_AS_FALLBACK +#define optional_CONFIG_ALIGN_AS_FALLBACK double +#endif + +// Compiler warning suppression: + +#if defined(__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wundef" +#elif defined(__GNUC__) +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wundef" +#elif defined(_MSC_VER) +#pragma warning(push) +#endif + +// half-open range [lo..hi): +#define optional_BETWEEN(v, lo, hi) ((lo) <= (v) && (v) < (hi)) + +// Compiler versions: +// +// MSVC++ 6.0 _MSC_VER == 1200 (Visual Studio 6.0) +// MSVC++ 7.0 _MSC_VER == 1300 (Visual Studio .NET 2002) +// MSVC++ 7.1 _MSC_VER == 1310 (Visual Studio .NET 2003) +// MSVC++ 8.0 _MSC_VER == 1400 (Visual Studio 2005) +// MSVC++ 9.0 _MSC_VER == 1500 (Visual Studio 2008) +// MSVC++ 10.0 _MSC_VER == 1600 (Visual Studio 2010) +// MSVC++ 11.0 _MSC_VER == 1700 (Visual Studio 2012) +// MSVC++ 12.0 _MSC_VER == 1800 (Visual Studio 2013) +// MSVC++ 14.0 _MSC_VER == 1900 (Visual Studio 2015) +// MSVC++ 14.1 _MSC_VER >= 1910 (Visual Studio 2017) + +#if defined(_MSC_VER) && !defined(__clang__) +#define optional_COMPILER_MSVC_VER (_MSC_VER) +#define optional_COMPILER_MSVC_VERSION \ + (_MSC_VER / 10 - 10 * (5 + (_MSC_VER < 1900))) +#else +#define optional_COMPILER_MSVC_VER 0 +#define optional_COMPILER_MSVC_VERSION 0 +#endif + +#define optional_COMPILER_VERSION(major, minor, patch) \ + (10 * (10 * (major) + (minor)) + (patch)) + +#if defined(__GNUC__) && !defined(__clang__) +#define optional_COMPILER_GNUC_VERSION \ + optional_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) +#else +#define optional_COMPILER_GNUC_VERSION 0 +#endif + +#if defined(__clang__) +#define optional_COMPILER_CLANG_VERSION \ + optional_COMPILER_VERSION(__clang_major__, __clang_minor__, \ + __clang_patchlevel__) +#else +#define optional_COMPILER_CLANG_VERSION 0 +#endif + +#if optional_BETWEEN(optional_COMPILER_MSVC_VERSION, 70, 140) +#pragma warning(disable : 4345) // initialization behavior changed +#endif + +#if optional_BETWEEN(optional_COMPILER_MSVC_VERSION, 70, 150) +#pragma warning(disable : 4814) // in C++14 'constexpr' will not imply 'const' +#endif + +// Presence of language and library features: + +#define optional_HAVE(FEATURE) (optional_HAVE_##FEATURE) + +#ifdef _HAS_CPP0X +#define optional_HAS_CPP0X _HAS_CPP0X +#else +#define optional_HAS_CPP0X 0 +#endif + +// Unless defined otherwise below, consider VC14 as C++11 for optional-lite: + +#if optional_COMPILER_MSVC_VER >= 1900 +#undef optional_CPP11_OR_GREATER +#define optional_CPP11_OR_GREATER 1 +#endif + +#define optional_CPP11_90 \ + (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1500) +#define optional_CPP11_100 \ + (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1600) +#define optional_CPP11_110 \ + (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1700) +#define optional_CPP11_120 \ + (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1800) +#define optional_CPP11_140 \ + (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1900) +#define optional_CPP11_141 \ + (optional_CPP11_OR_GREATER_ || optional_COMPILER_MSVC_VER >= 1910) + +#define optional_CPP14_000 (optional_CPP14_OR_GREATER) +#define optional_CPP17_000 (optional_CPP17_OR_GREATER) + +// Presence of C++11 language features: + +#define optional_HAVE_CONSTEXPR_11 optional_CPP11_140 +#define optional_HAVE_IS_DEFAULT optional_CPP11_140 +#define optional_HAVE_NOEXCEPT optional_CPP11_140 +#define optional_HAVE_NULLPTR optional_CPP11_100 +#define optional_HAVE_REF_QUALIFIER optional_CPP11_140 +#define optional_HAVE_INITIALIZER_LIST optional_CPP11_140 + +// Presence of C++14 language features: + +#define optional_HAVE_CONSTEXPR_14 optional_CPP14_000 + +// Presence of C++17 language features: + +#define optional_HAVE_NODISCARD optional_CPP17_000 + +// Presence of C++ library features: + +#define optional_HAVE_CONDITIONAL optional_CPP11_120 +#define optional_HAVE_REMOVE_CV optional_CPP11_120 +#define optional_HAVE_TYPE_TRAITS optional_CPP11_90 + +#define optional_HAVE_TR1_TYPE_TRAITS (!!optional_COMPILER_GNUC_VERSION) +#define optional_HAVE_TR1_ADD_POINTER (!!optional_COMPILER_GNUC_VERSION) + +// C++ feature usage: + +#if optional_HAVE(CONSTEXPR_11) +#define optional_constexpr constexpr +#else +#define optional_constexpr /*constexpr*/ +#endif + +#if optional_HAVE(IS_DEFAULT) +#define optional_is_default = default; +#else +#define optional_is_default \ + {} +#endif + +#if optional_HAVE(CONSTEXPR_14) +#define optional_constexpr14 constexpr +#else +#define optional_constexpr14 /*constexpr*/ +#endif + +#if optional_HAVE(NODISCARD) +#define optional_nodiscard [[nodiscard]] +#else +#define optional_nodiscard /*[[nodiscard]]*/ +#endif + +#if optional_HAVE(NOEXCEPT) +#define optional_noexcept noexcept +#else +#define optional_noexcept /*noexcept*/ +#endif + +#if optional_HAVE(NULLPTR) +#define optional_nullptr nullptr +#else +#define optional_nullptr NULL +#endif + +#if optional_HAVE(REF_QUALIFIER) +// NOLINTNEXTLINE( bugprone-macro-parentheses ) +#define optional_ref_qual & +#define optional_refref_qual && +#else +#define optional_ref_qual /*&*/ +#define optional_refref_qual /*&&*/ +#endif + +// additional includes: + +#if optional_CONFIG_NO_EXCEPTIONS +// already included: +#else +#include +#endif + +#if optional_CPP11_OR_GREATER +#include +#endif + +#if optional_HAVE(INITIALIZER_LIST) +#include +#endif + +#if optional_HAVE(TYPE_TRAITS) +#include +#elif optional_HAVE(TR1_TYPE_TRAITS) +#include +#endif + +// Method enabling + +#if optional_CPP11_OR_GREATER + +#define optional_REQUIRES_0(...) \ + template ::type = 0> + +#define optional_REQUIRES_T(...) \ + , typename = \ + typename std::enable_if<(__VA_ARGS__), \ + nonstd::optional_lite::detail::enabler>::type + +#define optional_REQUIRES_R(R, ...) \ + typename std::enable_if<(__VA_ARGS__), R>::type + +#define optional_REQUIRES_A(...) \ + , typename std::enable_if<(__VA_ARGS__), void *>::type = nullptr + +#endif + +// +// optional: +// + +namespace nonstd { +namespace optional_lite { + +namespace std11 { + +#if optional_CPP11_OR_GREATER +using std::move; +#else +template T &move(T &t) { return t; } +#endif + +#if optional_HAVE(CONDITIONAL) +using std::conditional; +#else +template struct conditional { + typedef T type; +}; +template struct conditional { + typedef F type; +}; +#endif // optional_HAVE_CONDITIONAL + +} // namespace std11 + +#if optional_CPP11_OR_GREATER + +/// type traits C++17: + +namespace std17 { + +#if optional_CPP17_OR_GREATER + +using std::is_nothrow_swappable; +using std::is_swappable; + +#elif optional_CPP11_OR_GREATER + +namespace detail { + +using std::swap; + +struct is_swappable { + template (), std::declval()))> + static std::true_type test(int /*unused*/); + + template static std::false_type test(...); +}; + +struct is_nothrow_swappable { + // wrap noexcept(expr) in separate function as work-around for VC140 (VS2015): + + template static constexpr bool satisfies() { + return noexcept(swap(std::declval(), std::declval())); + } + + template + static auto test(int /*unused*/) + -> std::integral_constant()> {} + + template static auto test(...) -> std::false_type; +}; + +} // namespace detail + +// is [nothow] swappable: + +template +struct is_swappable : decltype(detail::is_swappable::test(0)) {}; + +template +struct is_nothrow_swappable + : decltype(detail::is_nothrow_swappable::test(0)) {}; + +#endif // optional_CPP17_OR_GREATER + +} // namespace std17 + +/// type traits C++20: + +namespace std20 { + +template struct remove_cvref { + typedef typename std::remove_cv::type>::type + type; +}; + +} // namespace std20 + +#endif // optional_CPP11_OR_GREATER + +/// class optional + +template class optional; + +namespace detail { + +// for optional_REQUIRES_T + +#if optional_CPP11_OR_GREATER +enum class enabler {}; +#endif + +// C++11 emulation: + +struct nulltype {}; + +template struct typelist { + typedef Head head; + typedef Tail tail; +}; + +#if optional_CONFIG_MAX_ALIGN_HACK + +// Max align, use most restricted type for alignment: + +#define optional_UNIQUE(name) optional_UNIQUE2(name, __LINE__) +#define optional_UNIQUE2(name, line) optional_UNIQUE3(name, line) +#define optional_UNIQUE3(name, line) name##line + +#define optional_ALIGN_TYPE(type) \ + type optional_UNIQUE(_t); \ + struct_t optional_UNIQUE(_st) + +template struct struct_t { T _; }; + +union max_align_t { + optional_ALIGN_TYPE(char); + optional_ALIGN_TYPE(short int); + optional_ALIGN_TYPE(int); + optional_ALIGN_TYPE(long int); + optional_ALIGN_TYPE(float); + optional_ALIGN_TYPE(double); + optional_ALIGN_TYPE(long double); + optional_ALIGN_TYPE(char *); + optional_ALIGN_TYPE(short int *); + optional_ALIGN_TYPE(int *); + optional_ALIGN_TYPE(long int *); + optional_ALIGN_TYPE(float *); + optional_ALIGN_TYPE(double *); + optional_ALIGN_TYPE(long double *); + optional_ALIGN_TYPE(void *); + +#ifdef HAVE_LONG_LONG + optional_ALIGN_TYPE(long long); +#endif + + struct Unknown; + + Unknown (*optional_UNIQUE(_))(Unknown); + Unknown *Unknown::*optional_UNIQUE(_); + Unknown (Unknown::*optional_UNIQUE(_))(Unknown); + + struct_t optional_UNIQUE(_); + struct_t optional_UNIQUE(_); + struct_t optional_UNIQUE(_); +}; + +#undef optional_UNIQUE +#undef optional_UNIQUE2 +#undef optional_UNIQUE3 + +#undef optional_ALIGN_TYPE + +#elif defined(optional_CONFIG_ALIGN_AS) // optional_CONFIG_MAX_ALIGN_HACK + +// Use user-specified type for alignment: + +#define optional_ALIGN_AS(unused) optional_CONFIG_ALIGN_AS + +#else // optional_CONFIG_MAX_ALIGN_HACK + +// Determine POD type to use for alignment: + +#define optional_ALIGN_AS(to_align) \ + typename type_of_size::value>::type + +template struct alignment_of; + +template struct alignment_of_hack { + char c; + T t; + alignment_of_hack(); +}; + +template struct alignment_logic { + enum { value = A < S ? A : S }; +}; + +template struct alignment_of { + enum { + value = alignment_logic) - sizeof(T), + sizeof(T)>::value + }; +}; + +template struct type_of_size { + typedef typename std11::conditional< + N == sizeof(typename List::head), typename List::head, + typename type_of_size::type>::type type; +}; + +template struct type_of_size { + typedef optional_CONFIG_ALIGN_AS_FALLBACK type; +}; + +template struct struct_t { T _; }; + +#define optional_ALIGN_TYPE(type) typelist < type, typelist < struct_t + +struct Unknown; + +typedef optional_ALIGN_TYPE(char), optional_ALIGN_TYPE(short), + optional_ALIGN_TYPE(int), optional_ALIGN_TYPE(long), + optional_ALIGN_TYPE(float), optional_ALIGN_TYPE(double), + optional_ALIGN_TYPE(long double), + + optional_ALIGN_TYPE(char *), optional_ALIGN_TYPE(short *), + optional_ALIGN_TYPE(int *), optional_ALIGN_TYPE(long *), + optional_ALIGN_TYPE(float *), optional_ALIGN_TYPE(double *), + optional_ALIGN_TYPE(long double *), + + optional_ALIGN_TYPE(Unknown (*)(Unknown)), + optional_ALIGN_TYPE(Unknown *Unknown::*), + optional_ALIGN_TYPE(Unknown (Unknown::*)(Unknown)), + + nulltype >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> alignment_types; + +#undef optional_ALIGN_TYPE + +#endif // optional_CONFIG_MAX_ALIGN_HACK + +/// C++03 constructed union to hold value. + +template union storage_t { + // private: + // template< typename > friend class optional; + + typedef T value_type; + + storage_t() optional_is_default + + explicit storage_t(value_type const &v) { + construct_value(v); + } + + void construct_value(value_type const &v) { + ::new (value_ptr()) value_type(v); + } + +#if optional_CPP11_OR_GREATER + + explicit storage_t(value_type &&v) { construct_value(std::move(v)); } + + void construct_value(value_type &&v) { + ::new (value_ptr()) value_type(std::move(v)); + } + + template void emplace(Args &&... args) { + ::new (value_ptr()) value_type(std::forward(args)...); + } + + template + void emplace(std::initializer_list il, Args &&... args) { + ::new (value_ptr()) value_type(il, std::forward(args)...); + } + +#endif + + void destruct_value() { value_ptr()->~T(); } + + optional_nodiscard value_type const *value_ptr() const { + return as(); + } + + value_type *value_ptr() { return as(); } + + optional_nodiscard value_type const &value() const optional_ref_qual { + return *value_ptr(); + } + + value_type &value() optional_ref_qual { return *value_ptr(); } + +#if optional_CPP11_OR_GREATER + + optional_nodiscard value_type const &&value() const optional_refref_qual { + return std::move(value()); + } + + value_type &&value() optional_refref_qual { return std::move(value()); } + +#endif + +#if optional_CPP11_OR_GREATER + + using aligned_storage_t = + typename std::aligned_storage::type; + aligned_storage_t data; + +#elif optional_CONFIG_MAX_ALIGN_HACK + + typedef struct { + unsigned char data[sizeof(value_type)]; + } aligned_storage_t; + + max_align_t hack; + aligned_storage_t data; + +#else + typedef optional_ALIGN_AS(value_type) align_as_type; + + typedef struct { + align_as_type data[1 + (sizeof(value_type) - 1) / sizeof(align_as_type)]; + } aligned_storage_t; + aligned_storage_t data; + +#undef optional_ALIGN_AS + +#endif // optional_CONFIG_MAX_ALIGN_HACK + + optional_nodiscard void *ptr() optional_noexcept { return &data; } + + optional_nodiscard void const *ptr() const optional_noexcept { return &data; } + + template optional_nodiscard U *as() { + return reinterpret_cast(ptr()); + } + + template optional_nodiscard U const *as() const { + return reinterpret_cast(ptr()); + } +}; + +} // namespace detail + +/// disengaged state tag + +struct nullopt_t { + struct init {}; + explicit optional_constexpr nullopt_t(init /*unused*/) optional_noexcept {} +}; + +#if optional_HAVE(CONSTEXPR_11) +constexpr nullopt_t nullopt{nullopt_t::init{}}; +#else +// extra parenthesis to prevent the most vexing parse: +const nullopt_t nullopt((nullopt_t::init())); +#endif + +/// 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 + +/// optional + +template class optional { +private: + template friend class optional; + + typedef void (optional::*safe_bool)() const; + +public: + typedef T value_type; + + // x.x.3.1, constructors + + // 1a - default construct + optional_constexpr optional() optional_noexcept : has_value_(false), + contained() {} + + // 1b - construct explicitly empty + // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions ) + optional_constexpr optional(nullopt_t /*unused*/) optional_noexcept + : has_value_(false), + contained() {} + + // 2 - copy-construct + optional_constexpr14 + optional(optional const &other +#if optional_CPP11_OR_GREATER + optional_REQUIRES_A(true || std::is_copy_constructible::value) +#endif + ) + : has_value_(other.has_value()) { + if (other.has_value()) { + contained.construct_value(other.contained.value()); + } + } + +#if optional_CPP11_OR_GREATER + + // 3 (C++11) - move-construct from optional + optional_constexpr14 optional( + optional &&other optional_REQUIRES_A(true || + std::is_move_constructible::value) + // NOLINTNEXTLINE( performance-noexcept-move-constructor ) + ) noexcept(std::is_nothrow_move_constructible::value) + : has_value_(other.has_value()) { + if (other.has_value()) { + contained.construct_value(std::move(other.contained.value())); + } + } + + // 4a (C++11) - explicit converting copy-construct from optional + template + explicit optional(optional const &other optional_REQUIRES_A( + std::is_constructible::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible const &>::value && + !std::is_constructible const &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible const &, T>::value && + !std::is_convertible const &&, T>::value && + !std::is_convertible::value /*=> explicit */ + )) + : has_value_(other.has_value()) { + if (other.has_value()) { + contained.construct_value(T{other.contained.value()}); + } + } +#endif // optional_CPP11_OR_GREATER + + // 4b (C++98 and later) - non-explicit converting copy-construct from optional + template + // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions ) + optional(optional const &other +#if optional_CPP11_OR_GREATER + optional_REQUIRES_A( + std::is_constructible::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible const &>::value && + !std::is_constructible const &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible const &, T>::value && + !std::is_convertible const &&, T>::value && + std::is_convertible::value /*=> non-explicit */ + ) +#endif // optional_CPP11_OR_GREATER + ) + : has_value_(other.has_value()) { + if (other.has_value()) { + contained.construct_value(other.contained.value()); + } + } + +#if optional_CPP11_OR_GREATER + + // 5a (C++11) - explicit converting move-construct from optional + template + explicit optional(optional &&other optional_REQUIRES_A( + std::is_constructible::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible const &>::value && + !std::is_constructible const &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible const &, T>::value && + !std::is_convertible const &&, T>::value && + !std::is_convertible::value /*=> explicit */ + )) + : has_value_(other.has_value()) { + if (other.has_value()) { + contained.construct_value(T{std::move(other.contained.value())}); + } + } + + // 5a (C++11) - non-explicit converting move-construct from optional + template + // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions ) + optional(optional &&other optional_REQUIRES_A( + std::is_constructible::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible const &>::value && + !std::is_constructible const &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible const &, T>::value && + !std::is_convertible const &&, T>::value && + std::is_convertible::value /*=> non-explicit */ + )) + : has_value_(other.has_value()) { + if (other.has_value()) { + contained.construct_value(std::move(other.contained.value())); + } + } + + // 6 (C++11) - in-place construct + template ::value)> + optional_constexpr explicit optional(nonstd_lite_in_place_t(T), + Args &&... args) + : has_value_(true), contained(T(std::forward(args)...)) {} + + // 7 (C++11) - in-place construct, initializer-list + template &, + Args &&...>::value)> + optional_constexpr explicit optional(nonstd_lite_in_place_t(T), + std::initializer_list il, + Args &&... args) + : has_value_(true), contained(T(il, std::forward(args)...)) {} + + // 8a (C++11) - explicit move construct from value + template + optional_constexpr explicit optional(U &&value optional_REQUIRES_A( + std::is_constructible::value && + !std::is_same::type, + nonstd_lite_in_place_t(U)>::value && + !std::is_same::type, + optional>::value && + !std::is_convertible::value /*=> explicit */ + )) + : has_value_(true), contained(T{std::forward(value)}) {} + + // 8b (C++11) - non-explicit move construct from value + template + // NOLINTNEXTLINE( google-explicit-constructor, hicpp-explicit-conversions ) + optional_constexpr optional(U &&value optional_REQUIRES_A( + std::is_constructible::value && + !std::is_same::type, + nonstd_lite_in_place_t(U)>::value && + !std::is_same::type, + optional>::value && + std::is_convertible::value /*=> non-explicit */ + )) + : has_value_(true), contained(std::forward(value)) {} + +#else // optional_CPP11_OR_GREATER + + // 8 (C++98) + optional(value_type const &value) : has_value_(true), contained(value) {} + +#endif // optional_CPP11_OR_GREATER + + // x.x.3.2, destructor + + ~optional() { + if (has_value()) { + contained.destruct_value(); + } + } + + // x.x.3.3, assignment + + // 1 (C++98and later) - assign explicitly empty + optional &operator=(nullopt_t /*unused*/) optional_noexcept { + reset(); + return *this; + } + + // 2 (C++98and later) - copy-assign from optional +#if optional_CPP11_OR_GREATER + // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, + // misc-unconventional-assign-operator ) + optional_REQUIRES_R(optional &, true + // std::is_copy_constructible::value + // && std::is_copy_assignable::value + ) + operator=(optional const &other) noexcept( + std::is_nothrow_move_assignable::value + &&std::is_nothrow_move_constructible::value) +#else + optional &operator=(optional const &other) +#endif + { + if ((has_value() == true) && (other.has_value() == false)) { + reset(); + } else if ((has_value() == false) && (other.has_value() == true)) { + initialize(*other); + } else if ((has_value() == true) && (other.has_value() == true)) { + contained.value() = *other; + } + return *this; + } + +#if optional_CPP11_OR_GREATER + + // 3 (C++11) - move-assign from optional + // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, + // misc-unconventional-assign-operator ) + optional_REQUIRES_R(optional &, true + // std::is_move_constructible::value + // && std::is_move_assignable::value + ) + operator=(optional &&other) noexcept { + if ((has_value() == true) && (other.has_value() == false)) { + reset(); + } else if ((has_value() == false) && (other.has_value() == true)) { + initialize(std::move(*other)); + } else if ((has_value() == true) && (other.has_value() == true)) { + contained.value() = std::move(*other); + } + return *this; + } + + // 4 (C++11) - move-assign from value + template + // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, + // misc-unconventional-assign-operator ) + optional_REQUIRES_R( + optional &, + std::is_constructible::value &&std::is_assignable::value && + !std::is_same::type, + nonstd_lite_in_place_t(U)>::value && + !std::is_same::type, + optional>::value && + !(std::is_scalar::value && + std::is_same::type>::value)) + operator=(U &&value) { + if (has_value()) { + contained.value() = std::forward(value); + } else { + initialize(T(std::forward(value))); + } + return *this; + } + +#else // optional_CPP11_OR_GREATER + + // 4 (C++98) - copy-assign from value + template optional &operator=(U const &value) { + if (has_value()) + contained.value() = value; + else + initialize(T(value)); + return *this; + } + +#endif // optional_CPP11_OR_GREATER + + // 5 (C++98 and later) - converting copy-assign from optional + template +#if optional_CPP11_OR_GREATER + // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, + // misc-unconventional-assign-operator ) + optional_REQUIRES_R( + optional &, std::is_constructible::value + &&std::is_assignable::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible const &>::value && + !std::is_constructible const &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible const &, T>::value && + !std::is_convertible const &&, T>::value && + !std::is_assignable &>::value && + !std::is_assignable &&>::value && + !std::is_assignable const &>::value && + !std::is_assignable const &&>::value) +#else + optional & +#endif // optional_CPP11_OR_GREATER + operator=(optional const &other) { + return *this = optional(other); + } + +#if optional_CPP11_OR_GREATER + + // 6 (C++11) - converting move-assign from optional + template + // NOLINTNEXTLINE( cppcoreguidelines-c-copy-assignment-signature, + // misc-unconventional-assign-operator ) + optional_REQUIRES_R( + optional &, + std::is_constructible::value &&std::is_assignable::value && + !std::is_constructible &>::value && + !std::is_constructible &&>::value && + !std::is_constructible const &>::value && + !std::is_constructible const &&>::value && + !std::is_convertible &, T>::value && + !std::is_convertible &&, T>::value && + !std::is_convertible const &, T>::value && + !std::is_convertible const &&, T>::value && + !std::is_assignable &>::value && + !std::is_assignable &&>::value && + !std::is_assignable const &>::value && + !std::is_assignable const &&>::value) + operator=(optional &&other) { + return *this = optional(std::move(other)); + } + + // 7 (C++11) - emplace + template ::value)> + T &emplace(Args &&... args) { + *this = nullopt; + contained.emplace(std::forward(args)...); + has_value_ = true; + return contained.value(); + } + + // 8 (C++11) - emplace, initializer-list + template &, + Args &&...>::value)> + T &emplace(std::initializer_list il, Args &&... args) { + *this = nullopt; + contained.emplace(il, std::forward(args)...); + has_value_ = true; + return contained.value(); + } + +#endif // optional_CPP11_OR_GREATER + + // x.x.3.4, swap + + void swap(optional &other) +#if optional_CPP11_OR_GREATER + noexcept(std::is_nothrow_move_constructible::value + &&std17::is_nothrow_swappable::value) +#endif + { + using std::swap; + if ((has_value() == true) && (other.has_value() == true)) { + swap(**this, *other); + } else if ((has_value() == false) && (other.has_value() == true)) { + initialize(std11::move(*other)); + other.reset(); + } else if ((has_value() == true) && (other.has_value() == false)) { + other.initialize(std11::move(**this)); + reset(); + } + } + + // x.x.3.5, observers + + optional_constexpr value_type const *operator->() const { + return assert(has_value()), contained.value_ptr(); + } + + optional_constexpr14 value_type *operator->() { + return assert(has_value()), contained.value_ptr(); + } + + optional_constexpr value_type const &operator*() const optional_ref_qual { + return assert(has_value()), contained.value(); + } + + optional_constexpr14 value_type &operator*() optional_ref_qual { + return assert(has_value()), contained.value(); + } + +#if optional_HAVE(REF_QUALIFIER) && \ + (!optional_COMPILER_GNUC_VERSION || optional_COMPILER_GNUC_VERSION >= 490) + + optional_constexpr value_type const &&operator*() const optional_refref_qual { + return std::move(**this); + } + + optional_constexpr14 value_type &&operator*() optional_refref_qual { + return std::move(**this); + } + +#endif + +#if optional_CPP11_OR_GREATER + optional_constexpr explicit operator bool() const optional_noexcept { + return has_value(); + } +#else + optional_constexpr operator safe_bool() const optional_noexcept { + return has_value() ? &optional::this_type_does_not_support_comparisons : 0; + } +#endif + + // NOLINTNEXTLINE( modernize-use-nodiscard ) + /*optional_nodiscard*/ optional_constexpr bool + has_value() const optional_noexcept { + return has_value_; + } + + // NOLINTNEXTLINE( modernize-use-nodiscard ) + /*optional_nodiscard*/ optional_constexpr14 value_type const & + value() const optional_ref_qual { +#if optional_CONFIG_NO_EXCEPTIONS + assert(has_value()); +#else + if (!has_value()) { + throw bad_optional_access(); + } +#endif + return contained.value(); + } + + optional_constexpr14 value_type &value() optional_ref_qual { +#if optional_CONFIG_NO_EXCEPTIONS + assert(has_value()); +#else + if (!has_value()) { + throw bad_optional_access(); + } +#endif + return contained.value(); + } + +#if optional_HAVE(REF_QUALIFIER) && \ + (!optional_COMPILER_GNUC_VERSION || optional_COMPILER_GNUC_VERSION >= 490) + + // NOLINTNEXTLINE( modernize-use-nodiscard ) + /*optional_nodiscard*/ optional_constexpr value_type const && + value() const optional_refref_qual { + return std::move(value()); + } + + optional_constexpr14 value_type &&value() optional_refref_qual { + return std::move(value()); + } + +#endif + +#if optional_CPP11_OR_GREATER + + template + optional_constexpr value_type value_or(U &&v) const optional_ref_qual { + return has_value() ? contained.value() : static_cast(std::forward(v)); + } + + template + optional_constexpr14 value_type value_or(U &&v) optional_refref_qual { + return has_value() ? std::move(contained.value()) + : static_cast(std::forward(v)); + } + +#else + + template + optional_constexpr value_type value_or(U const &v) const { + return has_value() ? contained.value() : static_cast(v); + } + +#endif // optional_CPP11_OR_GREATER + + // x.x.3.6, modifiers + + void reset() optional_noexcept { + if (has_value()) { + contained.destruct_value(); + } + + has_value_ = false; + } + +private: + void this_type_does_not_support_comparisons() const {} + + template void initialize(V const &value) { + assert(!has_value()); + contained.construct_value(value); + has_value_ = true; + } + +#if optional_CPP11_OR_GREATER + template void initialize(V &&value) { + assert(!has_value()); + contained.construct_value(std::move(value)); + has_value_ = true; + } + +#endif + +private: + bool has_value_; + detail::storage_t contained; +}; + +// Relational operators + +template +inline optional_constexpr bool operator==(optional const &x, + optional const &y) { + return bool(x) != bool(y) ? false : !bool(x) ? true : *x == *y; +} + +template +inline optional_constexpr bool operator!=(optional const &x, + optional const &y) { + return !(x == y); +} + +template +inline optional_constexpr bool operator<(optional const &x, + optional const &y) { + return (!y) ? false : (!x) ? true : *x < *y; +} + +template +inline optional_constexpr bool operator>(optional const &x, + optional const &y) { + return (y < x); +} + +template +inline optional_constexpr bool operator<=(optional const &x, + optional const &y) { + return !(y < x); +} + +template +inline optional_constexpr bool operator>=(optional const &x, + optional const &y) { + return !(x < y); +} + +// Comparison with nullopt + +template +inline optional_constexpr bool +operator==(optional const &x, nullopt_t /*unused*/) optional_noexcept { + return (!x); +} + +template +inline optional_constexpr bool +operator==(nullopt_t /*unused*/, optional const &x) optional_noexcept { + return (!x); +} + +template +inline optional_constexpr bool +operator!=(optional const &x, nullopt_t /*unused*/) optional_noexcept { + return bool(x); +} + +template +inline optional_constexpr bool +operator!=(nullopt_t /*unused*/, optional const &x) optional_noexcept { + return bool(x); +} + +template +inline optional_constexpr bool +operator<(optional const & /*unused*/, + nullopt_t /*unused*/) optional_noexcept { + return false; +} + +template +inline optional_constexpr bool +operator<(nullopt_t /*unused*/, optional const &x) optional_noexcept { + return bool(x); +} + +template +inline optional_constexpr bool +operator<=(optional const &x, nullopt_t /*unused*/) optional_noexcept { + return (!x); +} + +template +inline optional_constexpr bool +operator<=(nullopt_t /*unused*/, + optional const & /*unused*/) optional_noexcept { + return true; +} + +template +inline optional_constexpr bool +operator>(optional const &x, nullopt_t /*unused*/) optional_noexcept { + return bool(x); +} + +template +inline optional_constexpr bool +operator>(nullopt_t /*unused*/, + optional const & /*unused*/) optional_noexcept { + return false; +} + +template +inline optional_constexpr bool +operator>=(optional const & /*unused*/, + nullopt_t /*unused*/) optional_noexcept { + return true; +} + +template +inline optional_constexpr bool +operator>=(nullopt_t /*unused*/, optional const &x) optional_noexcept { + return (!x); +} + +// Comparison with T + +template +inline optional_constexpr bool operator==(optional const &x, U const &v) { + return bool(x) ? *x == v : false; +} + +template +inline optional_constexpr bool operator==(U const &v, optional const &x) { + return bool(x) ? v == *x : false; +} + +template +inline optional_constexpr bool operator!=(optional const &x, U const &v) { + return bool(x) ? *x != v : true; +} + +template +inline optional_constexpr bool operator!=(U const &v, optional const &x) { + return bool(x) ? v != *x : true; +} + +template +inline optional_constexpr bool operator<(optional const &x, U const &v) { + return bool(x) ? *x < v : true; +} + +template +inline optional_constexpr bool operator<(U const &v, optional const &x) { + return bool(x) ? v < *x : false; +} + +template +inline optional_constexpr bool operator<=(optional const &x, U const &v) { + return bool(x) ? *x <= v : true; +} + +template +inline optional_constexpr bool operator<=(U const &v, optional const &x) { + return bool(x) ? v <= *x : false; +} + +template +inline optional_constexpr bool operator>(optional const &x, U const &v) { + return bool(x) ? *x > v : false; +} + +template +inline optional_constexpr bool operator>(U const &v, optional const &x) { + return bool(x) ? v > *x : true; +} + +template +inline optional_constexpr bool operator>=(optional const &x, U const &v) { + return bool(x) ? *x >= v : false; +} + +template +inline optional_constexpr bool operator>=(U const &v, optional const &x) { + return bool(x) ? v >= *x : true; +} + +// Specialized algorithms + +template ::value + &&std17::is_swappable::value) +#endif + > +void swap(optional &x, optional &y) +#if optional_CPP11_OR_GREATER + noexcept(noexcept(x.swap(y))) +#endif +{ + x.swap(y); +} + +#if optional_CPP11_OR_GREATER + +template +optional_constexpr optional::type> +make_optional(T &&value) { + return optional::type>(std::forward(value)); +} + +template +optional_constexpr optional make_optional(Args &&... args) { + return optional(nonstd_lite_in_place(T), std::forward(args)...); +} + +template +optional_constexpr optional make_optional(std::initializer_list il, + Args &&... args) { + return optional(nonstd_lite_in_place(T), il, std::forward(args)...); +} + +#else + +template optional make_optional(T const &value) { + return optional(value); +} + +#endif // optional_CPP11_OR_GREATER + +} // namespace optional_lite + +using optional_lite::nullopt; +using optional_lite::nullopt_t; +using optional_lite::optional; + +#if !optional_CONFIG_NO_EXCEPTIONS +using optional_lite::bad_optional_access; +#endif + +using optional_lite::make_optional; + +} // namespace nonstd + +#if optional_CPP11_OR_GREATER + +// specialize the std::hash algorithm: + +namespace std { + +template struct hash> { +public: + std::size_t operator()(nonstd::optional const &v) const optional_noexcept { + return bool(v) ? std::hash{}(*v) : 0; + } +}; + +} // namespace std + +#endif // optional_CPP11_OR_GREATER + +#if defined(__clang__) +#pragma clang diagnostic pop +#elif defined(__GNUC__) +#pragma GCC diagnostic pop +#elif defined(_MSC_VER) +#pragma warning(pop) +#endif + +#endif // optional_USES_STD_OPTIONAL + +#endif // NONSTD_OPTIONAL_LITE_HPP \ No newline at end of file