From 939eb3996a02c2596fcddbf98ecf70d776c6c5bf Mon Sep 17 00:00:00 2001
From: Antonio SJ Musumeci <trapexit@spawn.link>
Date: Fri, 30 Dec 2022 18:03:13 -0500
Subject: [PATCH] Add option to wait for branches to become new mounts

branches-mount-timeout=UINT64 in seconds (default: 0)
---
 LICENSE                   |    2 +-
 README.md                 |    5 +-
 src/branches.cpp          |   11 +
 src/branches.hpp          |    3 +-
 src/config.cpp            |  135 +-
 src/config.hpp            |    1 +
 src/fs_pathvector.hpp     |   10 +
 src/fs_wait_for_mount.cpp |  112 ++
 src/fs_wait_for_mount.hpp |   50 +
 src/mergerfs.cpp          |   35 +
 src/nonstd/expected.hpp   | 2537 +++++++++++++++++++++++++++++++++++++
 src/nonstd/optional.hpp   |  114 +-
 src/syslog.cpp            |   96 ++
 src/syslog.hpp            |   29 +
 14 files changed, 3034 insertions(+), 106 deletions(-)
 create mode 100644 src/fs_pathvector.hpp
 create mode 100644 src/fs_wait_for_mount.cpp
 create mode 100644 src/fs_wait_for_mount.hpp
 create mode 100644 src/nonstd/expected.hpp
 create mode 100644 src/syslog.cpp
 create mode 100644 src/syslog.hpp

