diff --git a/.githooks/readme-template.md b/.githooks/readme-template.md
index b4eed5e9..47891543 100644
--- a/.githooks/readme-template.md
+++ b/.githooks/readme-template.md
@@ -321,7 +321,7 @@ Completed 10000000 requests
Finished 10000000 requests
-Server Software:
+Server Software:
Server Hostname: 127.0.0.1
Server Port: 8888
@@ -412,8 +412,6 @@ This project uses git submodules, to properly checkout this project use:
This project depends on the following git sub-modules:
* [libc-ares](https://github.com/c-ares/c-ares) For async DNS resolver, this is a git submodule.
* [catch2](https://github.com/catchorg/Catch2) For testing, this is embedded in the `test/` directory.
- * [expected](https://github.com/TartanLlama/expected) For results on operations that can fail, this is a git submodule in the `vendor/` directory.
- * `-std=c++23` if it supports `__cpp_lib_expected` will use `std::expected` instead.
#### Building
mkdir Release && cd Release
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 1220d581..56ef7f43 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -165,7 +165,6 @@ jobs:
run: |
cd build-release-clang++-feature-networking-disabled
ctest -VV
-
build-fedora-32-to-37:
name: fedora-${{ matrix.fedora_version }}
runs-on: ubuntu-latest
@@ -281,4 +280,50 @@ jobs:
- name: test-release-cl
run: |
cd build-release-cl
- ctest --build-config Release -VV
+ build-emscripten-3_1_45:
+ name: emscripten-3_1_45
+ runs-on: ubuntu-latest
+ container:
+ image: ubuntu:22.04
+ env:
+ TZ: America/New_York
+ DEBIAN_FRONTEND: noninteractive
+ steps:
+ - name: apt
+ run: |
+ apt-get update
+ apt-get -y upgrade
+ apt install -y build-essential software-properties-common
+ add-apt-repository ppa:ubuntu-toolchain-r/test
+ apt-get install -y \
+ cmake \
+ git \
+ ninja-build
+ - name: Checkout
+ uses: actions/checkout@v2
+ with:
+ submodules: recursive
+ - name: install-emsdk
+ run: |
+ git clone https://github.com/emscripten-core/emsdk.git
+ cd emsdk
+ ./emsdk install 3.1.45
+ ./emsdk activate 3.1.45
+ - name: build-release-emscripten
+ run: |
+ cd emsdk
+ . ./emsdk_env.sh
+ cd ..
+ mkdir build-release-emscripten
+ cd build-release-emscripten
+ emcmake cmake \
+ -GNinja \
+ -DCMAKE_BUILD_TYPE=Release \
+ ..
+ ninja
+ - name: test-release-emscripten
+ run: |
+ cd emsdk
+ . ./emsdk_env.sh
+ cd ../build-release-emscripten
+ node --experimental-wasm-eh ./test/libcoro_test.js
diff --git a/.gitmodules b/.gitmodules
index aa4c349f..4bfa2051 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,3 @@
[submodule "vendor/c-ares/c-ares"]
- path = vendor/c-ares/c-ares
- url = https://github.com/c-ares/c-ares.git
-[submodule "vendor/tartanllama/expected"]
- path = vendor/tartanllama/expected
- url = https://github.com/jstranik/expected.git
+ path = vendor/c-ares/c-ares
+ url = https://github.com/c-ares/c-ares.git
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 18442475..67e14fd6 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -23,9 +23,10 @@ option(LIBCORO_EXTERNAL_DEPENDENCIES "Use Cmake find_package to resolve dependen
option(LIBCORO_BUILD_TESTS "Build the tests, Default=ON." ON)
option(LIBCORO_CODE_COVERAGE "Enable code coverage, tests must also be enabled, Default=OFF" OFF)
option(LIBCORO_BUILD_EXAMPLES "Build the examples, Default=ON." ON)
-cmake_dependent_option(LIBCORO_FEATURE_PLATFORM "Include linux platform features, Default=ON." ON "NOT MSVC" OFF)
-cmake_dependent_option(LIBCORO_FEATURE_NETWORKING "Include networking features, Default=ON." ON "NOT MSVC" OFF)
-cmake_dependent_option(LIBCORO_FEATURE_SSL "Include SSL encryption features, Default=ON." ON "NOT MSVC" OFF)
+
+cmake_dependent_option(LIBCORO_FEATURE_PLATFORM "Include linux platform features, Default=ON." ON "NOT EMSCRIPTEN; NOT MSVC" OFF)
+cmake_dependent_option(LIBCORO_FEATURE_NETWORKING "Include networking features, Default=ON." ON "NOT EMSCRIPTEN; NOT MSVC" OFF)
+cmake_dependent_option(LIBCORO_FEATURE_SSL "Include SSL encryption features, Default=ON." ON "NOT EMSCRIPTEN; NOT MSVC" OFF)
message("${PROJECT_NAME} LIBCORO_EXTERNAL_DEPENDENCIES = ${LIBCORO_EXTERNAL_DEPENDENCIES}")
message("${PROJECT_NAME} LIBCORO_BUILD_TESTS = ${LIBCORO_BUILD_TESTS}")
@@ -39,7 +40,6 @@ if(LIBCORO_EXTERNAL_DEPENDENCIES)
if(LIBCORO_FEATURE_NETWORKING)
find_package(c-ares CONFIG REQUIRED)
endif()
- find_package(tl-expected CONFIG REQUIRED)
else()
if(NOT LIBCORO_BUILD_TESTS)
# Disable testing in expected
@@ -51,7 +51,6 @@ else()
set(CARES_INSTALL OFF CACHE INTERNAL "")
add_subdirectory(vendor/c-ares/c-ares)
endif()
- add_subdirectory(vendor/tartanllama/expected)
endif()
set(LIBCORO_SOURCE_FILES
@@ -112,18 +111,28 @@ if(LIBCORO_FEATURE_NETWORKING)
endif()
endif()
+if(DEFINED EMSCRIPTEN)
+ add_compile_options(-fwasm-exceptions)
+ add_compile_options(-pthread)
+ add_compile_options(-matomics)
+
+ add_link_options(-fwasm-exceptions)
+ add_link_options(-pthread)
+ add_link_options(-sPROXY_TO_PTHREAD)
+ add_link_options(-sALLOW_MEMORY_GROWTH)
+ add_link_options(-sEXIT_RUNTIME)
+endif()
+
add_library(${PROJECT_NAME} STATIC ${LIBCORO_SOURCE_FILES})
set_target_properties(${PROJECT_NAME} PROPERTIES LINKER_LANGUAGE CXX PREFIX "")
target_compile_features(${PROJECT_NAME} PUBLIC cxx_std_20)
target_include_directories(${PROJECT_NAME} PUBLIC include)
-if(MSVC)
- # Not sure why this is only needed on Windows.
- target_include_directories(${PROJECT_NAME} PUBLIC vendor/tartanllama/expected/include)
-endif()
+
if(LIBCORO_FEATURE_PLATFORM)
- target_link_libraries(${PROJECT_NAME} PUBLIC pthread tl::expected)
+ target_link_libraries(${PROJECT_NAME} PUBLIC pthread)
target_compile_definitions(${PROJECT_NAME} PUBLIC LIBCORO_FEATURE_PLATFORM)
-endif()
+ endif()
+
if(LIBCORO_FEATURE_NETWORKING)
target_link_libraries(${PROJECT_NAME} PUBLIC c-ares::cares)
target_compile_definitions(${PROJECT_NAME} PUBLIC LIBCORO_FEATURE_NETWORKING)
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index 4d8509fd..44a42c3e 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -64,7 +64,9 @@ elseif(${CMAKE_CXX_COMPILER_ID} MATCHES "Clang")
target_compile_options(coro_task PUBLIC -Wall -Wextra -pipe)
target_compile_options(coro_generator PUBLIC -Wall -Wextra -pipe)
target_compile_options(coro_event PUBLIC -Wall -Wextra -pipe)
- target_compile_options(coro_latch PUBLIC -Wall -Wextra -pipe)
+ if(LIBCORO_FEATURE_PLATFORM)
+ target_compile_options(coro_latch PUBLIC -Wall -Wextra -pipe)
+ endif()
target_compile_options(coro_mutex PUBLIC -Wall -Wextra -pipe)
target_compile_options(coro_thread_pool PUBLIC -Wall -Wextra -pipe)
target_compile_options(coro_semaphore PUBLIC -Wall -Wextra -pipe)
diff --git a/include/coro/detail/tl_expected.hpp b/include/coro/detail/tl_expected.hpp
new file mode 100644
index 00000000..052a633b
--- /dev/null
+++ b/include/coro/detail/tl_expected.hpp
@@ -0,0 +1,2655 @@
+///
+// expected - An implementation of std::expected with extensions
+// Written in 2017 by Simon Brand (simonrbrand@gmail.com, @TartanLlama)
+//
+// Documentation available at http://tl.tartanllama.xyz/
+//
+// To the extent possible under law, the author(s) have dedicated all
+// copyright and related and neighboring rights to this software to the
+// public domain worldwide. This software is distributed without any warranty.
+//
+// You should have received a copy of the CC0 Public Domain Dedication
+// along with this software. If not, see
+// .
+///
+
+#ifndef TL_EXPECTED_HPP
+#define TL_EXPECTED_HPP
+
+#define TL_EXPECTED_VERSION_MAJOR 1
+#define TL_EXPECTED_VERSION_MINOR 0
+#define TL_EXPECTED_VERSION_PATCH 1
+
+#include
+#include
+#include
+#include
+
+#if defined(__EXCEPTIONS) || defined(_CPPUNWIND)
+ #define TL_EXPECTED_EXCEPTIONS_ENABLED
+#endif
+
+#if (defined(_MSC_VER) && _MSC_VER == 1900)
+ #define TL_EXPECTED_MSVC2015
+ #define TL_EXPECTED_MSVC2015_CONSTEXPR
+#else
+ #define TL_EXPECTED_MSVC2015_CONSTEXPR constexpr
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && !defined(__clang__))
+ #define TL_EXPECTED_GCC49
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 4 && !defined(__clang__))
+ #define TL_EXPECTED_GCC54
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 5 && __GNUC_MINOR__ <= 5 && !defined(__clang__))
+ #define TL_EXPECTED_GCC55
+#endif
+
+#if (defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ <= 9 && !defined(__clang__))
+ // GCC < 5 doesn't support overloading on const&& for member functions
+
+ #define TL_EXPECTED_NO_CONSTRR
+ // GCC < 5 doesn't support some standard C++11 type traits
+ #define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) std::has_trivial_copy_constructor
+ #define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::has_trivial_copy_assign
+
+ // This one will be different for GCC 5.7 if it's ever supported
+ #define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible
+
+// GCC 5 < v < 8 has a bug in is_trivially_copy_constructible which breaks std::vector
+// for non-copyable types
+#elif (defined(__GNUC__) && __GNUC__ < 8 && !defined(__clang__))
+ #ifndef TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+ #define TL_GCC_LESS_8_TRIVIALLY_COPY_CONSTRUCTIBLE_MUTEX
+namespace tl
+{
+namespace detail
+{
+template
+struct is_trivially_copy_constructible : std::is_trivially_copy_constructible
+{
+};
+ #ifdef _GLIBCXX_VECTOR
+template
+struct is_trivially_copy_constructible> : std::false_type
+{
+};
+ #endif
+} // namespace detail
+} // namespace tl
+ #endif
+
+ #define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) tl::detail::is_trivially_copy_constructible
+ #define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::is_trivially_copy_assignable
+ #define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible
+#else
+ #define TL_EXPECTED_IS_TRIVIALLY_COPY_CONSTRUCTIBLE(T) std::is_trivially_copy_constructible
+ #define TL_EXPECTED_IS_TRIVIALLY_COPY_ASSIGNABLE(T) std::is_trivially_copy_assignable
+ #define TL_EXPECTED_IS_TRIVIALLY_DESTRUCTIBLE(T) std::is_trivially_destructible
+#endif
+
+#if __cplusplus > 201103L
+ #define TL_EXPECTED_CXX14
+#endif
+
+#ifdef TL_EXPECTED_GCC49
+ #define TL_EXPECTED_GCC49_CONSTEXPR
+#else
+ #define TL_EXPECTED_GCC49_CONSTEXPR constexpr
+#endif
+
+#if (__cplusplus == 201103L || defined(TL_EXPECTED_MSVC2015) || defined(TL_EXPECTED_GCC49))
+ #define TL_EXPECTED_11_CONSTEXPR
+#else
+ #define TL_EXPECTED_11_CONSTEXPR constexpr
+#endif
+
+namespace tl
+{
+template
+class expected;
+
+#ifndef TL_MONOSTATE_INPLACE_MUTEX
+ #define TL_MONOSTATE_INPLACE_MUTEX
+class monostate
+{
+};
+
+struct in_place_t
+{
+ explicit in_place_t() = default;
+};
+static constexpr in_place_t in_place{};
+#endif
+
+template
+class unexpected
+{
+public:
+ static_assert(!std::is_same::value, "E must not be void");
+
+ unexpected() = delete;
+ constexpr explicit unexpected(const E& e) : m_val(e) {}
+
+ constexpr explicit unexpected(E&& e) : m_val(std::move(e)) {}
+
+ constexpr const E& value() const& { return m_val; }
+ TL_EXPECTED_11_CONSTEXPR E& value() & { return m_val; }
+ TL_EXPECTED_11_CONSTEXPR E&& value() && { return std::move(m_val); }
+ constexpr const E&& value() const&& { return std::move(m_val); }
+
+private:
+ E m_val;
+};
+
+template
+constexpr bool operator==(const unexpected& lhs, const unexpected& rhs)
+{
+ return lhs.value() == rhs.value();
+}
+template
+constexpr bool operator!=(const unexpected& lhs, const unexpected& rhs)
+{
+ return lhs.value() != rhs.value();
+}
+template
+constexpr bool operator<(const unexpected& lhs, const unexpected& rhs)
+{
+ return lhs.value() < rhs.value();
+}
+template
+constexpr bool operator<=(const unexpected& lhs, const unexpected& rhs)
+{
+ return lhs.value() <= rhs.value();
+}
+template
+constexpr bool operator>(const unexpected& lhs, const unexpected& rhs)
+{
+ return lhs.value() > rhs.value();
+}
+template
+constexpr bool operator>=(const unexpected& lhs, const unexpected& rhs)
+{
+ return lhs.value() >= rhs.value();
+}
+
+template
+unexpected::type> make_unexpected(E&& e)
+{
+ return unexpected::type>(std::forward(e));
+}
+
+struct unexpect_t
+{
+ unexpect_t() = default;
+};
+static constexpr unexpect_t unexpect{};
+
+namespace detail
+{
+template
+[[noreturn]] TL_EXPECTED_11_CONSTEXPR void throw_exception(E&& e)
+{
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+ throw std::forward(e);
+#else
+ #ifdef _MSC_VER
+ __assume(0);
+ #else
+ __builtin_unreachable();
+ #endif
+#endif
+}
+
+#ifndef TL_TRAITS_MUTEX
+ #define TL_TRAITS_MUTEX
+// C++14-style aliases for brevity
+template
+using remove_const_t = typename std::remove_const::type;
+template
+using remove_reference_t = typename std::remove_reference::type;
+template
+using decay_t = typename std::decay::type;
+template
+using enable_if_t = typename std::enable_if::type;
+template
+using conditional_t = typename std::conditional::type;
+
+// std::conjunction from C++17
+template
+struct conjunction : std::true_type
+{
+};
+template
+struct conjunction : B
+{
+};
+template
+struct conjunction : std::conditional, B>::type
+{
+};
+
+ #if defined(_LIBCPP_VERSION) && __cplusplus == 201103L
+ #define TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+ #endif
+
+ // In C++11 mode, there's an issue in libc++'s std::mem_fn
+ // which results in a hard-error when using it in a noexcept expression
+ // in some cases. This is a check to workaround the common failing case.
+ #ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+template
+struct is_pointer_to_non_const_member_func : std::false_type
+{
+};
+template
+struct is_pointer_to_non_const_member_func : std::true_type
+{
+};
+template
+struct is_pointer_to_non_const_member_func : std::true_type
+{
+};
+template
+struct is_pointer_to_non_const_member_func : std::true_type
+{
+};
+template
+struct is_pointer_to_non_const_member_func : std::true_type
+{
+};
+template
+struct is_pointer_to_non_const_member_func : std::true_type
+{
+};
+template
+struct is_pointer_to_non_const_member_func : std::true_type
+{
+};
+
+template
+struct is_const_or_const_ref : std::false_type
+{
+};
+template
+struct is_const_or_const_ref : std::true_type
+{
+};
+template
+struct is_const_or_const_ref : std::true_type
+{
+};
+ #endif
+
+// std::invoke from C++17
+// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
+template<
+ typename Fn,
+ typename... Args,
+ #ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
+ typename = enable_if_t::value && is_const_or_const_ref::value)>,
+ #endif
+ typename = enable_if_t>::value>,
+ int = 0>
+constexpr auto invoke(Fn&& f, Args&&... args) noexcept(noexcept(std::mem_fn(f)(std::forward(args)...)))
+ -> decltype(std::mem_fn(f)(std::forward(args)...))
+{
+ return std::mem_fn(f)(std::forward(args)...);
+}
+
+template>::value>>
+constexpr auto invoke(Fn&& f, Args&&... args) noexcept(noexcept(std::forward(f)(std::forward(args)...)))
+ -> decltype(std::forward(f)(std::forward(args)...))
+{
+ return std::forward(f)(std::forward(args)...);
+}
+
+// std::invoke_result from C++17
+template
+struct invoke_result_impl;
+
+template
+struct invoke_result_impl(), std::declval()...), void()), Us...>
+{
+ using type = decltype(detail::invoke(std::declval(), std::declval()...));
+};
+
+template
+using invoke_result = invoke_result_impl;
+
+template
+using invoke_result_t = typename invoke_result::type;
+
+ #if defined(_MSC_VER) && _MSC_VER <= 1900
+// TODO make a version which works with MSVC 2015
+template
+struct is_swappable : std::true_type
+{
+};
+
+template
+struct is_nothrow_swappable : std::true_type
+{
+};
+ #else
+// https://stackoverflow.com/questions/26744589/what-is-a-proper-way-to-implement-is-swappable-to-test-for-the-swappable-concept
+namespace swap_adl_tests
+{
+// if swap ADL finds this then it would call std::swap otherwise (same
+// signature)
+struct tag
+{
+};
+
+template
+tag swap(T&, T&);
+template
+tag swap(T (&a)[N], T (&b)[N]);
+
+// helper functions to test if an unqualified swap is possible, and if it
+// becomes std::swap
+template
+std::false_type can_swap(...) noexcept(false);
+template(), std::declval()))>
+std::true_type can_swap(int) noexcept(noexcept(swap(std::declval(), std::declval())));
+
+template
+std::false_type uses_std(...);
+template
+std::is_same(), std::declval())), tag> uses_std(int);
+
+template
+struct is_std_swap_noexcept
+ : std::integral_constant<
+ bool,
+ std::is_nothrow_move_constructible::value && std::is_nothrow_move_assignable::value>
+{
+};
+
+template
+struct is_std_swap_noexcept : is_std_swap_noexcept
+{
+};
+
+template
+struct is_adl_swap_noexcept : std::integral_constant(0))>
+{
+};
+} // namespace swap_adl_tests
+
+template
+struct is_swappable : std::integral_constant<
+ bool,
+ decltype(detail::swap_adl_tests::can_swap(0))::value &&
+ (!decltype(detail::swap_adl_tests::uses_std(0))::value ||
+ (std::is_move_assignable::value && std::is_move_constructible::value))>
+{
+};
+
+template
+struct is_swappable
+ : std::integral_constant<
+ bool,
+ decltype(detail::swap_adl_tests::can_swap(0))::value &&
+ (!decltype(detail::swap_adl_tests::uses_std(0))::value || is_swappable::value)>
+{
+};
+
+template
+struct is_nothrow_swappable
+ : std::integral_constant<
+ bool,
+ is_swappable::value && ((decltype(detail::swap_adl_tests::uses_std(0))::value &&
+ detail::swap_adl_tests::is_std_swap_noexcept::value) ||
+ (!decltype(detail::swap_adl_tests::uses_std(0))::value &&
+ detail::swap_adl_tests::is_adl_swap_noexcept::value))>
+{
+};
+ #endif
+#endif
+
+// Trait for checking if a type is a tl::expected
+template
+struct is_expected_impl : std::false_type
+{
+};
+template
+struct is_expected_impl> : std::true_type
+{
+};
+template
+using is_expected = is_expected_impl>;
+
+template
+using expected_enable_forward_value = detail::enable_if_t<
+ std::is_constructible::value && !std::is_same, in_place_t>::value &&
+ !std::is_same, detail::decay_t>::value &&
+ !std::is_same, detail::decay_t>::value>;
+
+template
+using expected_enable_from_other = detail::enable_if_t<
+ std::is_constructible::value && std::is_constructible::value &&
+ !std::is_constructible&>::value && !std::is_constructible&&>::value &&
+ !std::is_constructible&>::value &&
+ !std::is_constructible&&>::value && !std::is_convertible&, T>::value &&
+ !std::is_convertible&&, T>::value && !std::is_convertible&, T>::value &&
+ !std::is_convertible&&, T>::value>;
+
+template
+using is_void_or = conditional_t::value, std::true_type, U>;
+
+template
+using is_copy_constructible_or_void = is_void_or>;
+
+template
+using is_move_constructible_or_void = is_void_or>;
+
+template
+using is_copy_assignable_or_void = is_void_or>;
+
+template
+using is_move_assignable_or_void = is_void_or>;
+
+} // namespace detail
+
+namespace detail
+{
+struct no_init_t
+{
+};
+static constexpr no_init_t no_init{};
+
+// Implements the storage of the values, and ensures that the destructor is
+// trivial if it can be.
+//
+// This specialization is for where neither `T` or `E` is trivially
+// destructible, so the destructors must be called on destruction of the
+// `expected`
+template<
+ class T,
+ class E,
+ bool = std::is_trivially_destructible::value,
+ bool = std::is_trivially_destructible::value>
+struct expected_storage_base
+{
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+ template::value>* = nullptr>
+ constexpr expected_storage_base(in_place_t, Args&&... args) : m_val(std::forward(args)...),
+ m_has_val(true)
+ {
+ }
+
+ template<
+ class U,
+ class... Args,
+ detail::enable_if_t&, Args&&...>::value>* = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list il, Args&&... args)
+ : m_val(il, std::forward(args)...),
+ m_has_val(true)
+ {
+ }
+ template::value>* = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
+ : m_unexpect(std::forward(args)...),
+ m_has_val(false)
+ {
+ }
+
+ template<
+ class U,
+ class... Args,
+ detail::enable_if_t&, Args&&...>::value>* = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, Args&&... args)
+ : m_unexpect(il, std::forward(args)...),
+ m_has_val(false)
+ {
+ }
+
+ ~expected_storage_base()
+ {
+ if (m_has_val)
+ {
+ m_val.~T();
+ }
+ else
+ {
+ m_unexpect.~unexpected();
+ }
+ }
+ union
+ {
+ T m_val;
+ unexpected m_unexpect;
+ char m_no_init;
+ };
+ bool m_has_val;
+};
+
+// This specialization is for when both `T` and `E` are trivially-destructible,
+// so the destructor of the `expected` can be trivial.
+template
+struct expected_storage_base
+{
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+ template::value>* = nullptr>
+ constexpr expected_storage_base(in_place_t, Args&&... args) : m_val(std::forward(args)...),
+ m_has_val(true)
+ {
+ }
+
+ template<
+ class U,
+ class... Args,
+ detail::enable_if_t&, Args&&...>::value>* = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list il, Args&&... args)
+ : m_val(il, std::forward(args)...),
+ m_has_val(true)
+ {
+ }
+ template::value>* = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
+ : m_unexpect(std::forward(args)...),
+ m_has_val(false)
+ {
+ }
+
+ template<
+ class U,
+ class... Args,
+ detail::enable_if_t&, Args&&...>::value>* = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, Args&&... args)
+ : m_unexpect(il, std::forward(args)...),
+ m_has_val(false)
+ {
+ }
+
+ ~expected_storage_base() = default;
+ union
+ {
+ T m_val;
+ unexpected m_unexpect;
+ char m_no_init;
+ };
+ bool m_has_val;
+};
+
+// T is trivial, E is not.
+template
+struct expected_storage_base
+{
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+ template::value>* = nullptr>
+ constexpr expected_storage_base(in_place_t, Args&&... args) : m_val(std::forward(args)...),
+ m_has_val(true)
+ {
+ }
+
+ template<
+ class U,
+ class... Args,
+ detail::enable_if_t&, Args&&...>::value>* = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list il, Args&&... args)
+ : m_val(il, std::forward(args)...),
+ m_has_val(true)
+ {
+ }
+ template::value>* = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
+ : m_unexpect(std::forward(args)...),
+ m_has_val(false)
+ {
+ }
+
+ template<
+ class U,
+ class... Args,
+ detail::enable_if_t&, Args&&...>::value>* = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, Args&&... args)
+ : m_unexpect(il, std::forward(args)...),
+ m_has_val(false)
+ {
+ }
+
+ ~expected_storage_base()
+ {
+ if (!m_has_val)
+ {
+ m_unexpect.~unexpected();
+ }
+ }
+
+ union
+ {
+ T m_val;
+ unexpected m_unexpect;
+ char m_no_init;
+ };
+ bool m_has_val;
+};
+
+// E is trivial, T is not.
+template
+struct expected_storage_base
+{
+ constexpr expected_storage_base() : m_val(T{}), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_no_init(), m_has_val(false) {}
+
+ template::value>* = nullptr>
+ constexpr expected_storage_base(in_place_t, Args&&... args) : m_val(std::forward(args)...),
+ m_has_val(true)
+ {
+ }
+
+ template<
+ class U,
+ class... Args,
+ detail::enable_if_t&, Args&&...>::value>* = nullptr>
+ constexpr expected_storage_base(in_place_t, std::initializer_list il, Args&&... args)
+ : m_val(il, std::forward(args)...),
+ m_has_val(true)
+ {
+ }
+ template::value>* = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
+ : m_unexpect(std::forward(args)...),
+ m_has_val(false)
+ {
+ }
+
+ template<
+ class U,
+ class... Args,
+ detail::enable_if_t&, Args&&...>::value>* = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, Args&&... args)
+ : m_unexpect(il, std::forward(args)...),
+ m_has_val(false)
+ {
+ }
+
+ ~expected_storage_base()
+ {
+ if (m_has_val)
+ {
+ m_val.~T();
+ }
+ }
+ union
+ {
+ T m_val;
+ unexpected m_unexpect;
+ char m_no_init;
+ };
+ bool m_has_val;
+};
+
+// `T` is `void`, `E` is trivially-destructible
+template
+struct expected_storage_base
+{
+ TL_EXPECTED_MSVC2015_CONSTEXPR expected_storage_base() : m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_val(), m_has_val(false) {}
+
+ constexpr expected_storage_base(in_place_t) : m_has_val(true) {}
+
+ template::value>* = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
+ : m_unexpect(std::forward(args)...),
+ m_has_val(false)
+ {
+ }
+
+ template<
+ class U,
+ class... Args,
+ detail::enable_if_t&, Args&&...>::value>* = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, Args&&... args)
+ : m_unexpect(il, std::forward(args)...),
+ m_has_val(false)
+ {
+ }
+
+ ~expected_storage_base() = default;
+ struct dummy
+ {
+ };
+ union
+ {
+ unexpected m_unexpect;
+ dummy m_val;
+ };
+ bool m_has_val;
+};
+
+// `T` is `void`, `E` is not trivially-destructible
+template
+struct expected_storage_base
+{
+ constexpr expected_storage_base() : m_dummy(), m_has_val(true) {}
+ constexpr expected_storage_base(no_init_t) : m_dummy(), m_has_val(false) {}
+
+ constexpr expected_storage_base(in_place_t) : m_dummy(), m_has_val(true) {}
+
+ template::value>* = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, Args&&... args)
+ : m_unexpect(std::forward(args)...),
+ m_has_val(false)
+ {
+ }
+
+ template<
+ class U,
+ class... Args,
+ detail::enable_if_t&, Args&&...>::value>* = nullptr>
+ constexpr explicit expected_storage_base(unexpect_t, std::initializer_list il, Args&&... args)
+ : m_unexpect(il, std::forward(args)...),
+ m_has_val(false)
+ {
+ }
+
+ ~expected_storage_base()
+ {
+ if (!m_has_val)
+ {
+ m_unexpect.~unexpected();
+ }
+ }
+
+ union
+ {
+ unexpected m_unexpect;
+ char m_dummy;
+ };
+ bool m_has_val;
+};
+
+// This base class provides some handy member functions which can be used in
+// further derived classes
+template
+struct expected_operations_base : expected_storage_base
+{
+ using expected_storage_base::expected_storage_base;
+
+ template
+ void construct(Args&&... args) noexcept
+ {
+ new (std::addressof(this->m_val)) T(std::forward(args)...);
+ this->m_has_val = true;
+ }
+
+ template
+ void construct_with(Rhs&& rhs) noexcept
+ {
+ new (std::addressof(this->m_val)) T(std::forward(rhs).get());
+ this->m_has_val = true;
+ }
+
+ template
+ void construct_error(Args&&... args) noexcept
+ {
+ new (std::addressof(this->m_unexpect)) unexpected(std::forward(args)...);
+ this->m_has_val = false;
+ }
+
+#ifdef TL_EXPECTED_EXCEPTIONS_ENABLED
+
+ // These assign overloads ensure that the most efficient assignment
+ // implementation is used while maintaining the strong exception guarantee.
+ // The problematic case is where rhs has a value, but *this does not.
+ //
+ // This overload handles the case where we can just copy-construct `T`
+ // directly into place without throwing.
+ template::value>* = nullptr>
+ void assign(const expected_operations_base& rhs) noexcept
+ {
+ if (!this->m_has_val && rhs.m_has_val)
+ {
+ geterr().~unexpected();
+ construct(rhs.get());
+ }
+ else
+ {
+ assign_common(rhs);
+ }
+ }
+
+ // This overload handles the case where we can attempt to create a copy of
+ // `T`, then no-throw move it into place if the copy was successful.
+ template<
+ class U = T,
+ detail::enable_if_t<
+ !std::is_nothrow_copy_constructible::value && std::is_nothrow_move_constructible