diff --git a/LICENSE b/LICENSE
index b4fdad4f..1b8d3096 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,7 +1,7 @@
 /*
   ISC License
 
-  Copyright (c) 2021, Antonio SJ Musumeci <trapexit@spawn.link>
+  Copyright (c) 2023, Antonio SJ Musumeci <trapexit@spawn.link>
 
   Permission to use, copy, modify, and/or distribute this software for any
   purpose with or without fee is hereby granted, provided that the above
diff --git a/README.md b/README.md
index 0d0f76a0..84b8bf79 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 % mergerfs(1) mergerfs user manual
 % Antonio SJ Musumeci <trapexit@spawn.link>
-% 2023-01-16
+% 2023-01-24
 
 # NAME
 
@@ -165,6 +165,9 @@ These options are the same regardless of whether you use them with the `mergerfs
 * **nfsopenhack=off|git|all**: A workaround for exporting mergerfs
   over NFS where there are issues with creating files for write while
   setting the mode to read-only. (default: off)
+* **branches-mount-timeout=UINT**: Number of seconds to wait at
+  startup for branches to be a mount other than the mountpoint's
+  filesystem. (default: 0)
 * **follow-symlinks=never|directory|regular|all**: Turns symlinks into
   what they point to. (default: never)
 * **link-exdev=passthrough|rel-symlink|abs-base-symlink|abs-pool-symlink**:
diff --git a/src/branches.cpp b/src/branches.cpp
index 99a0eaf8..e8b2e621 100644
--- a/src/branches.cpp
+++ b/src/branches.cpp
@@ -363,6 +363,17 @@ Branches::Impl::to_paths(StrVec &paths_) const
     }
 }
 
+fs::PathVector
+Branches::Impl::to_paths() const
+{
+  fs::PathVector vp;
+
+  for(const auto &branch : *this)
+    vp.emplace_back(branch.path);
+
+  return vp;
+}
+
 int
 Branches::from_string(const std::string &str_)
 {
diff --git a/src/branches.hpp b/src/branches.hpp
index 28731847..1988948a 100644
--- a/src/branches.hpp
+++ b/src/branches.hpp
@@ -19,7 +19,7 @@
 #pragma once
 
 #include "branch.hpp"
-#include "nonstd/optional.hpp"
+#include "fs_pathvector.hpp"
 #include "strvec.hpp"
 #include "tofrom_string.hpp"
 
@@ -49,6 +49,7 @@ public:
   public:
     const uint64_t& minfreespace(void) const;
     void to_paths(StrVec &strvec) const;
+    fs::PathVector to_paths() const;
 
   public:
     Impl& operator=(Impl &impl_);
diff --git a/src/config.cpp b/src/config.cpp
index a9e3793e..93f9000d 100644
--- a/src/config.cpp
+++ b/src/config.cpp
@@ -53,6 +53,7 @@ namespace l
   readonly(const std::string &s_)
   {
     IFERT("async_read");
+    IFERT("branches-mount-timeout");
     IFERT("cache.symlinks");
     IFERT("cache.writeback");
     IFERT("fsname");
@@ -73,6 +74,7 @@ Config::Config()
     auto_cache(false),
     minfreespace(MINFREESPACE_DEFAULT),
     branches(minfreespace),
+    branches_mount_timeout(0),
     cache_attr(1),
     cache_entry(1),
     cache_files(CacheFiles::ENUM::LIBFUSE),
@@ -113,72 +115,73 @@ Config::Config()
     writeback_cache(false),
     xattr(XAttr::ENUM::PASSTHROUGH)
 {
-  _map["async_read"]           = &async_read;
-  _map["auto_cache"]           = &auto_cache;
-  _map["branches"]             = &branches;
-  _map["cache.attr"]           = &cache_attr;
-  _map["cache.entry"]          = &cache_entry;
-  _map["cache.files"]          = &cache_files;
-  _map["cache.negative_entry"] = &cache_negative_entry;
-  _map["cache.readdir"]        = &cache_readdir;
-  _map["cache.statfs"]         = &cache_statfs;
-  _map["cache.symlinks"]       = &cache_symlinks;
-  _map["cache.writeback"]      = &writeback_cache;
-  _map["category.action"]      = &category.action;
-  _map["category.create"]      = &category.create;
-  _map["category.search"]      = &category.search;
-  _map["direct_io"]            = &direct_io;
-  _map["dropcacheonclose"]     = &dropcacheonclose;
-  _map["follow-symlinks"]      = &follow_symlinks;
-  _map["fsname"]               = &fsname;
-  _map["func.access"]          = &func.access;
-  _map["func.chmod"]           = &func.chmod;
-  _map["func.chown"]           = &func.chown;
-  _map["func.create"]          = &func.create;
-  _map["func.getattr"]         = &func.getattr;
-  _map["func.getxattr"]        = &func.getxattr;
-  _map["func.link"]            = &func.link;
-  _map["func.listxattr"]       = &func.listxattr;
-  _map["func.mkdir"]           = &func.mkdir;
-  _map["func.mknod"]           = &func.mknod;
-  _map["func.open"]            = &func.open;
-  _map["func.readlink"]        = &func.readlink;
-  _map["func.removexattr"]     = &func.removexattr;
-  _map["func.rename"]          = &func.rename;
-  _map["func.rmdir"]           = &func.rmdir;
-  _map["func.setxattr"]        = &func.setxattr;
-  _map["func.symlink"]         = &func.symlink;
-  _map["func.truncate"]        = &func.truncate;
-  _map["func.unlink"]          = &func.unlink;
-  _map["func.utimens"]         = &func.utimens;
-  _map["fuse_msg_size"]        = &fuse_msg_size;
-  _map["ignorepponrename"]     = &ignorepponrename;
-  _map["inodecalc"]            = &inodecalc;
-  _map["kernel_cache"]         = &kernel_cache;
-  _map["link_cow"]             = &link_cow;
-  _map["link-exdev"]           = &link_exdev;
-  _map["log.metrics"]          = &log_metrics;
-  _map["minfreespace"]         = &minfreespace;
-  _map["mount"]                = &mount;
-  _map["moveonenospc"]         = &moveonenospc;
-  _map["nfsopenhack"]          = &nfsopenhack;
-  _map["nullrw"]               = &nullrw;
-  _map["pid"]                  = &pid;
-  _map["posix_acl"]            = &posix_acl;
-  //  _map["readdir"]              = &readdir;
-  _map["readdirplus"]          = &readdirplus;
-  _map["rename-exdev"]         = &rename_exdev;
-  _map["security_capability"]  = &security_capability;
-  _map["srcmounts"]            = &srcmounts;
-  _map["statfs"]               = &statfs;
-  _map["statfs_ignore"]        = &statfs_ignore;
-  _map["symlinkify"]           = &symlinkify;
-  _map["symlinkify_timeout"]   = &symlinkify_timeout;
-  _map["threads"]              = &fuse_read_thread_count;
-  _map["read-thread-count"]    = &fuse_read_thread_count;
-  _map["process-thread-count"] = &fuse_process_thread_count;
-  _map["version"]              = &version;
-  _map["xattr"]                = &xattr;
+  _map["async_read"]             = &async_read;
+  _map["auto_cache"]             = &auto_cache;
+  _map["branches"]               = &branches;
+  _map["branches-mount-timeout"] = &branches_mount_timeout;
+  _map["cache.attr"]             = &cache_attr;
+  _map["cache.entry"]            = &cache_entry;
+  _map["cache.files"]            = &cache_files;
+  _map["cache.negative_entry"]   = &cache_negative_entry;
+  _map["cache.readdir"]          = &cache_readdir;
+  _map["cache.statfs"]           = &cache_statfs;
+  _map["cache.symlinks"]         = &cache_symlinks;
+  _map["cache.writeback"]        = &writeback_cache;
+  _map["category.action"]        = &category.action;
+  _map["category.create"]        = &category.create;
+  _map["category.search"]        = &category.search;
+  _map["direct_io"]              = &direct_io;
+  _map["dropcacheonclose"]       = &dropcacheonclose;
+  _map["follow-symlinks"]        = &follow_symlinks;
+  _map["fsname"]                 = &fsname;
+  _map["func.access"]            = &func.access;
+  _map["func.chmod"]             = &func.chmod;
+  _map["func.chown"]             = &func.chown;
+  _map["func.create"]            = &func.create;
+  _map["func.getattr"]           = &func.getattr;
+  _map["func.getxattr"]          = &func.getxattr;
+  _map["func.link"]              = &func.link;
+  _map["func.listxattr"]         = &func.listxattr;
+  _map["func.mkdir"]             = &func.mkdir;
+  _map["func.mknod"]             = &func.mknod;
+  _map["func.open"]              = &func.open;
+  _map["func.readlink"]          = &func.readlink;
+  _map["func.removexattr"]       = &func.removexattr;
+  _map["func.rename"]            = &func.rename;
+  _map["func.rmdir"]             = &func.rmdir;
+  _map["func.setxattr"]          = &func.setxattr;
+  _map["func.symlink"]           = &func.symlink;
+  _map["func.truncate"]          = &func.truncate;
+  _map["func.unlink"]            = &func.unlink;
+  _map["func.utimens"]           = &func.utimens;
+  _map["fuse_msg_size"]          = &fuse_msg_size;
+  _map["ignorepponrename"]       = &ignorepponrename;
+  _map["inodecalc"]              = &inodecalc;
+  _map["kernel_cache"]           = &kernel_cache;
+  _map["link_cow"]               = &link_cow;
+  _map["link-exdev"]             = &link_exdev;
+  _map["log.metrics"]            = &log_metrics;
+  _map["minfreespace"]           = &minfreespace;
+  _map["mount"]                  = &mount;
+  _map["moveonenospc"]           = &moveonenospc;
+  _map["nfsopenhack"]            = &nfsopenhack;
+  _map["nullrw"]                 = &nullrw;
+  _map["pid"]                    = &pid;
+  _map["posix_acl"]              = &posix_acl;
+  //  _map["readdir"]            = &readdir;
+  _map["readdirplus"]            = &readdirplus;
+  _map["rename-exdev"]           = &rename_exdev;
+  _map["security_capability"]    = &security_capability;
+  _map["srcmounts"]              = &srcmounts;
+  _map["statfs"]                 = &statfs;
+  _map["statfs_ignore"]          = &statfs_ignore;
+  _map["symlinkify"]             = &symlinkify;
+  _map["symlinkify_timeout"]     = &symlinkify_timeout;
+  _map["threads"]                = &fuse_read_thread_count;
+  _map["read-thread-count"]      = &fuse_read_thread_count;
+  _map["process-thread-count"]   = &fuse_process_thread_count;
+  _map["version"]                = &version;
+  _map["xattr"]                  = &xattr;
 }
 
 Config&
diff --git a/src/config.hpp b/src/config.hpp
index 5e3a796d..2053d19b 100644
--- a/src/config.hpp
+++ b/src/config.hpp
@@ -103,6 +103,7 @@ public:
   ConfigBOOL     auto_cache;
   ConfigUINT64   minfreespace;
   Branches       branches;
+  ConfigUINT64   branches_mount_timeout;
   ConfigUINT64   cache_attr;
   ConfigUINT64   cache_entry;
   CacheFiles     cache_files;
diff --git a/src/fs_pathvector.hpp b/src/fs_pathvector.hpp
new file mode 100644
index 00000000..ac57bdfd
--- /dev/null
+++ b/src/fs_pathvector.hpp
@@ -0,0 +1,10 @@
+#pragma once
+
+#include "ghc/filesystem.hpp"
+
+#include <vector>
+
+namespace fs
+{
+  typedef std::vector<ghc::filesystem::path> PathVector;
+}
diff --git a/src/fs_wait_for_mount.cpp b/src/fs_wait_for_mount.cpp
new file mode 100644
index 00000000..35efd7d9
--- /dev/null
+++ b/src/fs_wait_for_mount.cpp
@@ -0,0 +1,112 @@
+/*
+  ISC License
+
+  Copyright (c) 2023, Antonio SJ Musumeci <trapexit@spawn.link>
+
+  Permission to use, copy, modify, and/or distribute this software for any
+  purpose with or without fee is hereby granted, provided that the above
+  copyright notice and this permission notice appear in all copies.
+
+  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include "fs_wait_for_mount.hpp"
+
+#include <thread>
+
+constexpr std::chrono::milliseconds SLEEP_DURATION = std::chrono::milliseconds(333);
+
+
+bool
+fs::wait_for_mount(const struct stat               &src_st_,
+                   const ghc::filesystem::path     &tgtpath_,
+                   const std::chrono::milliseconds &timeout_)
+{
+  int rv;
+  std::chrono::duration<double> time_diff;
+  std::chrono::time_point<std::chrono::steady_clock> start_time;
+
+  start_time = std::chrono::steady_clock::now();
+  while(true)
+    {
+      struct stat tgt_st;
+
+      rv = fs::stat(tgtpath_,&tgt_st);
+      if(rv == 0)
+        {
+          if(tgt_st.st_dev != src_st_.st_dev)
+            return true;
+        }
+
+      time_diff = (std::chrono::steady_clock::now() - start_time);
+      if(time_diff > timeout_)
+        return false;
+
+      std::this_thread::sleep_for(SLEEP_DURATION);
+    }
+
+  return false;
+}
+
+static
+void
+_wait_for_mount(const struct stat               &src_st_,
+                const fs::PathVector            &tgtpaths_,
+                const std::chrono::milliseconds &timeout_,
+                fs::PathVector                  &failed_paths_)
+{
+  bool rv;
+  fs::PathVector::const_iterator i;
+  std::chrono::milliseconds timeout;
+  std::chrono::milliseconds diff;
+  std::chrono::time_point<std::chrono::steady_clock> now;
+  std::chrono::time_point<std::chrono::steady_clock> start_time;
+
+  timeout = timeout_;
+  now = start_time = std::chrono::steady_clock::now();
+  for(auto i = tgtpaths_.begin(); i != tgtpaths_.end(); ++i)
+    {
+      diff = std::chrono::duration_cast<std::chrono::milliseconds>(now - start_time);
+      timeout -= diff;
+
+      rv = fs::wait_for_mount(src_st_,*i,timeout);
+      if(rv == false)
+        failed_paths_.push_back(*i);
+
+      now = std::chrono::steady_clock::now();
+    }
+}
+
+void
+fs::wait_for_mount(const struct stat               &src_st_,
+                   const fs::PathVector            &tgtpaths_,
+                   const std::chrono::milliseconds &timeout_,
+                   fs::PathVector                  &failed_paths_)
+{
+  if(tgtpaths_.empty())
+    return;
+
+  _wait_for_mount(src_st_,tgtpaths_,timeout_,failed_paths_);
+}
+
+void
+fs::wait_for_mount(const ghc::filesystem::path     &srcpath_,
+                   const fs::PathVector            &tgtpaths_,
+                   const std::chrono::milliseconds &timeout_,
+                   fs::PathVector                  &failed_paths_)
+{
+  int rv;
+  struct stat src_st;
+
+  rv = fs::stat(srcpath_,&src_st);
+  if(rv == -1)
+    return;
+
+  fs::wait_for_mount(src_st,tgtpaths_,timeout_,failed_paths_);
+}
diff --git a/src/fs_wait_for_mount.hpp b/src/fs_wait_for_mount.hpp
new file mode 100644
index 00000000..d3dfe12b
--- /dev/null
+++ b/src/fs_wait_for_mount.hpp
@@ -0,0 +1,50 @@
+/*
+  ISC License
+
+  Copyright (c) 2023, Antonio SJ Musumeci <trapexit@spawn.link>
+
+  Permission to use, copy, modify, and/or distribute this software for any
+  purpose with or without fee is hereby granted, provided that the above
+  copyright notice and this permission notice appear in all copies.
+
+  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#pragma once
+
+#include "ghc/filesystem.hpp"
+
+#include "fs_pathvector.hpp"
+#include "fs_stat.hpp"
+
+#include <chrono>
+#include <vector>
+
+
+namespace fs
+{
+  typedef std::vector<ghc::filesystem::path> PathVector;
+
+  bool
+  wait_for_mount(const struct stat               &st,
+                 const ghc::filesystem::path     &tgtpath,
+                 const std::chrono::milliseconds &timeout);
+
+  void
+  wait_for_mount(const struct stat               &st,
+                 const fs::PathVector            &tgtpaths,
+                 const std::chrono::milliseconds &timeout,
+                 fs::PathVector                  &failed_paths);
+
+  void
+  wait_for_mount(const ghc::filesystem::path     &srcpath,
+                 const fs::PathVector            &tgtpaths,
+                 const std::chrono::milliseconds &timeout,
+                 fs::PathVector                  &failed_paths);
+}
diff --git a/src/mergerfs.cpp b/src/mergerfs.cpp
index f947c251..52778bf4 100644
--- a/src/mergerfs.cpp
+++ b/src/mergerfs.cpp
@@ -14,6 +14,9 @@
   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
 
+#include "fs_wait_for_mount.hpp"
+#include "syslog.hpp"
+
 #include "fs_path.hpp"
 #include "mergerfs.hpp"
 #include "option_parser.hpp"
@@ -147,6 +150,33 @@ namespace l
     resources::setpriority(prio);
   }
 
+  static
+  void
+  wait_for_mount(const Config::Read &cfg_)
+  {
+    fs::PathVector paths;
+    fs::PathVector failed;
+    std::chrono::milliseconds timeout;
+
+    paths = cfg_->branches->to_paths();
+
+    syslog_info("Waiting %u seconds for branches to mount",
+                (uint64_t)cfg_->branches_mount_timeout);
+
+    timeout = std::chrono::milliseconds(cfg_->branches_mount_timeout * 1000);
+    fs::wait_for_mount((std::string)cfg_->mount,
+                       paths,
+                       timeout,
+                       failed);
+    for(auto &path : failed)
+      syslog_warning("Branch %s was not mounted within timeout",
+                     path.c_str());
+    if(failed.size())
+      syslog_warning("Continuing to mount mergerfs despite %u branches not "
+                     "being different from the mountpoint filesystem",
+                     failed.size());
+  }
+
   int
   main(const int   argc_,
        char      **argv_)
@@ -156,6 +186,8 @@ namespace l
     fuse_args       args;
     fuse_operations ops;
 
+    syslog_open();
+
     memset(&ops,0,sizeof(fuse_operations));
 
     args.argc      = argc_;
@@ -169,6 +201,9 @@ namespace l
         return 1;
       }
 
+    if(cfg->branches_mount_timeout > 0)
+      l::wait_for_mount(cfg);
+
     l::setup_resources();
     l::get_fuse_operations(ops,cfg->nullrw);
 
diff --git a/src/nonstd/expected.hpp b/src/nonstd/expected.hpp
new file mode 100644
index 00000000..eb9e2ba7
--- /dev/null
+++ b/src/nonstd/expected.hpp
@@ -0,0 +1,2537 @@
+// This version targets C++11 and later.
+//
+// Copyright (C) 2016-2020 Martin Moene.
+//
+// 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)
+//
+// expected lite is based on:
+//   A proposal to add a utility class to represent expected monad
+//   by Vicente J. Botet Escriba and Pierre Talbot. http:://wg21.link/p0323
+
+#ifndef NONSTD_EXPECTED_LITE_HPP
+#define NONSTD_EXPECTED_LITE_HPP
+
+#define expected_lite_MAJOR  0
+#define expected_lite_MINOR  6
+#define expected_lite_PATCH  2
+
+#define expected_lite_VERSION  expected_STRINGIFY(expected_lite_MAJOR) "." expected_STRINGIFY(expected_lite_MINOR) "." expected_STRINGIFY(expected_lite_PATCH)
+
+#define expected_STRINGIFY(  x )  expected_STRINGIFY_( x )
+#define expected_STRINGIFY_( x )  #x
+
+// expected-lite configuration:
+
+#define nsel_EXPECTED_DEFAULT  0
+#define nsel_EXPECTED_NONSTD   1
+#define nsel_EXPECTED_STD      2
+
+// tweak header support:
+
+#ifdef __has_include
+# if __has_include(<nonstd/expected.tweak.hpp>)
+#  include <nonstd/expected.tweak.hpp>
+# endif
+#define expected_HAVE_TWEAK_HEADER  1
+#else
+#define expected_HAVE_TWEAK_HEADER  0
+//# pragma message("expected.hpp: Note: Tweak header not supported.")
+#endif
+
+// expected selection and configuration:
+
+#if !defined( nsel_CONFIG_SELECT_EXPECTED )
+# define nsel_CONFIG_SELECT_EXPECTED  ( nsel_HAVE_STD_EXPECTED ? nsel_EXPECTED_STD : nsel_EXPECTED_NONSTD )
+#endif
+
+// Proposal revisions:
+//
+// DXXXXR0: --
+// N4015  : -2 (2014-05-26)
+// N4109  : -1 (2014-06-29)
+// P0323R0:  0 (2016-05-28)
+// P0323R1:  1 (2016-10-12)
+// -------:
+// P0323R2:  2 (2017-06-15)
+// P0323R3:  3 (2017-10-15)
+// P0323R4:  4 (2017-11-26)
+// P0323R5:  5 (2018-02-08)
+// P0323R6:  6 (2018-04-02)
+// P0323R7:  7 (2018-06-22) *
+//
+// expected-lite uses 2 and higher
+
+#ifndef  nsel_P0323R
+# define nsel_P0323R  7
+#endif
+
+// Control presence of C++ exception handling (try and auto discover):
+
+#ifndef nsel_CONFIG_NO_EXCEPTIONS
+# if defined(_MSC_VER)
+#  include <cstddef>    // for _HAS_EXCEPTIONS
+# endif
+# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS)
+#  define nsel_CONFIG_NO_EXCEPTIONS  0
+# else
+#  define nsel_CONFIG_NO_EXCEPTIONS  1
+# endif
+#endif
+
+// at default use SEH with MSVC for no C++ exceptions
+
+#ifndef  nsel_CONFIG_NO_EXCEPTIONS_SEH
+# define nsel_CONFIG_NO_EXCEPTIONS_SEH  ( nsel_CONFIG_NO_EXCEPTIONS && _MSC_VER )
+#endif
+
+// C++ language version detection (C++23 is speculative):
+// Note: VC14.0/1900 (VS2015) lacks too much from C++14.
+
+#ifndef   nsel_CPLUSPLUS
+# if defined(_MSVC_LANG ) && !defined(__clang__)
+#  define nsel_CPLUSPLUS  (_MSC_VER == 1900 ? 201103L : _MSVC_LANG )
+# else
+#  define nsel_CPLUSPLUS  __cplusplus
+# endif
+#endif
+
+#define nsel_CPP98_OR_GREATER  ( nsel_CPLUSPLUS >= 199711L )
+#define nsel_CPP11_OR_GREATER  ( nsel_CPLUSPLUS >= 201103L )
+#define nsel_CPP14_OR_GREATER  ( nsel_CPLUSPLUS >= 201402L )
+#define nsel_CPP17_OR_GREATER  ( nsel_CPLUSPLUS >= 201703L )
+#define nsel_CPP20_OR_GREATER  ( nsel_CPLUSPLUS >= 202002L )
+#define nsel_CPP23_OR_GREATER  ( nsel_CPLUSPLUS >= 202300L )
+
+// Use C++23 std::expected if available and requested:
+
+#if nsel_CPP23_OR_GREATER && defined(__has_include )
+# if __has_include( <expected> )
+#  define nsel_HAVE_STD_EXPECTED  1
+# else
+#  define nsel_HAVE_STD_EXPECTED  0
+# endif
+#else
+# define  nsel_HAVE_STD_EXPECTED  0
+#endif
+
+#define  nsel_USES_STD_EXPECTED  ( (nsel_CONFIG_SELECT_EXPECTED == nsel_EXPECTED_STD) || ((nsel_CONFIG_SELECT_EXPECTED == nsel_EXPECTED_DEFAULT) && nsel_HAVE_STD_EXPECTED) )
+
+//
+// in_place: code duplicated in any-lite, expected-lite, expected-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 <utility>:
+
+#if nsel_CPP17_OR_GREATER
+
+#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;
+
+#define nonstd_lite_in_place_t(      T)  std::in_place_t
+#define nonstd_lite_in_place_type_t( T)  std::in_place_type_t<T>
+#define nonstd_lite_in_place_index_t(K)  std::in_place_index_t<K>
+
+#define nonstd_lite_in_place(      T)    std::in_place_t{}
+#define nonstd_lite_in_place_type( T)    std::in_place_type_t<T>{}
+#define nonstd_lite_in_place_index(K)    std::in_place_index_t<K>{}
+
+} // namespace nonstd
+
+#else // nsel_CPP17_OR_GREATER
+
+#include <cstddef>
+
+namespace nonstd {
+namespace detail {
+
+template< class T >
+struct in_place_type_tag {};
+
+template< std::size_t K >
+struct in_place_index_tag {};
+
+} // namespace detail
+
+struct in_place_t {};
+
+template< class T >
+inline in_place_t in_place( detail::in_place_type_tag<T> = detail::in_place_type_tag<T>() )
+{
+    return in_place_t();
+}
+
+template< std::size_t K >
+inline in_place_t in_place( detail::in_place_index_tag<K> = detail::in_place_index_tag<K>() )
+{
+    return in_place_t();
+}
+
+template< class T >
+inline in_place_t in_place_type( detail::in_place_type_tag<T> = detail::in_place_type_tag<T>() )
+{
+    return in_place_t();
+}
+
+template< std::size_t K >
+inline in_place_t in_place_index( detail::in_place_index_tag<K> = detail::in_place_index_tag<K>() )
+{
+    return in_place_t();
+}
+
+// mimic templated typedef:
+
+#define nonstd_lite_in_place_t(      T)  nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag<T>  )
+#define nonstd_lite_in_place_type_t( T)  nonstd::in_place_t(&)( nonstd::detail::in_place_type_tag<T>  )
+#define nonstd_lite_in_place_index_t(K)  nonstd::in_place_t(&)( nonstd::detail::in_place_index_tag<K> )
+
+#define nonstd_lite_in_place(      T)    nonstd::in_place_type<T>
+#define nonstd_lite_in_place_type( T)    nonstd::in_place_type<T>
+#define nonstd_lite_in_place_index(K)    nonstd::in_place_index<K>
+
+} // namespace nonstd
+
+#endif // nsel_CPP17_OR_GREATER
+#endif // nonstd_lite_HAVE_IN_PLACE_TYPES
+
+//
+// Using std::expected:
+//
+
+#if nsel_USES_STD_EXPECTED
+
+#include <expected>
+
+namespace nonstd {
+
+    using std::expected;
+//  ...
+}
+
+#else // nsel_USES_STD_EXPECTED
+
+#include <cassert>
+#include <exception>
+#include <functional>
+#include <initializer_list>
+#include <memory>
+#include <new>
+#include <system_error>
+#include <type_traits>
+#include <utility>
+
+// additional includes:
+
+#if nsel_CONFIG_NO_EXCEPTIONS
+# if nsel_CONFIG_NO_EXCEPTIONS_SEH
+#  include <windows.h>   // for ExceptionCodes
+# else
+// already included: <cassert>
+# endif
+#else
+# include <stdexcept>
+#endif
+
+// C++ feature usage:
+
+#if nsel_CPP11_OR_GREATER
+# define nsel_constexpr  constexpr
+#else
+# define nsel_constexpr  /*constexpr*/
+#endif
+
+#if nsel_CPP14_OR_GREATER
+# define nsel_constexpr14 constexpr
+#else
+# define nsel_constexpr14 /*constexpr*/
+#endif
+
+#if nsel_CPP17_OR_GREATER
+# define nsel_inline17 inline
+#else
+# define nsel_inline17 /*inline*/
+#endif
+
+// Compiler versions:
+//
+// MSVC++  6.0  _MSC_VER == 1200  nsel_COMPILER_MSVC_VERSION ==  60  (Visual Studio 6.0)
+// MSVC++  7.0  _MSC_VER == 1300  nsel_COMPILER_MSVC_VERSION ==  70  (Visual Studio .NET 2002)
+// MSVC++  7.1  _MSC_VER == 1310  nsel_COMPILER_MSVC_VERSION ==  71  (Visual Studio .NET 2003)
+// MSVC++  8.0  _MSC_VER == 1400  nsel_COMPILER_MSVC_VERSION ==  80  (Visual Studio 2005)
+// MSVC++  9.0  _MSC_VER == 1500  nsel_COMPILER_MSVC_VERSION ==  90  (Visual Studio 2008)
+// MSVC++ 10.0  _MSC_VER == 1600  nsel_COMPILER_MSVC_VERSION == 100  (Visual Studio 2010)
+// MSVC++ 11.0  _MSC_VER == 1700  nsel_COMPILER_MSVC_VERSION == 110  (Visual Studio 2012)
+// MSVC++ 12.0  _MSC_VER == 1800  nsel_COMPILER_MSVC_VERSION == 120  (Visual Studio 2013)
+// MSVC++ 14.0  _MSC_VER == 1900  nsel_COMPILER_MSVC_VERSION == 140  (Visual Studio 2015)
+// MSVC++ 14.1  _MSC_VER >= 1910  nsel_COMPILER_MSVC_VERSION == 141  (Visual Studio 2017)
+// MSVC++ 14.2  _MSC_VER >= 1920  nsel_COMPILER_MSVC_VERSION == 142  (Visual Studio 2019)
+
+#if defined(_MSC_VER) && !defined(__clang__)
+# define nsel_COMPILER_MSVC_VER      (_MSC_VER )
+# define nsel_COMPILER_MSVC_VERSION  (_MSC_VER / 10 - 10 * ( 5 + (_MSC_VER < 1900)) )
+#else
+# define nsel_COMPILER_MSVC_VER      0
+# define nsel_COMPILER_MSVC_VERSION  0
+#endif
+
+#define nsel_COMPILER_VERSION( major, minor, patch )  ( 10 * ( 10 * (major) + (minor) ) + (patch) )
+
+#if defined(__clang__)
+# define nsel_COMPILER_CLANG_VERSION  nsel_COMPILER_VERSION(__clang_major__, __clang_minor__, __clang_patchlevel__)
+#else
+# define nsel_COMPILER_CLANG_VERSION  0
+#endif
+
+#if defined(__GNUC__) && !defined(__clang__)
+# define nsel_COMPILER_GNUC_VERSION  nsel_COMPILER_VERSION(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__)
+#else
+# define nsel_COMPILER_GNUC_VERSION  0
+#endif
+
+// half-open range [lo..hi):
+//#define nsel_BETWEEN( v, lo, hi ) ( (lo) <= (v) && (v) < (hi) )
+
+// Method enabling
+
+#define nsel_REQUIRES_0(...) \
+    template< bool B = (__VA_ARGS__), typename std::enable_if<B, int>::type = 0 >
+
+#define nsel_REQUIRES_T(...) \
+    , typename std::enable_if< (__VA_ARGS__), int >::type = 0
+
+#define nsel_REQUIRES_R(R, ...) \
+    typename std::enable_if< (__VA_ARGS__), R>::type
+
+#define nsel_REQUIRES_A(...) \
+    , typename std::enable_if< (__VA_ARGS__), void*>::type = nullptr
+
+// Presence of language and library features:
+
+#ifdef _HAS_CPP0X
+# define nsel_HAS_CPP0X  _HAS_CPP0X
+#else
+# define nsel_HAS_CPP0X  0
+#endif
+
+//#define nsel_CPP11_140  (nsel_CPP11_OR_GREATER || nsel_COMPILER_MSVC_VER >= 1900)
+
+// Clang, GNUC, MSVC warning suppression macros:
+
+#ifdef __clang__
+# pragma clang diagnostic push
+#elif defined  __GNUC__
+# pragma  GCC  diagnostic push
+#endif // __clang__
+
+#if nsel_COMPILER_MSVC_VERSION >= 140
+# pragma warning( push )
+# define nsel_DISABLE_MSVC_WARNINGS(codes)  __pragma( warning(disable: codes) )
+#else
+# define nsel_DISABLE_MSVC_WARNINGS(codes)
+#endif
+
+#ifdef __clang__
+# define nsel_RESTORE_WARNINGS()  _Pragma("clang diagnostic pop")
+#elif defined __GNUC__
+# define nsel_RESTORE_WARNINGS()  _Pragma("GCC diagnostic pop")
+#elif nsel_COMPILER_MSVC_VERSION >= 140
+# define nsel_RESTORE_WARNINGS()  __pragma( warning( pop ) )
+#else
+# define nsel_RESTORE_WARNINGS()
+#endif
+
+// Suppress the following MSVC (GSL) warnings:
+// - C26409: Avoid calling new and delete explicitly, use std::make_unique<T> instead (r.11)
+
+nsel_DISABLE_MSVC_WARNINGS( 26409 )
+
+//
+// expected:
+//
+
+namespace nonstd { namespace expected_lite {
+
+// type traits C++17:
+
+namespace std17 {
+
+#if nsel_CPP17_OR_GREATER
+
+using std::conjunction;
+using std::is_swappable;
+using std::is_nothrow_swappable;
+
+#else // nsel_CPP17_OR_GREATER
+
+namespace detail {
+
+using std::swap;
+
+struct is_swappable
+{
+    template< typename T, typename = decltype( swap( std::declval<T&>(), std::declval<T&>() ) ) >
+    static std::true_type test( int /* unused */);
+
+    template< typename >
+    static std::false_type test(...);
+};
+
+struct is_nothrow_swappable
+{
+    // wrap noexcept(expr) in separate function as work-around for VC140 (VS2015):
+
+    template< typename T >
+    static constexpr bool satisfies()
+    {
+        return noexcept( swap( std::declval<T&>(), std::declval<T&>() ) );
+    }
+
+    template< typename T >
+    static auto test( int ) -> std::integral_constant<bool, satisfies<T>()>{}
+
+    template< typename >
+    static auto test(...) -> std::false_type;
+};
+} // namespace detail
+
+// is [nothow] swappable:
+
+template< typename T >
+struct is_swappable : decltype( detail::is_swappable::test<T>(0) ){};
+
+template< typename T >
+struct is_nothrow_swappable : decltype( detail::is_nothrow_swappable::test<T>(0) ){};
+
+// conjunction:
+
+template< typename... > struct conjunction : std::true_type{};
+template< typename B1 > struct conjunction<B1> : B1{};
+
+template< typename B1, typename... Bn >
+struct conjunction<B1, Bn...> : std::conditional<bool(B1::value), conjunction<Bn...>, B1>::type{};
+
+#endif // nsel_CPP17_OR_GREATER
+
+} // namespace std17
+
+// type traits C++20:
+
+namespace std20 {
+
+#if defined(__cpp_lib_remove_cvref)
+
+using std::remove_cvref;
+
+#else
+
+template< typename T >
+struct remove_cvref
+{
+    typedef typename std::remove_cv< typename std::remove_reference<T>::type >::type type;
+};
+
+#endif
+
+} // namespace std20
+
+// forward declaration:
+
+template< typename T, typename E >
+class expected;
+
+namespace detail {
+
+/// discriminated union to hold value or 'error'.
+
+template< typename T, typename E >
+class storage_t_impl
+{
+    template< typename, typename > friend class nonstd::expected_lite::expected;
+
+public:
+    using value_type = T;
+    using error_type = E;
+
+    // no-op construction
+    storage_t_impl() {}
+    ~storage_t_impl() {}
+
+    explicit storage_t_impl( bool has_value )
+        : m_has_value( has_value )
+    {}
+
+    void construct_value( value_type const & e )
+    {
+        new( &m_value ) value_type( e );
+    }
+
+    void construct_value( value_type && e )
+    {
+        new( &m_value ) value_type( std::move( e ) );
+    }
+
+    template< class... Args >
+    void emplace_value( Args&&... args )
+    {
+        new( &m_value ) value_type( std::forward<Args>(args)...);
+    }
+
+    template< class U, class... Args >
+    void emplace_value( std::initializer_list<U> il, Args&&... args )
+    {
+        new( &m_value ) value_type( il, std::forward<Args>(args)... );
+    }
+
+    void destruct_value()
+    {
+        m_value.~value_type();
+    }
+
+    void construct_error( error_type const & e )
+    {
+        new( &m_error ) error_type( e );
+    }
+
+    void construct_error( error_type && e )
+    {
+        new( &m_error ) error_type( std::move( e ) );
+    }
+
+    template< class... Args >
+    void emplace_error( Args&&... args )
+    {
+        new( &m_error ) error_type( std::forward<Args>(args)...);
+    }
+
+    template< class U, class... Args >
+    void emplace_error( std::initializer_list<U> il, Args&&... args )
+    {
+        new( &m_error ) error_type( il, std::forward<Args>(args)... );
+    }
+
+    void destruct_error()
+    {
+        m_error.~error_type();
+    }
+
+    constexpr value_type const & value() const &
+    {
+        return m_value;
+    }
+
+    value_type & value() &
+    {
+        return m_value;
+    }
+
+    constexpr value_type const && value() const &&
+    {
+        return std::move( m_value );
+    }
+
+    nsel_constexpr14 value_type && value() &&
+    {
+        return std::move( m_value );
+    }
+
+    value_type const * value_ptr() const
+    {
+        return &m_value;
+    }
+
+    value_type * value_ptr()
+    {
+        return &m_value;
+    }
+
+    error_type const & error() const &
+    {
+        return m_error;
+    }
+
+    error_type & error() &
+    {
+        return m_error;
+    }
+
+    constexpr error_type const && error() const &&
+    {
+        return std::move( m_error );
+    }
+
+    nsel_constexpr14 error_type && error() &&
+    {
+        return std::move( m_error );
+    }
+
+    bool has_value() const
+    {
+        return m_has_value;
+    }
+
+    void set_has_value( bool v )
+    {
+        m_has_value = v;
+    }
+
+private:
+    union
+    {
+        value_type m_value;
+        error_type m_error;
+    };
+
+    bool m_has_value = false;
+};
+
+/// discriminated union to hold only 'error'.
+
+template< typename E >
+struct storage_t_impl<void, E>
+{
+    template< typename, typename > friend class nonstd::expected_lite::expected;
+
+public:
+    using value_type = void;
+    using error_type = E;
+
+    // no-op construction
+    storage_t_impl() {}
+    ~storage_t_impl() {}
+
+    explicit storage_t_impl( bool has_value )
+        : m_has_value( has_value )
+    {}
+
+    void construct_error( error_type const & e )
+    {
+        new( &m_error ) error_type( e );
+    }
+
+    void construct_error( error_type && e )
+    {
+        new( &m_error ) error_type( std::move( e ) );
+    }
+
+    template< class... Args >
+    void emplace_error( Args&&... args )
+    {
+        new( &m_error ) error_type( std::forward<Args>(args)...);
+    }
+
+    template< class U, class... Args >
+    void emplace_error( std::initializer_list<U> il, Args&&... args )
+    {
+        new( &m_error ) error_type( il, std::forward<Args>(args)... );
+    }
+
+    void destruct_error()
+    {
+        m_error.~error_type();
+    }
+
+    error_type const & error() const &
+    {
+        return m_error;
+    }
+
+    error_type & error() &
+    {
+        return m_error;
+    }
+
+    constexpr error_type const && error() const &&
+    {
+        return std::move( m_error );
+    }
+
+    nsel_constexpr14 error_type && error() &&
+    {
+        return std::move( m_error );
+    }
+
+    bool has_value() const
+    {
+        return m_has_value;
+    }
+
+    void set_has_value( bool v )
+    {
+        m_has_value = v;
+    }
+
+private:
+    union
+    {
+        char m_dummy;
+        error_type m_error;
+    };
+
+    bool m_has_value = false;
+};
+
+template< typename T, typename E, bool isConstructable, bool isMoveable >
+class storage_t
+{
+public:
+    storage_t() = default;
+    ~storage_t() = default;
+
+    explicit storage_t( bool has_value )
+        : storage_t_impl<T, E>( has_value )
+    {}
+
+    storage_t( storage_t const & other ) = delete;
+    storage_t( storage_t &&      other ) = delete;
+};
+
+template< typename T, typename E >
+class storage_t<T, E, true, true> : public storage_t_impl<T, E>
+{
+public:
+    storage_t() = default;
+    ~storage_t() = default;
+
+    explicit storage_t( bool has_value )
+        : storage_t_impl<T, E>( has_value )
+    {}
+
+    storage_t( storage_t const & other )
+        : storage_t_impl<T, E>( other.has_value() )
+    {
+        if ( this->has_value() ) this->construct_value( other.value() );
+        else                     this->construct_error( other.error() );
+    }
+
+    storage_t(storage_t && other )
+        : storage_t_impl<T, E>( other.has_value() )
+    {
+        if ( this->has_value() ) this->construct_value( std::move( other.value() ) );
+        else                     this->construct_error( std::move( other.error() ) );
+    }
+};
+
+template< typename E >
+class storage_t<void, E, true, true> : public storage_t_impl<void, E>
+{
+public:
+    storage_t() = default;
+    ~storage_t() = default;
+
+    explicit storage_t( bool has_value )
+        : storage_t_impl<void, E>( has_value )
+    {}
+
+    storage_t( storage_t const & other )
+        : storage_t_impl<void, E>( other.has_value() )
+    {
+        if ( this->has_value() ) ;
+        else                     this->construct_error( other.error() );
+    }
+
+    storage_t(storage_t && other )
+        : storage_t_impl<void, E>( other.has_value() )
+    {
+        if ( this->has_value() ) ;
+        else                     this->construct_error( std::move( other.error() ) );
+    }
+};
+
+template< typename T, typename E >
+class storage_t<T, E, true, false> : public storage_t_impl<T, E>
+{
+public:
+    storage_t() = default;
+    ~storage_t() = default;
+
+    explicit storage_t( bool has_value )
+        : storage_t_impl<T, E>( has_value )
+    {}
+
+    storage_t( storage_t const & other )
+        : storage_t_impl<T, E>(other.has_value())
+    {
+        if ( this->has_value() ) this->construct_value( other.value() );
+        else                     this->construct_error( other.error() );
+    }
+
+    storage_t( storage_t && other ) = delete;
+};
+
+template< typename E >
+class storage_t<void, E, true, false> : public storage_t_impl<void, E>
+{
+public:
+    storage_t() = default;
+    ~storage_t() = default;
+
+    explicit storage_t( bool has_value )
+        : storage_t_impl<void, E>( has_value )
+    {}
+
+    storage_t( storage_t const & other )
+        : storage_t_impl<void, E>(other.has_value())
+    {
+        if ( this->has_value() ) ;
+        else                     this->construct_error( other.error() );
+    }
+
+    storage_t( storage_t && other ) = delete;
+};
+
+template< typename T, typename E >
+class storage_t<T, E, false, true> : public storage_t_impl<T, E>
+{
+public:
+    storage_t() = default;
+    ~storage_t() = default;
+
+    explicit storage_t( bool has_value )
+        : storage_t_impl<T, E>( has_value )
+    {}
+
+    storage_t( storage_t const & other ) = delete;
+
+    storage_t( storage_t && other )
+        : storage_t_impl<T, E>( other.has_value() )
+    {
+        if ( this->has_value() ) this->construct_value( std::move( other.value() ) );
+        else                     this->construct_error( std::move( other.error() ) );
+    }
+};
+
+template< typename E >
+class storage_t<void, E, false, true> : public storage_t_impl<void, E>
+{
+public:
+    storage_t() = default;
+    ~storage_t() = default;
+
+    explicit storage_t( bool has_value )
+        : storage_t_impl<void, E>( has_value )
+    {}
+
+    storage_t( storage_t const & other ) = delete;
+
+    storage_t( storage_t && other )
+        : storage_t_impl<void, E>( other.has_value() )
+    {
+        if ( this->has_value() ) ;
+        else                     this->construct_error( std::move( other.error() ) );
+    }
+};
+
+} // namespace detail
+
+/// x.x.5 Unexpected object type; unexpected_type; C++17 and later can also use aliased type unexpected.
+
+#if nsel_P0323R <= 2
+template< typename E = std::exception_ptr >
+class unexpected_type
+#else
+template< typename E >
+class unexpected_type
+#endif // nsel_P0323R
+{
+public:
+    using error_type = E;
+
+    // x.x.5.2.1 Constructors
+
+//  unexpected_type() = delete;
+
+    constexpr unexpected_type( unexpected_type const & ) = default;
+    constexpr unexpected_type( unexpected_type && ) = default;
+
+    template< typename... Args
+        nsel_REQUIRES_T(
+            std::is_constructible<E, Args&&...>::value
+        )
+    >
+    constexpr explicit unexpected_type( nonstd_lite_in_place_t(E), Args &&... args )
+    : m_error( std::forward<Args>( args )...)
+    {}
+
+    template< typename U, typename... Args
+        nsel_REQUIRES_T(
+            std::is_constructible<E, std::initializer_list<U>, Args&&...>::value
+        )
+    >
+    constexpr explicit unexpected_type( nonstd_lite_in_place_t(E), std::initializer_list<U> il, Args &&... args )
+    : m_error( il, std::forward<Args>( args )...)
+    {}
+
+    template< typename E2
+        nsel_REQUIRES_T(
+            std::is_constructible<E,E2>::value
+            && !std::is_same< typename std20::remove_cvref<E2>::type, nonstd_lite_in_place_t(E2) >::value
+            && !std::is_same< typename std20::remove_cvref<E2>::type, unexpected_type >::value
+        )
+    >
+    constexpr explicit unexpected_type( E2 && error )
+    : m_error( std::forward<E2>( error ) )
+    {}
+
+    template< typename E2
+        nsel_REQUIRES_T(
+            std::is_constructible<    E, E2>::value
+            && !std::is_constructible<E, unexpected_type<E2>       &   >::value
+            && !std::is_constructible<E, unexpected_type<E2>           >::value
+            && !std::is_constructible<E, unexpected_type<E2> const &   >::value
+            && !std::is_constructible<E, unexpected_type<E2> const     >::value
+            && !std::is_convertible<     unexpected_type<E2>       &, E>::value
+            && !std::is_convertible<     unexpected_type<E2>        , E>::value
+            && !std::is_convertible<     unexpected_type<E2> const &, E>::value
+            && !std::is_convertible<     unexpected_type<E2> const  , E>::value
+            && !std::is_convertible< E2 const &, E>::value /*=> explicit */
+        )
+    >
+    constexpr explicit unexpected_type( unexpected_type<E2> const & error )
+    : m_error( E{ error.value() } )
+    {}
+
+    template< typename E2
+        nsel_REQUIRES_T(
+            std::is_constructible<    E, E2>::value
+            && !std::is_constructible<E, unexpected_type<E2>       &   >::value
+            && !std::is_constructible<E, unexpected_type<E2>           >::value
+            && !std::is_constructible<E, unexpected_type<E2> const &   >::value
+            && !std::is_constructible<E, unexpected_type<E2> const     >::value
+            && !std::is_convertible<     unexpected_type<E2>       &, E>::value
+            && !std::is_convertible<     unexpected_type<E2>        , E>::value
+            && !std::is_convertible<     unexpected_type<E2> const &, E>::value
+            && !std::is_convertible<     unexpected_type<E2> const  , E>::value
+            &&  std::is_convertible< E2 const &, E>::value /*=> explicit */
+        )
+    >
+    constexpr /*non-explicit*/ unexpected_type( unexpected_type<E2> const & error )
+    : m_error( error.value() )
+    {}
+
+    template< typename E2
+        nsel_REQUIRES_T(
+            std::is_constructible<    E, E2>::value
+            && !std::is_constructible<E, unexpected_type<E2>       &   >::value
+            && !std::is_constructible<E, unexpected_type<E2>           >::value
+            && !std::is_constructible<E, unexpected_type<E2> const &   >::value
+            && !std::is_constructible<E, unexpected_type<E2> const     >::value
+            && !std::is_convertible<     unexpected_type<E2>       &, E>::value
+            && !std::is_convertible<     unexpected_type<E2>        , E>::value
+            && !std::is_convertible<     unexpected_type<E2> const &, E>::value
+            && !std::is_convertible<     unexpected_type<E2> const  , E>::value
+            && !std::is_convertible< E2 const &, E>::value /*=> explicit */
+        )
+    >
+    constexpr explicit unexpected_type( unexpected_type<E2> && error )
+    : m_error( E{ std::move( error.value() ) } )
+    {}
+
+    template< typename E2
+        nsel_REQUIRES_T(
+            std::is_constructible<    E, E2>::value
+            && !std::is_constructible<E, unexpected_type<E2>       &   >::value
+            && !std::is_constructible<E, unexpected_type<E2>           >::value
+            && !std::is_constructible<E, unexpected_type<E2> const &   >::value
+            && !std::is_constructible<E, unexpected_type<E2> const     >::value
+            && !std::is_convertible<     unexpected_type<E2>       &, E>::value
+            && !std::is_convertible<     unexpected_type<E2>        , E>::value
+            && !std::is_convertible<     unexpected_type<E2> const &, E>::value
+            && !std::is_convertible<     unexpected_type<E2> const  , E>::value
+            &&  std::is_convertible< E2 const &, E>::value /*=> non-explicit */
+        )
+    >
+    constexpr /*non-explicit*/ unexpected_type( unexpected_type<E2> && error )
+    : m_error( std::move( error.value() ) )
+    {}
+
+    // x.x.5.2.2 Assignment
+
+    nsel_constexpr14 unexpected_type& operator=( unexpected_type const & ) = default;
+    nsel_constexpr14 unexpected_type& operator=( unexpected_type && ) = default;
+
+    template< typename E2 = E >
+    nsel_constexpr14 unexpected_type & operator=( unexpected_type<E2> const & other )
+    {
+        unexpected_type{ other.value() }.swap( *this );
+        return *this;
+    }
+
+    template< typename E2 = E >
+    nsel_constexpr14 unexpected_type & operator=( unexpected_type<E2> && other )
+    {
+        unexpected_type{ std::move( other.value() ) }.swap( *this );
+        return *this;
+    }
+
+    // x.x.5.2.3 Observers
+
+    nsel_constexpr14 E & value() & noexcept
+    {
+        return m_error;
+    }
+
+    constexpr E const & value() const & noexcept
+    {
+        return m_error;
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+    nsel_constexpr14 E && value() && noexcept
+    {
+        return std::move( m_error );
+    }
+
+    constexpr E const && value() const && noexcept
+    {
+        return std::move( m_error );
+    }
+
+#endif
+
+    // x.x.5.2.4 Swap
+
+    template< typename U=E >
+    nsel_REQUIRES_R( void,
+        std17::is_swappable<U>::value
+    )
+    swap( unexpected_type & other ) noexcept (
+        std17::is_nothrow_swappable<U>::value
+    )
+    {
+        using std::swap;
+        swap( m_error, other.m_error );
+    }
+
+    // TODO: ??? unexpected_type: in-class friend operator==, !=
+
+private:
+    error_type m_error;
+};
+
+#if nsel_CPP17_OR_GREATER
+
+/// template deduction guide:
+
+template< typename E >
+unexpected_type( E ) -> unexpected_type< E >;
+
+#endif
+
+/// class unexpected_type, std::exception_ptr specialization (P0323R2)
+
+#if !nsel_CONFIG_NO_EXCEPTIONS
+#if  nsel_P0323R <= 2
+
+// TODO: Should expected be specialized for particular E types such as exception_ptr and how?
+//       See p0323r7 2.1. Ergonomics, http://wg21.link/p0323
+template<>
+class unexpected_type< std::exception_ptr >
+{
+public:
+    using error_type = std::exception_ptr;
+
+    unexpected_type() = delete;
+
+    ~unexpected_type(){}
+
+    explicit unexpected_type( std::exception_ptr const & error )
+    : m_error( error )
+    {}
+
+    explicit unexpected_type(std::exception_ptr && error )
+    : m_error( std::move( error ) )
+    {}
+
+    template< typename E >
+    explicit unexpected_type( E error )
+    : m_error( std::make_exception_ptr( error ) )
+    {}
+
+    std::exception_ptr const & value() const
+    {
+        return m_error;
+    }
+
+    std::exception_ptr & value()
+    {
+        return m_error;
+    }
+
+private:
+    std::exception_ptr m_error;
+};
+
+#endif // nsel_P0323R
+#endif // !nsel_CONFIG_NO_EXCEPTIONS
+
+/// x.x.4, Unexpected equality operators
+
+template< typename E1, typename E2 >
+constexpr bool operator==( unexpected_type<E1> const & x, unexpected_type<E2> const & y )
+{
+    return x.value() == y.value();
+}
+
+template< typename E1, typename E2 >
+constexpr bool operator!=( unexpected_type<E1> const & x, unexpected_type<E2> const & y )
+{
+    return ! ( x == y );
+}
+
+#if nsel_P0323R <= 2
+
+template< typename E >
+constexpr bool operator<( unexpected_type<E> const & x, unexpected_type<E> const & y )
+{
+    return x.value() < y.value();
+}
+
+template< typename E >
+constexpr bool operator>( unexpected_type<E> const & x, unexpected_type<E> const & y )
+{
+    return ( y < x );
+}
+
+template< typename E >
+constexpr bool operator<=( unexpected_type<E> const & x, unexpected_type<E> const & y )
+{
+    return ! ( y < x  );
+}
+
+template< typename E >
+constexpr bool operator>=( unexpected_type<E> const & x, unexpected_type<E> const & y )
+{
+    return ! ( x < y );
+}
+
+#endif // nsel_P0323R
+
+/// x.x.5 Specialized algorithms
+
+template< typename E
+    nsel_REQUIRES_T(
+        std17::is_swappable<E>::value
+    )
+>
+void swap( unexpected_type<E> & x, unexpected_type<E> & y) noexcept ( noexcept ( x.swap(y) ) )
+{
+    x.swap( y );
+}
+
+#if nsel_P0323R <= 2
+
+// unexpected: relational operators for std::exception_ptr:
+
+inline constexpr bool operator<( unexpected_type<std::exception_ptr> const & /*x*/, unexpected_type<std::exception_ptr> const & /*y*/ )
+{
+    return false;
+}
+
+inline constexpr bool operator>( unexpected_type<std::exception_ptr> const & /*x*/, unexpected_type<std::exception_ptr> const & /*y*/ )
+{
+    return false;
+}
+
+inline constexpr bool operator<=( unexpected_type<std::exception_ptr> const & x, unexpected_type<std::exception_ptr> const & y )
+{
+    return ( x == y );
+}
+
+inline constexpr bool operator>=( unexpected_type<std::exception_ptr> const & x, unexpected_type<std::exception_ptr> const & y )
+{
+    return ( x == y );
+}
+
+#endif // nsel_P0323R
+
+// unexpected: traits
+
+#if nsel_P0323R <= 3
+
+template< typename E>
+struct is_unexpected : std::false_type {};
+
+template< typename E>
+struct is_unexpected< unexpected_type<E> > : std::true_type {};
+
+#endif // nsel_P0323R
+
+// unexpected: factory
+
+// keep make_unexpected() removed in p0323r2 for pre-C++17:
+
+template< typename E>
+nsel_constexpr14 auto
+make_unexpected( E && value ) -> unexpected_type< typename std::decay<E>::type >
+{
+    return unexpected_type< typename std::decay<E>::type >( std::forward<E>(value) );
+}
+
+#if nsel_P0323R <= 3
+
+/*nsel_constexpr14*/ auto inline
+make_unexpected_from_current_exception() -> unexpected_type< std::exception_ptr >
+{
+    return unexpected_type< std::exception_ptr >( std::current_exception() );
+}
+
+#endif // nsel_P0323R
+
+/// x.x.6, x.x.7 expected access error
+
+template< typename E >
+class bad_expected_access;
+
+/// x.x.7 bad_expected_access<void>: expected access error
+
+template <>
+class bad_expected_access< void > : public std::exception
+{
+public:
+    explicit bad_expected_access()
+    : std::exception()
+    {}
+};
+
+/// x.x.6 bad_expected_access: expected access error
+
+#if !nsel_CONFIG_NO_EXCEPTIONS
+
+template< typename E >
+class bad_expected_access : public bad_expected_access< void >
+{
+public:
+    using error_type = E;
+
+    explicit bad_expected_access( error_type error )
+    : m_error( error )
+    {}
+
+    virtual char const * what() const noexcept override
+    {
+        return "bad_expected_access";
+    }
+
+    nsel_constexpr14 error_type & error() &
+    {
+        return m_error;
+    }
+
+    constexpr error_type const & error() const &
+    {
+        return m_error;
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+    nsel_constexpr14 error_type && error() &&
+    {
+        return std::move( m_error );
+    }
+
+    constexpr error_type const && error() const &&
+    {
+        return std::move( m_error );
+    }
+
+#endif
+
+private:
+    error_type m_error;
+};
+
+#endif // nsel_CONFIG_NO_EXCEPTIONS
+
+/// x.x.8 unexpect tag, in_place_unexpected tag: construct an error
+
+struct unexpect_t{};
+using in_place_unexpected_t = unexpect_t;
+
+nsel_inline17 constexpr unexpect_t unexpect{};
+nsel_inline17 constexpr unexpect_t in_place_unexpected{};
+
+/// class error_traits
+
+#if nsel_CONFIG_NO_EXCEPTIONS
+
+namespace detail {
+    inline bool text( char const * /*text*/ ) { return true; }
+}
+
+template< typename Error >
+struct error_traits
+{
+    static void rethrow( Error const & /*e*/ )
+    {
+#if nsel_CONFIG_NO_EXCEPTIONS_SEH
+        RaiseException( EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL );
+#else
+        assert( false && detail::text("throw bad_expected_access<Error>{ e };") );
+#endif
+    }
+};
+
+template<>
+struct error_traits< std::exception_ptr >
+{
+    static void rethrow( std::exception_ptr const & /*e*/ )
+    {
+#if nsel_CONFIG_NO_EXCEPTIONS_SEH
+        RaiseException( EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL );
+#else
+        assert( false && detail::text("throw bad_expected_access<std::exception_ptr>{ e };") );
+#endif
+    }
+};
+
+template<>
+struct error_traits< std::error_code >
+{
+    static void rethrow( std::error_code const & /*e*/ )
+    {
+#if nsel_CONFIG_NO_EXCEPTIONS_SEH
+        RaiseException( EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL );
+#else
+        assert( false && detail::text("throw std::system_error( e );") );
+#endif
+    }
+};
+
+#else // nsel_CONFIG_NO_EXCEPTIONS
+
+template< typename Error >
+struct error_traits
+{
+    static void rethrow( Error const & e )
+    {
+        throw bad_expected_access<Error>{ e };
+    }
+};
+
+template<>
+struct error_traits< std::exception_ptr >
+{
+    static void rethrow( std::exception_ptr const & e )
+    {
+        std::rethrow_exception( e );
+    }
+};
+
+template<>
+struct error_traits< std::error_code >
+{
+    static void rethrow( std::error_code const & e )
+    {
+        throw std::system_error( e );
+    }
+};
+
+#endif // nsel_CONFIG_NO_EXCEPTIONS
+
+} // namespace expected_lite
+
+// provide nonstd::unexpected_type:
+
+using expected_lite::unexpected_type;
+
+namespace expected_lite {
+
+/// class expected
+
+#if nsel_P0323R <= 2
+template< typename T, typename E = std::exception_ptr >
+class expected
+#else
+template< typename T, typename E >
+class expected
+#endif // nsel_P0323R
+{
+private:
+    template< typename, typename > friend class expected;
+
+public:
+    using value_type = T;
+    using error_type = E;
+    using unexpected_type = nonstd::unexpected_type<E>;
+
+    template< typename U >
+    struct rebind
+    {
+        using type = expected<U, error_type>;
+    };
+
+    // x.x.4.1 constructors
+
+    nsel_REQUIRES_0(
+        std::is_default_constructible<T>::value
+    )
+    nsel_constexpr14 expected()
+    : contained( true )
+    {
+        contained.construct_value( value_type() );
+    }
+
+    nsel_constexpr14 expected( expected const & ) = default;
+    nsel_constexpr14 expected( expected &&      ) = default;
+
+    template< typename U, typename G
+        nsel_REQUIRES_T(
+            std::is_constructible<    T, U const &>::value
+            &&  std::is_constructible<E, G const &>::value
+            && !std::is_constructible<T, expected<U, G>       &    >::value
+            && !std::is_constructible<T, expected<U, G>       &&   >::value
+            && !std::is_constructible<T, expected<U, G> const &    >::value
+            && !std::is_constructible<T, expected<U, G> const &&   >::value
+            && !std::is_convertible<     expected<U, G>       & , T>::value
+            && !std::is_convertible<     expected<U, G>       &&, T>::value
+            && !std::is_convertible<     expected<U, G> const & , T>::value
+            && !std::is_convertible<     expected<U, G> const &&, T>::value
+            && (!std::is_convertible<U const &, T>::value || !std::is_convertible<G const &, E>::value ) /*=> explicit */
+        )
+    >
+    nsel_constexpr14 explicit expected( expected<U, G> const & other )
+    : contained( other.has_value() )
+    {
+        if ( has_value() ) contained.construct_value( T{ other.contained.value() } );
+        else               contained.construct_error( E{ other.contained.error() } );
+    }
+
+    template< typename U, typename G
+        nsel_REQUIRES_T(
+            std::is_constructible<    T, U const &>::value
+            &&  std::is_constructible<E, G const &>::value
+            && !std::is_constructible<T, expected<U, G>       &    >::value
+            && !std::is_constructible<T, expected<U, G>       &&   >::value
+            && !std::is_constructible<T, expected<U, G> const &    >::value
+            && !std::is_constructible<T, expected<U, G> const &&   >::value
+            && !std::is_convertible<     expected<U, G>       & , T>::value
+            && !std::is_convertible<     expected<U, G>       &&, T>::value
+            && !std::is_convertible<     expected<U, G> const  &, T>::value
+            && !std::is_convertible<     expected<U, G> const &&, T>::value
+            && !(!std::is_convertible<U const &, T>::value || !std::is_convertible<G const &, E>::value ) /*=> non-explicit */
+        )
+    >
+    nsel_constexpr14 /*non-explicit*/ expected( expected<U, G> const & other )
+    : contained( other.has_value() )
+    {
+        if ( has_value() ) contained.construct_value( other.contained.value() );
+        else               contained.construct_error( other.contained.error() );
+    }
+
+    template< typename U, typename G
+        nsel_REQUIRES_T(
+            std::is_constructible<    T, U>::value
+            &&  std::is_constructible<E, G>::value
+            && !std::is_constructible<T, expected<U, G>       &    >::value
+            && !std::is_constructible<T, expected<U, G>       &&   >::value
+            && !std::is_constructible<T, expected<U, G> const &    >::value
+            && !std::is_constructible<T, expected<U, G> const &&   >::value
+            && !std::is_convertible<     expected<U, G>       & , T>::value
+            && !std::is_convertible<     expected<U, G>       &&, T>::value
+            && !std::is_convertible<     expected<U, G> const & , T>::value
+            && !std::is_convertible<     expected<U, G> const &&, T>::value
+            && (!std::is_convertible<U, T>::value || !std::is_convertible<G, E>::value ) /*=> explicit */
+        )
+    >
+    nsel_constexpr14 explicit expected( expected<U, G> && other )
+    : contained( other.has_value() )
+    {
+        if ( has_value() ) contained.construct_value( T{ std::move( other.contained.value() ) } );
+        else               contained.construct_error( E{ std::move( other.contained.error() ) } );
+    }
+
+    template< typename U, typename G
+        nsel_REQUIRES_T(
+            std::is_constructible<    T, U>::value
+            &&  std::is_constructible<E, G>::value
+            && !std::is_constructible<T, expected<U, G>      &     >::value
+            && !std::is_constructible<T, expected<U, G>      &&    >::value
+            && !std::is_constructible<T, expected<U, G> const &    >::value
+            && !std::is_constructible<T, expected<U, G> const &&   >::value
+            && !std::is_convertible<     expected<U, G>       & , T>::value
+            && !std::is_convertible<     expected<U, G>       &&, T>::value
+            && !std::is_convertible<     expected<U, G> const & , T>::value
+            && !std::is_convertible<     expected<U, G> const &&, T>::value
+            && !(!std::is_convertible<U, T>::value || !std::is_convertible<G, E>::value ) /*=> non-explicit */
+        )
+    >
+    nsel_constexpr14 /*non-explicit*/ expected( expected<U, G> && other )
+    : contained( other.has_value() )
+    {
+        if ( has_value() ) contained.construct_value( std::move( other.contained.value() ) );
+        else               contained.construct_error( std::move( other.contained.error() ) );
+    }
+
+    template< typename U = T
+        nsel_REQUIRES_T(
+            std::is_copy_constructible<U>::value
+        )
+    >
+    nsel_constexpr14 expected( value_type const & value )
+    : contained( true )
+    {
+        contained.construct_value( value );
+    }
+
+    template< typename U = T
+        nsel_REQUIRES_T(
+            std::is_constructible<T,U&&>::value
+            && !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value
+            && !std::is_same<        expected<T,E>     , typename std20::remove_cvref<U>::type>::value
+            && !std::is_same<nonstd::unexpected_type<E>, typename std20::remove_cvref<U>::type>::value
+            && !std::is_convertible<U&&,T>::value /*=> explicit */
+        )
+    >
+    nsel_constexpr14 explicit expected( U && value ) noexcept
+    (
+        std::is_nothrow_move_constructible<U>::value &&
+        std::is_nothrow_move_constructible<E>::value
+    )
+    : contained( true )
+    {
+        contained.construct_value( T{ std::forward<U>( value ) } );
+    }
+
+    template< typename U = T
+        nsel_REQUIRES_T(
+            std::is_constructible<T,U&&>::value
+            && !std::is_same<typename std20::remove_cvref<U>::type, nonstd_lite_in_place_t(U)>::value
+            && !std::is_same<        expected<T,E>     , typename std20::remove_cvref<U>::type>::value
+            && !std::is_same<nonstd::unexpected_type<E>, typename std20::remove_cvref<U>::type>::value
+            &&  std::is_convertible<U&&,T>::value /*=> non-explicit */
+        )
+    >
+    nsel_constexpr14 /*non-explicit*/ expected( U && value ) noexcept
+    (
+        std::is_nothrow_move_constructible<U>::value &&
+        std::is_nothrow_move_constructible<E>::value
+    )
+    : contained( true )
+    {
+        contained.construct_value( std::forward<U>( value ) );
+    }
+
+    // construct error:
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            std::is_constructible<E, G const &   >::value
+            && !std::is_convertible< G const &, E>::value /*=> explicit */
+        )
+    >
+    nsel_constexpr14 explicit expected( nonstd::unexpected_type<G> const & error )
+    : contained( false )
+    {
+        contained.construct_error( E{ error.value() } );
+    }
+
+    template< typename G = E
+            nsel_REQUIRES_T(
+            std::is_constructible<E, G const &   >::value
+            && std::is_convertible<  G const &, E>::value /*=> non-explicit */
+        )
+    >
+    nsel_constexpr14 /*non-explicit*/ expected( nonstd::unexpected_type<G> const & error )
+    : contained( false )
+    {
+        contained.construct_error( error.value() );
+    }
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            std::is_constructible<E, G&&   >::value
+            && !std::is_convertible< G&&, E>::value /*=> explicit */
+        )
+    >
+    nsel_constexpr14 explicit expected( nonstd::unexpected_type<G> && error )
+    : contained( false )
+    {
+        contained.construct_error( E{ std::move( error.value() ) } );
+    }
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            std::is_constructible<E, G&&   >::value
+            && std::is_convertible<  G&&, E>::value /*=> non-explicit */
+        )
+    >
+    nsel_constexpr14 /*non-explicit*/ expected( nonstd::unexpected_type<G> && error )
+    : contained( false )
+    {
+        contained.construct_error( std::move( error.value() ) );
+    }
+
+    // in-place construction, value
+
+    template< typename... Args
+        nsel_REQUIRES_T(
+            std::is_constructible<T, Args&&...>::value
+        )
+    >
+    nsel_constexpr14 explicit expected( nonstd_lite_in_place_t(T), Args&&... args )
+    : contained( true )
+    {
+        contained.emplace_value( std::forward<Args>( args )... );
+    }
+
+    template< typename U, typename... Args
+        nsel_REQUIRES_T(
+            std::is_constructible<T, std::initializer_list<U>, Args&&...>::value
+        )
+    >
+    nsel_constexpr14 explicit expected( nonstd_lite_in_place_t(T), std::initializer_list<U> il, Args&&... args )
+    : contained( true )
+    {
+        contained.emplace_value( il, std::forward<Args>( args )... );
+    }
+
+    // in-place construction, error
+
+    template< typename... Args
+        nsel_REQUIRES_T(
+            std::is_constructible<E, Args&&...>::value
+        )
+    >
+    nsel_constexpr14 explicit expected( unexpect_t, Args&&... args )
+    : contained( false )
+    {
+        contained.emplace_error( std::forward<Args>( args )... );
+    }
+
+    template< typename U, typename... Args
+        nsel_REQUIRES_T(
+            std::is_constructible<E, std::initializer_list<U>, Args&&...>::value
+        )
+    >
+    nsel_constexpr14 explicit expected( unexpect_t, std::initializer_list<U> il, Args&&... args )
+    : contained( false )
+    {
+        contained.emplace_error( il, std::forward<Args>( args )... );
+    }
+
+    // x.x.4.2 destructor
+
+    // TODO: ~expected: triviality
+    // Effects: If T is not cv void and is_trivially_destructible_v<T> is false and bool(*this), calls val.~T(). If is_trivially_destructible_v<E> is false and !bool(*this), calls unexpect.~unexpected<E>().
+    // Remarks: If either T is cv void or is_trivially_destructible_v<T> is true, and is_trivially_destructible_v<E> is true, then this destructor shall be a trivial destructor.
+
+    ~expected()
+    {
+        if ( has_value() ) contained.destruct_value();
+        else               contained.destruct_error();
+    }
+
+    // x.x.4.3 assignment
+
+    expected & operator=( expected const & other )
+    {
+        expected( other ).swap( *this );
+        return *this;
+    }
+
+    expected & operator=( expected && other ) noexcept
+    (
+        std::is_nothrow_move_constructible<   T>::value
+        && std::is_nothrow_move_assignable<   T>::value
+        && std::is_nothrow_move_constructible<E>::value     // added for missing
+        && std::is_nothrow_move_assignable<   E>::value )   //   nothrow above
+    {
+        expected( std::move( other ) ).swap( *this );
+        return *this;
+    }
+
+    template< typename U
+        nsel_REQUIRES_T(
+            !std::is_same<expected<T,E>, typename std20::remove_cvref<U>::type>::value
+            && std17::conjunction<std::is_scalar<T>, std::is_same<T, std::decay<U>> >::value
+            && std::is_constructible<T ,U>::value
+            && std::is_assignable<   T&,U>::value
+            && std::is_nothrow_move_constructible<E>::value )
+    >
+    expected & operator=( U && value )
+    {
+        expected( std::forward<U>( value ) ).swap( *this );
+        return *this;
+    }
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            std::is_constructible<E, G const&>::value &&
+            std::is_copy_constructible<G>::value    // TODO: std::is_nothrow_copy_constructible<G>
+            && std::is_copy_assignable<G>::value
+        )
+    >
+    expected & operator=( nonstd::unexpected_type<G> const & error )
+    {
+        expected( unexpect, error.value() ).swap( *this );
+        return *this;
+    }
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            std::is_constructible<E, G&&>::value &&
+            std::is_move_constructible<G>::value    // TODO: std::is_nothrow_move_constructible<G>
+            && std::is_move_assignable<G>::value
+        )
+    >
+    expected & operator=( nonstd::unexpected_type<G> && error )
+    {
+        expected( unexpect, std::move( error.value() ) ).swap( *this );
+        return *this;
+    }
+
+    template< typename... Args
+        nsel_REQUIRES_T(
+            std::is_nothrow_constructible<T, Args&&...>::value
+        )
+    >
+    value_type & emplace( Args &&... args )
+    {
+        expected( nonstd_lite_in_place(T), std::forward<Args>(args)... ).swap( *this );
+        return value();
+    }
+
+    template< typename U, typename... Args
+        nsel_REQUIRES_T(
+            std::is_nothrow_constructible<T, std::initializer_list<U>&, Args&&...>::value
+        )
+    >
+    value_type & emplace( std::initializer_list<U> il, Args &&... args )
+    {
+        expected( nonstd_lite_in_place(T), il, std::forward<Args>(args)... ).swap( *this );
+        return value();
+    }
+
+    // x.x.4.4 swap
+
+    template< typename U=T, typename G=E >
+    nsel_REQUIRES_R( void,
+        std17::is_swappable<   U>::value
+        && std17::is_swappable<G>::value
+        && ( std::is_move_constructible<U>::value || std::is_move_constructible<G>::value )
+    )
+    swap( expected & other ) noexcept
+    (
+        std::is_nothrow_move_constructible<T>::value && std17::is_nothrow_swappable<T&>::value &&
+        std::is_nothrow_move_constructible<E>::value && std17::is_nothrow_swappable<E&>::value
+    )
+    {
+        using std::swap;
+
+        if      (   bool(*this) &&   bool(other) ) { swap( contained.value(), other.contained.value() ); }
+        else if ( ! bool(*this) && ! bool(other) ) { swap( contained.error(), other.contained.error() ); }
+        else if (   bool(*this) && ! bool(other) ) { error_type t( std::move( other.error() ) );
+                                                     other.contained.destruct_error();
+                                                     other.contained.construct_value( std::move( contained.value() ) );
+                                                     contained.destruct_value();
+                                                     contained.construct_error( std::move( t ) );
+                                                     bool has_value = contained.has_value();
+                                                     bool other_has_value = other.has_value();
+                                                     other.contained.set_has_value(has_value);
+                                                     contained.set_has_value(other_has_value);
+                                                   }
+        else if ( ! bool(*this) &&   bool(other) ) { other.swap( *this ); }
+    }
+
+    // x.x.4.5 observers
+
+    constexpr value_type const * operator ->() const
+    {
+        return assert( has_value() ), contained.value_ptr();
+    }
+
+    value_type * operator ->()
+    {
+        return assert( has_value() ), contained.value_ptr();
+    }
+
+    constexpr value_type const & operator *() const &
+    {
+        return assert( has_value() ), contained.value();
+    }
+
+    value_type & operator *() &
+    {
+        return assert( has_value() ), contained.value();
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+    constexpr value_type const && operator *() const &&
+    {
+        return std::move( ( assert( has_value() ), contained.value() ) );
+    }
+
+    nsel_constexpr14 value_type && operator *() &&
+    {
+        return std::move( ( assert( has_value() ), contained.value() ) );
+    }
+
+#endif
+
+    constexpr explicit operator bool() const noexcept
+    {
+        return has_value();
+    }
+
+    constexpr bool has_value() const noexcept
+    {
+        return contained.has_value();
+    }
+
+    constexpr value_type const & value() const &
+    {
+        return has_value()
+            ? ( contained.value() )
+            : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() );
+    }
+
+    value_type & value() &
+    {
+        return has_value()
+            ? ( contained.value() )
+            : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() );
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+    constexpr value_type const && value() const &&
+    {
+        return std::move( has_value()
+            ? ( contained.value() )
+            : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() ) );
+    }
+
+    nsel_constexpr14 value_type && value() &&
+    {
+        return std::move( has_value()
+            ? ( contained.value() )
+            : ( error_traits<error_type>::rethrow( contained.error() ), contained.value() ) );
+    }
+
+#endif
+
+    constexpr error_type const & error() const &
+    {
+        return assert( ! has_value() ), contained.error();
+    }
+
+    error_type & error() &
+    {
+        return assert( ! has_value() ), contained.error();
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+    constexpr error_type const && error() const &&
+    {
+        return std::move( ( assert( ! has_value() ), contained.error() ) );
+    }
+
+    error_type && error() &&
+    {
+        return std::move( ( assert( ! has_value() ), contained.error() ) );
+    }
+
+#endif
+
+    constexpr unexpected_type get_unexpected() const
+    {
+        return make_unexpected( contained.error() );
+    }
+
+    template< typename Ex >
+    bool has_exception() const
+    {
+        using ContainedEx = typename std::remove_reference< decltype( get_unexpected().value() ) >::type;
+        return ! has_value() && std::is_base_of< Ex, ContainedEx>::value;
+    }
+
+    template< typename U
+        nsel_REQUIRES_T(
+            std::is_copy_constructible< T>::value
+            && std::is_convertible<U&&, T>::value
+        )
+    >
+    value_type value_or( U && v ) const &
+    {
+        return has_value()
+            ? contained.value()
+            : static_cast<T>( std::forward<U>( v ) );
+    }
+
+    template< typename U
+        nsel_REQUIRES_T(
+            std::is_move_constructible< T>::value
+            && std::is_convertible<U&&, T>::value
+        )
+    >
+    value_type value_or( U && v ) &&
+    {
+        return has_value()
+            ? std::move( contained.value() )
+            : static_cast<T>( std::forward<U>( v ) );
+    }
+
+    // unwrap()
+
+//  template <class U, class E>
+//  constexpr expected<U,E> expected<expected<U,E>,E>::unwrap() const&;
+
+//  template <class T, class E>
+//  constexpr expected<T,E> expected<T,E>::unwrap() const&;
+
+//  template <class U, class E>
+//  expected<U,E> expected<expected<U,E>, E>::unwrap() &&;
+
+//  template <class T, class E>
+//  template expected<T,E> expected<T,E>::unwrap() &&;
+
+    // factories
+
+//  template< typename Ex, typename F>
+//  expected<T,E> catch_exception(F&& f);
+
+//  template< typename F>
+//  expected<decltype(func(declval<T>())),E> map(F&& func) ;
+
+//  template< typename F>
+//  'see below' bind(F&& func);
+
+//  template< typename F>
+//  expected<T,E> catch_error(F&& f);
+
+//  template< typename F>
+//  'see below' then(F&& func);
+
+private:
+    detail::storage_t
+    <
+        T
+        ,E
+        , std::is_copy_constructible<T>::value && std::is_copy_constructible<E>::value
+        , std::is_move_constructible<T>::value && std::is_move_constructible<E>::value
+    >
+    contained;
+};
+
+/// class expected, void specialization
+
+template< typename E >
+class expected<void, E>
+{
+private:
+    template< typename, typename > friend class expected;
+
+public:
+    using value_type = void;
+    using error_type = E;
+    using unexpected_type = nonstd::unexpected_type<E>;
+
+    // x.x.4.1 constructors
+
+    constexpr expected() noexcept
+        : contained( true )
+    {}
+
+    nsel_constexpr14 expected( expected const & other ) = default;
+    nsel_constexpr14 expected( expected &&      other ) = default;
+
+    constexpr explicit expected( nonstd_lite_in_place_t(void) )
+        : contained( true )
+    {}
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            !std::is_convertible<G const &, E>::value /*=> explicit */
+        )
+    >
+    nsel_constexpr14 explicit expected( nonstd::unexpected_type<G> const & error )
+        : contained( false )
+    {
+        contained.construct_error( E{ error.value() } );
+    }
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            std::is_convertible<G const &, E>::value /*=> non-explicit */
+        )
+    >
+    nsel_constexpr14 /*non-explicit*/ expected( nonstd::unexpected_type<G> const & error )
+        : contained( false )
+    {
+        contained.construct_error( error.value() );
+    }
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            !std::is_convertible<G&&, E>::value /*=> explicit */
+        )
+    >
+    nsel_constexpr14 explicit expected( nonstd::unexpected_type<G> && error )
+        : contained( false )
+    {
+        contained.construct_error( E{ std::move( error.value() ) } );
+    }
+
+    template< typename G = E
+        nsel_REQUIRES_T(
+            std::is_convertible<G&&, E>::value /*=> non-explicit */
+        )
+    >
+    nsel_constexpr14 /*non-explicit*/ expected( nonstd::unexpected_type<G> && error )
+        : contained( false )
+    {
+        contained.construct_error( std::move( error.value() ) );
+    }
+
+    template< typename... Args
+        nsel_REQUIRES_T(
+            std::is_constructible<E, Args&&...>::value
+        )
+    >
+    nsel_constexpr14 explicit expected( unexpect_t, Args&&... args )
+        : contained( false )
+    {
+        contained.emplace_error( std::forward<Args>( args )... );
+    }
+
+    template< typename U, typename... Args
+        nsel_REQUIRES_T(
+            std::is_constructible<E, std::initializer_list<U>, Args&&...>::value
+        )
+    >
+    nsel_constexpr14 explicit expected( unexpect_t, std::initializer_list<U> il, Args&&... args )
+        : contained( false )
+    {
+        contained.emplace_error( il, std::forward<Args>( args )... );
+    }
+
+    // destructor
+
+    ~expected()
+    {
+        if ( ! has_value() )
+        {
+            contained.destruct_error();
+        }
+    }
+
+    // x.x.4.3 assignment
+
+    expected & operator=( expected const & other )
+    {
+        expected( other ).swap( *this );
+        return *this;
+    }
+
+    expected & operator=( expected && other ) noexcept
+    (
+        std::is_nothrow_move_assignable<E>::value &&
+        std::is_nothrow_move_constructible<E>::value )
+    {
+        expected( std::move( other ) ).swap( *this );
+        return *this;
+    }
+
+    void emplace()
+    {
+        expected().swap( *this );
+    }
+
+    // x.x.4.4 swap
+
+    template< typename G = E >
+    nsel_REQUIRES_R( void,
+        std17::is_swappable<G>::value
+        && std::is_move_constructible<G>::value
+    )
+    swap( expected & other ) noexcept
+    (
+        std::is_nothrow_move_constructible<E>::value && std17::is_nothrow_swappable<E&>::value
+    )
+    {
+        using std::swap;
+
+        if      ( ! bool(*this) && ! bool(other) ) { swap( contained.error(), other.contained.error() ); }
+        else if (   bool(*this) && ! bool(other) ) { contained.construct_error( std::move( other.error() ) );
+                                                     bool has_value = contained.has_value();
+                                                     bool other_has_value = other.has_value();
+                                                     other.contained.set_has_value(has_value);
+                                                     contained.set_has_value(other_has_value);
+                                                     }
+        else if ( ! bool(*this) &&   bool(other) ) { other.swap( *this ); }
+    }
+
+    // x.x.4.5 observers
+
+    constexpr explicit operator bool() const noexcept
+    {
+        return has_value();
+    }
+
+    constexpr bool has_value() const noexcept
+    {
+        return contained.has_value();
+    }
+
+    void value() const
+    {
+        if ( ! has_value() )
+        {
+            error_traits<error_type>::rethrow( contained.error() );
+        }
+    }
+
+    constexpr error_type const & error() const &
+    {
+        return assert( ! has_value() ), contained.error();
+    }
+
+    error_type & error() &
+    {
+        return assert( ! has_value() ), contained.error();
+    }
+
+#if !nsel_COMPILER_GNUC_VERSION || nsel_COMPILER_GNUC_VERSION >= 490
+
+    constexpr error_type const && error() const &&
+    {
+        return std::move( ( assert( ! has_value() ), contained.error() ) );
+    }
+
+    error_type && error() &&
+    {
+        return std::move( ( assert( ! has_value() ), contained.error() ) );
+    }
+
+#endif
+
+    constexpr unexpected_type get_unexpected() const
+    {
+        return make_unexpected( contained.error() );
+    }
+
+    template< typename Ex >
+    bool has_exception() const
+    {
+        using ContainedEx = typename std::remove_reference< decltype( get_unexpected().value() ) >::type;
+        return ! has_value() && std::is_base_of< Ex, ContainedEx>::value;
+    }
+
+//  template constexpr 'see below' unwrap() const&;
+//
+//  template 'see below' unwrap() &&;
+
+    // factories
+
+//  template< typename Ex, typename F>
+//  expected<void,E> catch_exception(F&& f);
+//
+//  template< typename F>
+//  expected<decltype(func()), E> map(F&& func) ;
+//
+//  template< typename F>
+//  'see below' bind(F&& func) ;
+//
+//  template< typename F>
+//  expected<void,E> catch_error(F&& f);
+//
+//  template< typename F>
+//  'see below' then(F&& func);
+
+private:
+    detail::storage_t
+    <
+        void
+        , E
+        , std::is_copy_constructible<E>::value
+        , std::is_move_constructible<E>::value
+    >
+    contained;
+};
+
+// x.x.4.6 expected<>: comparison operators
+
+template< typename T1, typename E1, typename T2, typename E2
+    nsel_REQUIRES_T(
+        !std::is_void<T1>::value && !std::is_void<T2>::value
+    )
+>
+constexpr bool operator==( expected<T1,E1> const & x, expected<T2,E2> const & y )
+{
+    return bool(x) != bool(y) ? false : bool(x) ? *x == *y : x.error() == y.error();
+}
+
+template< typename T1, typename E1, typename T2, typename E2
+    nsel_REQUIRES_T(
+        std::is_void<T1>::value && std::is_void<T2>::value
+    )
+>
+constexpr bool operator==( expected<T1,E1> const & x, expected<T2,E2> const & y )
+{
+    return bool(x) != bool(y) ? false : bool(x) || static_cast<bool>( x.error() == y.error() );
+}
+
+template< typename T1, typename E1, typename T2, typename E2 >
+constexpr bool operator!=( expected<T1,E1> const & x, expected<T2,E2> const & y )
+{
+    return !(x == y);
+}
+
+#if nsel_P0323R <= 2
+
+template< typename T, typename E >
+constexpr bool operator<( expected<T,E> const & x, expected<T,E> const & y )
+{
+    return (!y) ? false : (!x) ? true : *x < *y;
+}
+
+template< typename T, typename E >
+constexpr bool operator>( expected<T,E> const & x, expected<T,E> const & y )
+{
+    return (y < x);
+}
+
+template< typename T, typename E >
+constexpr bool operator<=( expected<T,E> const & x, expected<T,E> const & y )
+{
+    return !(y < x);
+}
+
+template< typename T, typename E >
+constexpr bool operator>=( expected<T,E> const & x, expected<T,E> const & y )
+{
+    return !(x < y);
+}
+
+#endif
+
+// x.x.4.7 expected: comparison with T
+
+template< typename T1, typename E1, typename T2
+    nsel_REQUIRES_T(
+        !std::is_void<T1>::value
+    )
+>
+constexpr bool operator==( expected<T1,E1> const & x, T2 const & v )
+{
+    return bool(x) ? *x == v : false;
+}
+
+template< typename T1, typename E1, typename T2
+    nsel_REQUIRES_T(
+        !std::is_void<T1>::value
+    )
+>
+constexpr bool operator==(T2 const & v, expected<T1,E1> const & x )
+{
+    return bool(x) ? v == *x : false;
+}
+
+template< typename T1, typename E1, typename T2 >
+constexpr bool operator!=( expected<T1,E1> const & x, T2 const & v )
+{
+    return bool(x) ? *x != v : true;
+}
+
+template< typename T1, typename E1, typename T2 >
+constexpr bool operator!=( T2 const & v, expected<T1,E1> const & x )
+{
+    return bool(x) ? v != *x : true;
+}
+
+#if nsel_P0323R <= 2
+
+template< typename T, typename E >
+constexpr bool operator<( expected<T,E> const & x, T const & v )
+{
+    return bool(x) ? *x < v : true;
+}
+
+template< typename T, typename E >
+constexpr bool operator<( T const & v, expected<T,E> const & x )
+{
+    return bool(x) ? v < *x : false;
+}
+
+template< typename T, typename E >
+constexpr bool operator>( T const & v, expected<T,E> const & x )
+{
+    return bool(x) ? *x < v : false;
+}
+
+template< typename T, typename E >
+constexpr bool operator>( expected<T,E> const & x, T const & v )
+{
+    return bool(x) ? v < *x : false;
+}
+
+template< typename T, typename E >
+constexpr bool operator<=( T const & v, expected<T,E> const & x )
+{
+    return bool(x) ? ! ( *x < v ) : false;
+}
+
+template< typename T, typename E >
+constexpr bool operator<=( expected<T,E> const & x, T const & v )
+{
+    return bool(x) ? ! ( v < *x ) : true;
+}
+
+template< typename T, typename E >
+constexpr bool operator>=( expected<T,E> const & x, T const & v )
+{
+    return bool(x) ? ! ( *x < v ) : false;
+}
+
+template< typename T, typename E >
+constexpr bool operator>=( T const & v, expected<T,E> const & x )
+{
+    return bool(x) ? ! ( v < *x ) : true;
+}
+
+#endif // nsel_P0323R
+
+// x.x.4.8 expected: comparison with unexpected_type
+
+template< typename T1, typename E1 , typename E2 >
+constexpr bool operator==( expected<T1,E1> const & x, unexpected_type<E2> const & u )
+{
+    return (!x) ? x.get_unexpected() == u : false;
+}
+
+template< typename T1, typename E1 , typename E2 >
+constexpr bool operator==( unexpected_type<E2> const & u, expected<T1,E1> const & x )
+{
+    return ( x == u );
+}
+
+template< typename T1, typename E1 , typename E2 >
+constexpr bool operator!=( expected<T1,E1> const & x, unexpected_type<E2> const & u )
+{
+    return ! ( x == u );
+}
+
+template< typename T1, typename E1 , typename E2 >
+constexpr bool operator!=( unexpected_type<E2> const & u, expected<T1,E1> const & x )
+{
+    return ! ( x == u );
+}
+
+#if nsel_P0323R <= 2
+
+template< typename T, typename E >
+constexpr bool operator<( expected<T,E> const & x, unexpected_type<E> const & u )
+{
+    return (!x) ? ( x.get_unexpected() < u ) : false;
+}
+
+template< typename T, typename E >
+constexpr bool operator<( unexpected_type<E> const & u, expected<T,E> const & x )
+{
+  return (!x) ? ( u < x.get_unexpected() ) : true ;
+}
+
+template< typename T, typename E >
+constexpr bool operator>( expected<T,E> const & x, unexpected_type<E> const & u )
+{
+    return ( u < x );
+}
+
+template< typename T, typename E >
+constexpr bool operator>( unexpected_type<E> const & u, expected<T,E> const & x )
+{
+    return ( x < u );
+}
+
+template< typename T, typename E >
+constexpr bool operator<=( expected<T,E> const & x, unexpected_type<E> const & u )
+{
+    return ! ( u < x );
+}
+
+template< typename T, typename E >
+constexpr bool operator<=( unexpected_type<E> const & u, expected<T,E> const & x)
+{
+    return ! ( x < u );
+}
+
+template< typename T, typename E >
+constexpr bool operator>=( expected<T,E> const & x, unexpected_type<E> const & u  )
+{
+    return ! ( u > x );
+}
+
+template< typename T, typename E >
+constexpr bool operator>=( unexpected_type<E> const & u, expected<T,E> const & x )
+{
+    return ! ( x > u );
+}
+
+#endif // nsel_P0323R
+
+/// x.x.x Specialized algorithms
+
+template< typename T, typename E
+    nsel_REQUIRES_T(
+        ( std::is_void<T>::value || std::is_move_constructible<T>::value )
+        && std::is_move_constructible<E>::value
+        && std17::is_swappable<T>::value
+        && std17::is_swappable<E>::value )
+>
+void swap( expected<T,E> & x, expected<T,E> & y ) noexcept ( noexcept ( x.swap(y) ) )
+{
+    x.swap( y );
+}
+
+#if nsel_P0323R <= 3
+
+template< typename T >
+constexpr auto make_expected( T && v ) -> expected< typename std::decay<T>::type >
+{
+    return expected< typename std::decay<T>::type >( std::forward<T>( v ) );
+}
+
+// expected<void> specialization:
+
+auto inline make_expected() -> expected<void>
+{
+    return expected<void>( in_place );
+}
+
+template< typename T >
+constexpr auto make_expected_from_current_exception() -> expected<T>
+{
+    return expected<T>( make_unexpected_from_current_exception() );
+}
+
+template< typename T >
+auto make_expected_from_exception( std::exception_ptr v ) -> expected<T>
+{
+    return expected<T>( unexpected_type<std::exception_ptr>( std::forward<std::exception_ptr>( v ) ) );
+}
+
+template< typename T, typename E >
+constexpr auto make_expected_from_error( E e ) -> expected<T, typename std::decay<E>::type>
+{
+    return expected<T, typename std::decay<E>::type>( make_unexpected( e ) );
+}
+
+template< typename F
+    nsel_REQUIRES_T( ! std::is_same<typename std::result_of<F()>::type, void>::value )
+>
+/*nsel_constexpr14*/
+auto make_expected_from_call( F f ) -> expected< typename std::result_of<F()>::type >
+{
+    try
+    {
+        return make_expected( f() );
+    }
+    catch (...)
+    {
+        return make_unexpected_from_current_exception();
+    }
+}
+
+template< typename F
+    nsel_REQUIRES_T( std::is_same<typename std::result_of<F()>::type, void>::value )
+>
+/*nsel_constexpr14*/
+auto make_expected_from_call( F f ) -> expected<void>
+{
+    try
+    {
+        f();
+        return make_expected();
+    }
+    catch (...)
+    {
+        return make_unexpected_from_current_exception();
+    }
+}
+
+#endif // nsel_P0323R
+
+} // namespace expected_lite
+
+using namespace expected_lite;
+
+// using expected_lite::expected;
+// using ...
+
+} // namespace nonstd
+
+namespace std {
+
+// expected: hash support
+
+template< typename T, typename E >
+struct hash< nonstd::expected<T,E> >
+{
+    using result_type = std::size_t;
+    using argument_type = nonstd::expected<T,E>;
+
+    constexpr result_type operator()(argument_type const & arg) const
+    {
+        return arg ? std::hash<T>{}(*arg) : result_type{};
+    }
+};
+
+// TBD - ?? remove? see spec.
+template< typename T, typename E >
+struct hash< nonstd::expected<T&,E> >
+{
+    using result_type = std::size_t;
+    using argument_type = nonstd::expected<T&,E>;
+
+    constexpr result_type operator()(argument_type const & arg) const
+    {
+        return arg ? std::hash<T>{}(*arg) : result_type{};
+    }
+};
+
+// TBD - implement
+// bool(e), hash<expected<void,E>>()(e) shall evaluate to the hashing true;
+// otherwise it evaluates to an unspecified value if E is exception_ptr or
+// a combination of hashing false and hash<E>()(e.error()).
+
+template< typename E >
+struct hash< nonstd::expected<void,E> >
+{
+};
+
+} // namespace std
+
+namespace nonstd {
+
+// void unexpected() is deprecated && removed in C++17
+
+#if nsel_CPP17_OR_GREATER || nsel_COMPILER_MSVC_VERSION > 141
+template< typename E >
+using unexpected = unexpected_type<E>;
+#endif
+
+} // namespace nonstd
+
+#undef nsel_REQUIRES
+#undef nsel_REQUIRES_0
+#undef nsel_REQUIRES_T
+
+nsel_RESTORE_WARNINGS()
+
+#endif // nsel_USES_STD_EXPECTED
+
+#endif // NONSTD_EXPECTED_LITE_HPP
diff --git a/src/nonstd/optional.hpp b/src/nonstd/optional.hpp
index 2c9f1224..c1e0f5db 100644
--- a/src/nonstd/optional.hpp
+++ b/src/nonstd/optional.hpp
@@ -44,20 +44,26 @@
 # define optional_CONFIG_SELECT_OPTIONAL  ( optional_HAVE_STD_OPTIONAL ? optional_OPTIONAL_STD : optional_OPTIONAL_NONSTD )
 #endif
 
+// Control presence of extensions:
+
+#ifndef optional_CONFIG_NO_EXTENSIONS
+#define optional_CONFIG_NO_EXTENSIONS  0
+#endif
+
 // Control presence of exception handling (try and auto discover):
 
 #ifndef optional_CONFIG_NO_EXCEPTIONS
 # if defined(_MSC_VER)
 # include <cstddef>     // for _HAS_EXCEPTIONS
 # endif
-# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (_HAS_EXCEPTIONS)
+# if defined(__cpp_exceptions) || defined(__EXCEPTIONS) || (defined(_HAS_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):
+// C++ language version detection (C++23 is speculative):
 // Note: VC14.0/1900 (VS2015) lacks too much from C++14.
 
 #ifndef   optional_CPLUSPLUS
@@ -73,7 +79,8 @@
 #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 )
+#define optional_CPP20_OR_GREATER  ( optional_CPLUSPLUS >= 202002L )
+#define optional_CPP23_OR_GREATER  ( optional_CPLUSPLUS >= 202300L )
 
 // C++ language version (represent 98 as 3):
 
@@ -782,7 +789,7 @@ union storage_t
 
     void construct_value( value_type && v )
     {
-        ::new( value_ptr() ) value_type( std::move( v ) );
+        ::new( const_cast<void *>(static_cast<const volatile void *>(value_ptr())) ) value_type( std::move( v ) );
     }
 
     template< class... Args >
@@ -794,13 +801,13 @@ union storage_t
     template< class... Args >
     void emplace( Args&&... args )
     {
-        ::new( value_ptr() ) value_type( std::forward<Args>(args)... );
+        ::new( const_cast<void *>(static_cast<const volatile void *>(value_ptr())) ) value_type( std::forward<Args>(args)... );
     }
 
     template< class U, class... Args >
     void emplace( std::initializer_list<U> il, Args&&... args )
     {
-        ::new( value_ptr() ) value_type( il, std::forward<Args>(args)... );
+        ::new( const_cast<void *>(static_cast<const volatile void *>(value_ptr())) ) value_type( il, std::forward<Args>(args)... );
     }
 
 #endif
@@ -1485,7 +1492,40 @@ public:
         return has_value() ? contained.value() : static_cast<value_type>( v );
     }
 
-#endif // optional_CPP11_OR_GREATER
+#endif // optional_HAVE( REF_QUALIFIER )
+
+#if !optional_CONFIG_NO_EXTENSIONS
+#if  optional_HAVE( REF_QUALIFIER )
+
+    template< typename F >
+    optional_constexpr value_type value_or_eval( F f ) const &
+    {
+        return has_value() ? contained.value() : f();
+    }
+
+    template< typename F >
+    optional_constexpr14 value_type value_or_eval( F f ) &&
+    {
+        if ( has_value() )
+        {
+            return std::move( contained.value() );
+        }
+        else
+        {
+            return f();
+        }
+    }
+
+#else
+
+    template< typename F >
+    optional_constexpr value_type value_or_eval( F f ) const
+    {
+        return has_value() ? contained.value() : f();
+    }
+
+#endif //  optional_HAVE( REF_QUALIFIER )
+#endif // !optional_CONFIG_NO_EXTENSIONS
 
     // x.x.3.6, modifiers
 
@@ -1530,37 +1570,37 @@ private:
 // Relational operators
 
 template< typename T, typename U >
-inline optional_constexpr bool operator==( optional<T> const & x, optional<U> const & y )
+optional_nodiscard optional_constexpr bool operator==( optional<T> const & x, optional<U> const & y )
 {
     return bool(x) != bool(y) ? false : !bool( x ) ? true : *x == *y;
 }
 
 template< typename T, typename U >
-inline optional_constexpr bool operator!=( optional<T> const & x, optional<U> const & y )
+optional_nodiscard optional_constexpr bool operator!=( optional<T> const & x, optional<U> const & y )
 {
     return !(x == y);
 }
 
 template< typename T, typename U >
-inline optional_constexpr bool operator<( optional<T> const & x, optional<U> const & y )
+optional_nodiscard optional_constexpr bool operator<( optional<T> const & x, optional<U> const & y )
 {
     return (!y) ? false : (!x) ? true : *x < *y;
 }
 
 template< typename T, typename U >
-inline optional_constexpr bool operator>( optional<T> const & x, optional<U> const & y )
+optional_nodiscard optional_constexpr bool operator>( optional<T> const & x, optional<U> const & y )
 {
     return (y < x);
 }
 
 template< typename T, typename U >
-inline optional_constexpr bool operator<=( optional<T> const & x, optional<U> const & y )
+optional_nodiscard optional_constexpr bool operator<=( optional<T> const & x, optional<U> const & y )
 {
     return !(y < x);
 }
 
 template< typename T, typename U >
-inline optional_constexpr bool operator>=( optional<T> const & x, optional<U> const & y )
+optional_nodiscard optional_constexpr bool operator>=( optional<T> const & x, optional<U> const & y )
 {
     return !(x < y);
 }
@@ -1568,73 +1608,73 @@ inline optional_constexpr bool operator>=( optional<T> const & x, optional<U> co
 // Comparison with nullopt
 
 template< typename T >
-inline optional_constexpr bool operator==( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
+optional_nodiscard optional_constexpr bool operator==( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
 {
     return (!x);
 }
 
 template< typename T >
-inline optional_constexpr bool operator==( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
+optional_nodiscard optional_constexpr bool operator==( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
 {
     return (!x);
 }
 
 template< typename T >
-inline optional_constexpr bool operator!=( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
+optional_nodiscard optional_constexpr bool operator!=( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
 {
     return bool(x);
 }
 
 template< typename T >
-inline optional_constexpr bool operator!=( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
+optional_nodiscard optional_constexpr bool operator!=( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
 {
     return bool(x);
 }
 
 template< typename T >
-inline optional_constexpr bool operator<( optional<T> const & /*unused*/, nullopt_t /*unused*/ ) optional_noexcept
+optional_nodiscard optional_constexpr bool operator<( optional<T> const & /*unused*/, nullopt_t /*unused*/ ) optional_noexcept
 {
     return false;
 }
 
 template< typename T >
-inline optional_constexpr bool operator<( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
+optional_nodiscard optional_constexpr bool operator<( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
 {
     return bool(x);
 }
 
 template< typename T >
-inline optional_constexpr bool operator<=( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
+optional_nodiscard optional_constexpr bool operator<=( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
 {
     return (!x);
 }
 
 template< typename T >
-inline optional_constexpr bool operator<=( nullopt_t /*unused*/, optional<T> const & /*unused*/ ) optional_noexcept
+optional_nodiscard optional_constexpr bool operator<=( nullopt_t /*unused*/, optional<T> const & /*unused*/ ) optional_noexcept
 {
     return true;
 }
 
 template< typename T >
-inline optional_constexpr bool operator>( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
+optional_nodiscard optional_constexpr bool operator>( optional<T> const & x, nullopt_t /*unused*/ ) optional_noexcept
 {
     return bool(x);
 }
 
 template< typename T >
-inline optional_constexpr bool operator>( nullopt_t /*unused*/, optional<T> const & /*unused*/ ) optional_noexcept
+optional_nodiscard optional_constexpr bool operator>( nullopt_t /*unused*/, optional<T> const & /*unused*/ ) optional_noexcept
 {
     return false;
 }
 
 template< typename T >
-inline optional_constexpr bool operator>=( optional<T> const & /*unused*/, nullopt_t /*unused*/ ) optional_noexcept
+optional_nodiscard optional_constexpr bool operator>=( optional<T> const & /*unused*/, nullopt_t /*unused*/ ) optional_noexcept
 {
     return true;
 }
 
 template< typename T >
-inline optional_constexpr bool operator>=( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
+optional_nodiscard optional_constexpr bool operator>=( nullopt_t /*unused*/, optional<T> const & x ) optional_noexcept
 {
     return (!x);
 }
@@ -1642,73 +1682,73 @@ inline optional_constexpr bool operator>=( nullopt_t /*unused*/, optional<T> con
 // Comparison with T
 
 template< typename T, typename U >
-inline optional_constexpr bool operator==( optional<T> const & x, U const & v )
+optional_nodiscard optional_constexpr bool operator==( optional<T> const & x, U const & v )
 {
     return bool(x) ? *x == v : false;
 }
 
 template< typename T, typename U >
-inline optional_constexpr bool operator==( U const & v, optional<T> const & x )
+optional_nodiscard optional_constexpr bool operator==( U const & v, optional<T> const & x )
 {
     return bool(x) ? v == *x : false;
 }
 
 template< typename T, typename U >
-inline optional_constexpr bool operator!=( optional<T> const & x, U const & v )
+optional_nodiscard optional_constexpr bool operator!=( optional<T> const & x, U const & v )
 {
     return bool(x) ? *x != v : true;
 }
 
 template< typename T, typename U >
-inline optional_constexpr bool operator!=( U const & v, optional<T> const & x )
+optional_nodiscard optional_constexpr bool operator!=( U const & v, optional<T> const & x )
 {
     return bool(x) ? v != *x : true;
 }
 
 template< typename T, typename U >
-inline optional_constexpr bool operator<( optional<T> const & x, U const & v )
+optional_nodiscard optional_constexpr bool operator<( optional<T> const & x, U const & v )
 {
     return bool(x) ? *x < v : true;
 }
 
 template< typename T, typename U >
-inline optional_constexpr bool operator<( U const & v, optional<T> const & x )
+optional_nodiscard optional_constexpr bool operator<( U const & v, optional<T> const & x )
 {
     return bool(x) ? v < *x : false;
 }
 
 template< typename T, typename U >
-inline optional_constexpr bool operator<=( optional<T> const & x, U const & v )
+optional_nodiscard optional_constexpr bool operator<=( optional<T> const & x, U const & v )
 {
     return bool(x) ? *x <= v : true;
 }
 
 template< typename T, typename U >
-inline optional_constexpr bool operator<=( U const & v, optional<T> const & x )
+optional_nodiscard optional_constexpr bool operator<=( U const & v, optional<T> const & x )
 {
     return bool(x) ? v <= *x : false;
 }
 
 template< typename T, typename U >
-inline optional_constexpr bool operator>( optional<T> const & x, U const & v )
+optional_nodiscard optional_constexpr bool operator>( optional<T> const & x, U const & v )
 {
     return bool(x) ? *x > v : false;
 }
 
 template< typename T, typename U >
-inline optional_constexpr bool operator>( U const & v, optional<T> const & x )
+optional_nodiscard optional_constexpr bool operator>( U const & v, optional<T> const & x )
 {
     return bool(x) ? v > *x : true;
 }
 
 template< typename T, typename U >
-inline optional_constexpr bool operator>=( optional<T> const & x, U const & v )
+optional_nodiscard optional_constexpr bool operator>=( optional<T> const & x, U const & v )
 {
     return bool(x) ? *x >= v : false;
 }
 
 template< typename T, typename U >
-inline optional_constexpr bool operator>=( U const & v, optional<T> const & x )
+optional_nodiscard optional_constexpr bool operator>=( U const & v, optional<T> const & x )
 {
     return bool(x) ? v >= *x : true;
 }
diff --git a/src/syslog.cpp b/src/syslog.cpp
new file mode 100644
index 00000000..d6fe100c
--- /dev/null
+++ b/src/syslog.cpp
@@ -0,0 +1,96 @@
+/*
+  ISC License
+
+  Copyright (c) 2023, Antonio SJ Musumeci <trapexit@spawn.link>
+
+  Permission to use, copy, modify, and/or distribute this software for any
+  purpose with or without fee is hereby granted, provided that the above
+  copyright notice and this permission notice appear in all copies.
+
+  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <stdarg.h>
+#include <syslog.h>
+
+static bool g_SYSLOG_ENABLED = false;
+
+void
+syslog_open()
+{
+  const char *ident = "mergerfs";
+  const int   option = (LOG_CONS|LOG_PID);
+  const int   facility = LOG_USER;
+
+  openlog(ident,option,facility);
+  g_SYSLOG_ENABLED = true;
+}
+
+void
+syslog_close()
+{
+  closelog();
+  g_SYSLOG_ENABLED = false;
+}
+
+void
+syslog_log(const int   priority_,
+           const char *format_,
+           va_list     valist_)
+{
+  if(g_SYSLOG_ENABLED == false)
+    return;
+
+  vsyslog(priority_,format_,valist_);
+}
+
+void
+syslog_log(const int   priority_,
+           const char *format_,
+           ...)
+{
+  va_list valist;
+
+  va_start(valist,format_);
+  syslog_log(priority_,format_,valist);
+  va_end(valist);
+}
+
+void
+syslog_info(const char *format_,
+            ...)
+{
+  va_list valist;
+
+  va_start(valist,format_);
+  syslog_log(LOG_INFO,format_,valist);
+  va_end(valist);  
+}
+
+void
+syslog_warning(const char *format_,
+               ...)
+{
+  va_list valist;
+
+  va_start(valist,format_);
+  syslog_log(LOG_WARNING,format_,valist);
+  va_end(valist);  
+}
+
+void
+syslog_error(const char *format_,
+             ...)
+{
+  va_list valist;
+
+  va_start(valist,format_);
+  syslog_log(LOG_ERR,format_,valist);
+  va_end(valist);  
+}
diff --git a/src/syslog.hpp b/src/syslog.hpp
new file mode 100644
index 00000000..f9df4f51
--- /dev/null
+++ b/src/syslog.hpp
@@ -0,0 +1,29 @@
+/*
+  ISC License
+
+  Copyright (c) 2023, Antonio SJ Musumeci <trapexit@spawn.link>
+
+  Permission to use, copy, modify, and/or distribute this software for any
+  purpose with or without fee is hereby granted, provided that the above
+  copyright notice and this permission notice appear in all copies.
+
+  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#pragma once
+
+#include <syslog.h>
+
+
+void syslog_open();
+void syslog_log(const int priority, const char *format, ...);
+void syslog_info(const char *format, ...);
+void syslog_warning(const char *format, ...);
+void syslog_error(const char *format, ...);
+void syslog_close();