Cmake love (#55)

* Do CMake properly

* Remove uneeded file

* Add patch to version

* Init submodule in appveyor

* Use FetchContent

* Up CMake version requirement

* Use Xenial on Travis instead of Trusty

* Tidy traits

* Use Xenial repos
This commit is contained in:
Simon Brand
2019-06-25 11:20:05 +01:00
committed by GitHub
parent 1fb40294e6
commit 4f541d7221
20 changed files with 392 additions and 342 deletions

View File

@@ -3,6 +3,7 @@ os:
- Visual Studio 2017
build_script:
- git submodule update --init --recursive
- mkdir build
- cd build
- cmake ..

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "cmake/tl-cmake"]
path = cmake/tl-cmake
url = https://github.com/TartanLlama/tl-cmake.git

View File

@@ -1,6 +1,6 @@
language: cpp
dist: trusty
dist: xenial
sudo: false
matrix:
@@ -97,7 +97,7 @@ matrix:
addons:
apt:
sources:
- sourceline: "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.9 main"
- sourceline: "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-3.9 main"
key_url: "http://apt.llvm.org/llvm-snapshot.gpg.key"
- ubuntu-toolchain-r-test
packages:
@@ -108,7 +108,7 @@ matrix:
addons:
apt:
sources:
- llvm-toolchain-trusty-4.0
- llvm-toolchain-xenial-4.0
- ubuntu-toolchain-r-test
packages:
- clang++-4.0
@@ -118,7 +118,7 @@ matrix:
addons:
apt:
sources:
- llvm-toolchain-trusty-5.0
- llvm-toolchain-xenial-5.0
- ubuntu-toolchain-r-test
packages:
- clang++-5.0
@@ -128,7 +128,7 @@ matrix:
addons:
apt:
sources:
- llvm-toolchain-trusty-6.0
- llvm-toolchain-xenial-6.0
- ubuntu-toolchain-r-test
packages:
- clang++-6.0
@@ -211,7 +211,7 @@ matrix:
addons:
apt:
sources:
- sourceline: "deb http://apt.llvm.org/trusty/ llvm-toolchain-trusty-3.9 main"
- sourceline: "deb http://apt.llvm.org/xenial/ llvm-toolchain-xenial-3.9 main"
key_url: "http://apt.llvm.org/llvm-snapshot.gpg.key"
- ubuntu-toolchain-r-test
packages:
@@ -222,7 +222,7 @@ matrix:
addons:
apt:
sources:
- llvm-toolchain-trusty-4.0
- llvm-toolchain-xenial-4.0
- ubuntu-toolchain-r-test
packages:
- clang++-4.0
@@ -232,7 +232,7 @@ matrix:
addons:
apt:
sources:
- llvm-toolchain-trusty-5.0
- llvm-toolchain-xenial-5.0
- ubuntu-toolchain-r-test
packages:
- clang++-5.0
@@ -242,7 +242,7 @@ matrix:
addons:
apt:
sources:
- llvm-toolchain-trusty-6.0
- llvm-toolchain-xenial-6.0
- ubuntu-toolchain-r-test
packages:
- clang++-6.0

View File

@@ -1,12 +1,23 @@
cmake_minimum_required(VERSION 3.0)
cmake_minimum_required(VERSION 3.11)
project(expected)
project(tl-expected VERSION 1.0.0 LANGUAGES CXX)
option(EXPECTED_ENABLE_TESTS "Enable tests." ON)
add_library(expected INTERFACE)
target_sources(expected INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/tl/expected.hpp)
target_include_directories(expected INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/tl)
include(FetchContent)
FetchContent_Declare(
tl_cmake
GIT_REPOSITORY https://github.com/TartanLlama/tl-cmake.git
)
FetchContent_GetProperties(tl_cmake)
if(NOT tl_cmake_POPULATED)
FetchContent_Populate(tl_cmake)
set(CMAKE_MODULE_PATH ${tl_cmake_SOURCE_DIR} ${CMAKE_MODULE_PATH})
endif()
include(add-tl)
tl_add_library(expected SOURCES
include/tl/expected.hpp)
# Prepare "Catch" library for other executables
set(CATCH_INCLUDE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/test)

1
cmake/tl-cmake Submodule

Submodule cmake/tl-cmake added at 284c6a3f0f

View File

@@ -0,0 +1,3 @@
@PACKAGE_INIT@
include("${CMAKE_CURRENT_LIST_DIR}/tl-expected-targets.cmake")

View File

@@ -12,8 +12,8 @@ class ExpectedConan(ConanFile):
exports_sources = "*"
def source(self):
tools.replace_in_file('CMakeLists.txt', 'project(expected)',
'''project(expected)
tools.replace_in_file('CMakeLists.txt', 'project(tl-expected)',
'''project(tl-expected)
include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake)
conan_basic_setup()
''')
@@ -31,4 +31,4 @@ conan_basic_setup()
self.run('%s/bin/tests' % self.build_folder)
def package(self):
self.copy('*.hpp', dst='include/tl', src='tl')
self.copy('*.hpp', dst='include/tl', src='include/tl')

View File

@@ -14,8 +14,9 @@
#ifndef TL_EXPECTED_HPP
#define TL_EXPECTED_HPP
#define TL_EXPECTED_VERSION_MAJOR 0
#define TL_EXPECTED_VERSION_MINOR 2
#define TL_EXPECTED_VERSION_MAJOR 1
#define TL_EXPECTED_VERSION_MINOR 0
#define TL_EXPECTED_VERSION_PATCH 0
#include <exception>
#include <functional>
@@ -234,24 +235,55 @@ template <class...> struct conjunction : std::true_type {};
template <class B> struct conjunction<B> : B {};
template <class B, class... Bs>
struct conjunction<B, Bs...>
: std::conditional<bool(B::value), conjunction<Bs...>, B>::type {};
: std::conditional<bool(B::value), conjunction<Bs...>, 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 <class T> struct is_pointer_to_non_const_member_func : std::false_type {};
template <class T, class Ret, class... Args>
struct is_pointer_to_non_const_member_func<Ret(T::*) (Args...)> : std::true_type {};
template <class T, class Ret, class... Args>
struct is_pointer_to_non_const_member_func<Ret(T::*) (Args...)&> : std::true_type {};
template <class T, class Ret, class... Args>
struct is_pointer_to_non_const_member_func<Ret(T::*) (Args...) &&> : std::true_type {};
template <class T, class Ret, class... Args>
struct is_pointer_to_non_const_member_func<Ret(T::*) (Args...) volatile> : std::true_type {};
template <class T, class Ret, class... Args>
struct is_pointer_to_non_const_member_func<Ret(T::*) (Args...) volatile &> : std::true_type {};
template <class T, class Ret, class... Args>
struct is_pointer_to_non_const_member_func<Ret(T::*) (Args...) volatile &&> : std::true_type {};
template <class T> struct is_const_or_const_ref : std::false_type {};
template <class T> struct is_const_or_const_ref<T const&> : std::true_type {};
template <class T> struct is_const_or_const_ref<T const> : std::true_type {};
#endif
// std::invoke from C++17
// https://stackoverflow.com/questions/38288042/c11-14-invoke-workaround
template <typename Fn, typename... Args,
typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>{}>,
int = 0>
constexpr auto invoke(Fn &&f, Args &&... args) noexcept(
#ifdef TL_TRAITS_LIBCXX_MEM_FN_WORKAROUND
typename = enable_if_t<!(is_pointer_to_non_const_member_func<Fn>::value
&& is_const_or_const_ref<Args...>::value)>,
#endif
typename = enable_if_t<std::is_member_pointer<decay_t<Fn>>::value>,
int = 0>
constexpr auto invoke(Fn && f, Args && ... args) noexcept(
noexcept(std::mem_fn(f)(std::forward<Args>(args)...)))
-> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
-> decltype(std::mem_fn(f)(std::forward<Args>(args)...)) {
return std::mem_fn(f)(std::forward<Args>(args)...);
}
template <typename Fn, typename... Args,
typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>{}>>
constexpr auto invoke(Fn &&f, Args &&... args) noexcept(
typename = enable_if_t<!std::is_member_pointer<decay_t<Fn>>::value>>
constexpr auto invoke(Fn && f, Args && ... args) noexcept(
noexcept(std::forward<Fn>(f)(std::forward<Args>(args)...)))
-> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
-> decltype(std::forward<Fn>(f)(std::forward<Args>(args)...)) {
return std::forward<Fn>(f)(std::forward<Args>(args)...);
}
@@ -260,8 +292,8 @@ template <class F, class, class... Us> struct invoke_result_impl;
template <class F, class... Us>
struct invoke_result_impl<
F, decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
Us...> {
F, decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...), void()),
Us...> {
using type = decltype(detail::invoke(std::declval<F>(), std::declval<Us>()...));
};
@@ -279,68 +311,68 @@ template <class T, class U = T> 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 {};
// if swap ADL finds this then it would call std::swap otherwise (same
// signature)
struct tag {};
template <class T> tag swap(T &, T &);
template <class T, std::size_t N> tag swap(T (&a)[N], T (&b)[N]);
template <class T> tag swap(T&, T&);
template <class T, std::size_t N> 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 <class, class> std::false_type can_swap(...) noexcept(false);
template <class T, class U,
class = decltype(swap(std::declval<T &>(), std::declval<U &>()))>
std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T &>(),
std::declval<U &>())));
// helper functions to test if an unqualified swap is possible, and if it
// becomes std::swap
template <class, class> std::false_type can_swap(...) noexcept(false);
template <class T, class U,
class = decltype(swap(std::declval<T&>(), std::declval<U&>()))>
std::true_type can_swap(int) noexcept(noexcept(swap(std::declval<T&>(),
std::declval<U&>())));
template <class, class> std::false_type uses_std(...);
template <class T, class U>
std::is_same<decltype(swap(std::declval<T &>(), std::declval<U &>())), tag>
uses_std(int);
template <class, class> std::false_type uses_std(...);
template <class T, class U>
std::is_same<decltype(swap(std::declval<T&>(), std::declval<U&>())), tag>
uses_std(int);
template <class T>
struct is_std_swap_noexcept
template <class T>
struct is_std_swap_noexcept
: std::integral_constant<bool,
std::is_nothrow_move_constructible<T>::value &&
std::is_nothrow_move_assignable<T>::value> {};
std::is_nothrow_move_constructible<T>::value&&
std::is_nothrow_move_assignable<T>::value> {};
template <class T, std::size_t N>
struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
template <class T, std::size_t N>
struct is_std_swap_noexcept<T[N]> : is_std_swap_noexcept<T> {};
template <class T, class U>
struct is_adl_swap_noexcept
template <class T, class U>
struct is_adl_swap_noexcept
: std::integral_constant<bool, noexcept(can_swap<T, U>(0))> {};
} // namespace swap_adl_tests
template <class T, class U = T>
struct is_swappable
: std::integral_constant<
bool,
decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
(!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
(std::is_move_assignable<T>::value &&
std::is_move_constructible<T>::value))> {};
: std::integral_constant<
bool,
decltype(detail::swap_adl_tests::can_swap<T, U>(0))::value &&
(!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value ||
(std::is_move_assignable<T>::value &&
std::is_move_constructible<T>::value))> {};
template <class T, std::size_t N>
struct is_swappable<T[N], T[N]>
: std::integral_constant<
bool,
decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
(!decltype(
detail::swap_adl_tests::uses_std<T[N], T[N]>(0))::value ||
is_swappable<T, T>::value)> {};
: std::integral_constant<
bool,
decltype(detail::swap_adl_tests::can_swap<T[N], T[N]>(0))::value &&
(!decltype(
detail::swap_adl_tests::uses_std<T[N], T[N]>(0))::value ||
is_swappable<T, T>::value)> {};
template <class T, class U = T>
struct is_nothrow_swappable
: std::integral_constant<
bool,
is_swappable<T, U>::value &&
((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value
&&detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
(!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
detail::swap_adl_tests::is_adl_swap_noexcept<T,
U>::value))> {
: std::integral_constant<
bool,
is_swappable<T, U>::value &&
((decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value
&& detail::swap_adl_tests::is_std_swap_noexcept<T>::value) ||
(!decltype(detail::swap_adl_tests::uses_std<T, U>(0))::value &&
detail::swap_adl_tests::is_adl_swap_noexcept<T,
U>::value))> {
};
#endif
#endif
@@ -2442,5 +2474,4 @@ void swap(expected<T, E> &lhs,
}
} // namespace tl
#define TL_OPTIONAL_EXPECTED_MUTEX
#endif

View File

@@ -1,5 +1,5 @@
#include "catch.hpp"
#include "expected.hpp"
#include <tl/expected.hpp>
TEST_CASE("Simple assignment", "[assignment.simple]") {
tl::expected<int, int> e1 = 42;

View File

@@ -1,5 +1,5 @@
#include "catch.hpp"
#include "expected.hpp"
#include <tl/expected.hpp>
#include <string>

View File

@@ -1,5 +1,5 @@
#include "catch.hpp"
#include "expected.hpp"
#include <tl/expected.hpp>
TEST_CASE("Constexpr", "[constexpr]") {
//TODO

View File

@@ -1,5 +1,5 @@
#include "catch.hpp"
#include "expected.hpp"
#include <tl/expected.hpp>
#include <vector>
#include <type_traits>

View File

@@ -1,5 +1,5 @@
#include "catch.hpp"
#include "expected.hpp"
#include <tl/expected.hpp>
#include <memory>
#include <vector>
#include <tuple>

View File

@@ -1,5 +1,5 @@
#include "catch.hpp"
#include "expected.hpp"
#include <tl/expected.hpp>
#define TOKENPASTE(x, y) x##y
#define TOKENPASTE2(x, y) TOKENPASTE(x, y)

View File

@@ -1,129 +1,129 @@
#include "catch.hpp"
#include "expected.hpp"
#include <string>
using std::string;
tl::expected<int, string> getInt3(int val) { return val; }
tl::expected<int, string> getInt2(int val) { return val; }
tl::expected<int, string> getInt1() { return getInt2(5).and_then(getInt3); }
TEST_CASE("Issue 1", "[issues.1]") { getInt1(); }
tl::expected<int, int> operation1() { return 42; }
tl::expected<std::string, int> operation2(int const val) { return "Bananas"; }
TEST_CASE("Issue 17", "[issues.17]") {
auto const intermediate_result = operation1();
intermediate_result.and_then(operation2);
}
struct a {};
struct b : a {};
auto doit() -> tl::expected<std::unique_ptr<b>, int> {
return tl::make_unexpected(0);
}
TEST_CASE("Issue 23", "[issues.23]") {
tl::expected<std::unique_ptr<a>, int> msg = doit();
REQUIRE(!msg.has_value());
}
TEST_CASE("Issue 26", "[issues.26]") {
tl::expected<a, int> exp = tl::expected<b, int>(tl::unexpect, 0);
REQUIRE(!exp.has_value());
}
struct foo {
foo() = default;
foo(foo &) = delete;
foo(foo &&){};
};
TEST_CASE("Issue 29", "[issues.29]") {
std::vector<foo> v;
v.emplace_back();
tl::expected<std::vector<foo>, int> ov = std::move(v);
REQUIRE(ov->size() == 1);
}
tl::expected<int, std::string> error() {
return tl::make_unexpected(std::string("error1 "));
}
std::string maperror(std::string s) { return s + "maperror "; }
TEST_CASE("Issue 30", "[issues.30]") {
error().map_error(maperror);
}
struct i31{
int i;
};
TEST_CASE("Issue 31", "[issues.31]") {
const tl::expected<i31, int> a = i31{42};
a->i;
tl::expected< void, std::string > result;
tl::expected< void, std::string > result2 = result;
result2 = result;
}
TEST_CASE("Issue 33", "[issues.33]") {
tl::expected<void, int> res {tl::unexpect, 0};
REQUIRE(!res);
res = res.map_error([](int i) { return 42; });
REQUIRE(res.error() == 42);
}
tl::expected<void, std::string> voidWork() { return {}; }
tl::expected<int, std::string> work2() { return 42; }
void errorhandling(std::string){}
TEST_CASE("Issue 34", "[issues.34]") {
tl::expected <int, std::string> result = voidWork ()
.and_then (work2);
result.map_error ([&] (std::string result) {errorhandling (result);});
}
struct non_copyable {
non_copyable(non_copyable&&) = default;
non_copyable(non_copyable const&) = delete;
non_copyable() = default;
};
TEST_CASE("Issue 42", "[issues.42]") {
tl::expected<non_copyable,int>{}.map([](non_copyable) {});
}
TEST_CASE("Issue 43", "[issues.43]") {
auto result = tl::expected<void, std::string>{};
result = tl::make_unexpected(std::string{ "foo" });
}
#if !(__GNUC__ <= 5)
#include <memory>
using MaybeDataPtr = tl::expected<int, std::unique_ptr<int>>;
MaybeDataPtr test(int i) noexcept
{
return std::move(i);
}
MaybeDataPtr test2(int i) noexcept
{
return std::move(i);
}
TEST_CASE("Issue 49", "[issues.49]") {
auto m = test(10)
.and_then(test2);
}
#include "catch.hpp"
#include <tl/expected.hpp>
#include <string>
using std::string;
tl::expected<int, string> getInt3(int val) { return val; }
tl::expected<int, string> getInt2(int val) { return val; }
tl::expected<int, string> getInt1() { return getInt2(5).and_then(getInt3); }
TEST_CASE("Issue 1", "[issues.1]") { getInt1(); }
tl::expected<int, int> operation1() { return 42; }
tl::expected<std::string, int> operation2(int const val) { return "Bananas"; }
TEST_CASE("Issue 17", "[issues.17]") {
auto const intermediate_result = operation1();
intermediate_result.and_then(operation2);
}
struct a {};
struct b : a {};
auto doit() -> tl::expected<std::unique_ptr<b>, int> {
return tl::make_unexpected(0);
}
TEST_CASE("Issue 23", "[issues.23]") {
tl::expected<std::unique_ptr<a>, int> msg = doit();
REQUIRE(!msg.has_value());
}
TEST_CASE("Issue 26", "[issues.26]") {
tl::expected<a, int> exp = tl::expected<b, int>(tl::unexpect, 0);
REQUIRE(!exp.has_value());
}
struct foo {
foo() = default;
foo(foo &) = delete;
foo(foo &&){};
};
TEST_CASE("Issue 29", "[issues.29]") {
std::vector<foo> v;
v.emplace_back();
tl::expected<std::vector<foo>, int> ov = std::move(v);
REQUIRE(ov->size() == 1);
}
tl::expected<int, std::string> error() {
return tl::make_unexpected(std::string("error1 "));
}
std::string maperror(std::string s) { return s + "maperror "; }
TEST_CASE("Issue 30", "[issues.30]") {
error().map_error(maperror);
}
struct i31{
int i;
};
TEST_CASE("Issue 31", "[issues.31]") {
const tl::expected<i31, int> a = i31{42};
a->i;
tl::expected< void, std::string > result;
tl::expected< void, std::string > result2 = result;
result2 = result;
}
TEST_CASE("Issue 33", "[issues.33]") {
tl::expected<void, int> res {tl::unexpect, 0};
REQUIRE(!res);
res = res.map_error([](int i) { return 42; });
REQUIRE(res.error() == 42);
}
tl::expected<void, std::string> voidWork() { return {}; }
tl::expected<int, std::string> work2() { return 42; }
void errorhandling(std::string){}
TEST_CASE("Issue 34", "[issues.34]") {
tl::expected <int, std::string> result = voidWork ()
.and_then (work2);
result.map_error ([&] (std::string result) {errorhandling (result);});
}
struct non_copyable {
non_copyable(non_copyable&&) = default;
non_copyable(non_copyable const&) = delete;
non_copyable() = default;
};
TEST_CASE("Issue 42", "[issues.42]") {
tl::expected<non_copyable,int>{}.map([](non_copyable) {});
}
TEST_CASE("Issue 43", "[issues.43]") {
auto result = tl::expected<void, std::string>{};
result = tl::make_unexpected(std::string{ "foo" });
}
#if !(__GNUC__ <= 5)
#include <memory>
using MaybeDataPtr = tl::expected<int, std::unique_ptr<int>>;
MaybeDataPtr test(int i) noexcept
{
return std::move(i);
}
MaybeDataPtr test2(int i) noexcept
{
return std::move(i);
}
TEST_CASE("Issue 49", "[issues.49]") {
auto m = test(10)
.and_then(test2);
}
#endif

View File

@@ -1,5 +1,5 @@
#include "catch.hpp"
#include "expected.hpp"
#include <tl/expected.hpp>
TEST_CASE("Noexcept", "[noexcept]") {
//TODO

View File

@@ -1,5 +1,5 @@
#include "catch.hpp"
#include "expected.hpp"
#include <tl/expected.hpp>
struct move_detector {
move_detector() = default;

View File

@@ -1,5 +1,5 @@
#include "catch.hpp"
#include "expected.hpp"
#include <tl/expected.hpp>
TEST_CASE("Relational operators", "[relops]") {
//TODO

View File

@@ -1,101 +1,101 @@
#include "catch.hpp"
#include "expected.hpp"
struct no_throw {
no_throw(std::string i) : i(i) {}
std::string i;
};
struct canthrow_move {
canthrow_move(std::string i) : i(i) {}
canthrow_move(canthrow_move const &) = default;
canthrow_move(canthrow_move &&other) noexcept(false) : i(other.i) {}
canthrow_move &operator=(canthrow_move &&) = default;
std::string i;
};
bool should_throw = false;
struct willthrow_move {
willthrow_move(std::string i) : i(i) {}
willthrow_move(willthrow_move const &) = default;
willthrow_move(willthrow_move &&other) : i(other.i) {
if (should_throw)
throw 0;
}
willthrow_move &operator=(willthrow_move &&) = default;
std::string i;
};
static_assert(tl::detail::is_swappable<no_throw>::value, "");
template <class T1, class T2> void swap_test() {
std::string s1 = "abcdefghijklmnopqrstuvwxyz";
std::string s2 = "zyxwvutsrqponmlkjihgfedcba";
tl::expected<T1, T2> a{s1};
tl::expected<T1, T2> b{s2};
swap(a, b);
REQUIRE(a->i == s2);
REQUIRE(b->i == s1);
a = s1;
b = tl::unexpected<T2>(s2);
swap(a, b);
REQUIRE(a.error().i == s2);
REQUIRE(b->i == s1);
a = tl::unexpected<T2>(s1);
b = s2;
swap(a, b);
REQUIRE(a->i == s2);
REQUIRE(b.error().i == s1);
a = tl::unexpected<T2>(s1);
b = tl::unexpected<T2>(s2);
swap(a, b);
REQUIRE(a.error().i == s2);
REQUIRE(b.error().i == s1);
a = s1;
b = s2;
a.swap(b);
REQUIRE(a->i == s2);
REQUIRE(b->i == s1);
a = s1;
b = tl::unexpected<T2>(s2);
a.swap(b);
REQUIRE(a.error().i == s2);
REQUIRE(b->i == s1);
a = tl::unexpected<T2>(s1);
b = s2;
a.swap(b);
REQUIRE(a->i == s2);
REQUIRE(b.error().i == s1);
a = tl::unexpected<T2>(s1);
b = tl::unexpected<T2>(s2);
a.swap(b);
REQUIRE(a.error().i == s2);
REQUIRE(b.error().i == s1);
}
TEST_CASE("swap") {
swap_test<no_throw, no_throw>();
swap_test<no_throw, canthrow_move>();
swap_test<canthrow_move, no_throw>();
std::string s1 = "abcdefghijklmnopqrstuvwxyz";
std::string s2 = "zyxwvutsrqponmlkjihgfedcbaxxx";
tl::expected<no_throw, willthrow_move> a{s1};
tl::expected<no_throw, willthrow_move> b{tl::unexpect, s2};
should_throw = 1;
#ifdef _MSC_VER
//this seems to break catch on GCC and Clang
REQUIRE_THROWS(swap(a, b));
#endif
REQUIRE(a->i == s1);
REQUIRE(b.error().i == s2);
#include "catch.hpp"
#include <tl/expected.hpp>
struct no_throw {
no_throw(std::string i) : i(i) {}
std::string i;
};
struct canthrow_move {
canthrow_move(std::string i) : i(i) {}
canthrow_move(canthrow_move const &) = default;
canthrow_move(canthrow_move &&other) noexcept(false) : i(other.i) {}
canthrow_move &operator=(canthrow_move &&) = default;
std::string i;
};
bool should_throw = false;
struct willthrow_move {
willthrow_move(std::string i) : i(i) {}
willthrow_move(willthrow_move const &) = default;
willthrow_move(willthrow_move &&other) : i(other.i) {
if (should_throw)
throw 0;
}
willthrow_move &operator=(willthrow_move &&) = default;
std::string i;
};
static_assert(tl::detail::is_swappable<no_throw>::value, "");
template <class T1, class T2> void swap_test() {
std::string s1 = "abcdefghijklmnopqrstuvwxyz";
std::string s2 = "zyxwvutsrqponmlkjihgfedcba";
tl::expected<T1, T2> a{s1};
tl::expected<T1, T2> b{s2};
swap(a, b);
REQUIRE(a->i == s2);
REQUIRE(b->i == s1);
a = s1;
b = tl::unexpected<T2>(s2);
swap(a, b);
REQUIRE(a.error().i == s2);
REQUIRE(b->i == s1);
a = tl::unexpected<T2>(s1);
b = s2;
swap(a, b);
REQUIRE(a->i == s2);
REQUIRE(b.error().i == s1);
a = tl::unexpected<T2>(s1);
b = tl::unexpected<T2>(s2);
swap(a, b);
REQUIRE(a.error().i == s2);
REQUIRE(b.error().i == s1);
a = s1;
b = s2;
a.swap(b);
REQUIRE(a->i == s2);
REQUIRE(b->i == s1);
a = s1;
b = tl::unexpected<T2>(s2);
a.swap(b);
REQUIRE(a.error().i == s2);
REQUIRE(b->i == s1);
a = tl::unexpected<T2>(s1);
b = s2;
a.swap(b);
REQUIRE(a->i == s2);
REQUIRE(b.error().i == s1);
a = tl::unexpected<T2>(s1);
b = tl::unexpected<T2>(s2);
a.swap(b);
REQUIRE(a.error().i == s2);
REQUIRE(b.error().i == s1);
}
TEST_CASE("swap") {
swap_test<no_throw, no_throw>();
swap_test<no_throw, canthrow_move>();
swap_test<canthrow_move, no_throw>();
std::string s1 = "abcdefghijklmnopqrstuvwxyz";
std::string s2 = "zyxwvutsrqponmlkjihgfedcbaxxx";
tl::expected<no_throw, willthrow_move> a{s1};
tl::expected<no_throw, willthrow_move> b{tl::unexpect, s2};
should_throw = 1;
#ifdef _MSC_VER
//this seems to break catch on GCC and Clang
REQUIRE_THROWS(swap(a, b));
#endif
REQUIRE(a->i == s1);
REQUIRE(b.error().i == s2);
}

View File

@@ -1,32 +1,32 @@
struct no_throw {
no_throw(std::string i) : i(i) {}
std::string i;
};
struct canthrow_move {
canthrow_move(std::string i) : i(i) {}
canthrow_move(canthrow_move const &) = default;
canthrow_move(canthrow_move &&other) noexcept(false) : i(other.i) {}
canthrow_move &operator=(canthrow_move &&) = default;
std::string i;
};
bool should_throw = false;
struct willthrow_move {
willthrow_move(std::string i) : i(i) {}
willthrow_move(willthrow_move const &) = default;
willthrow_move(willthrow_move &&other) : i(other.i) {
if (should_throw)
throw 0;
}
willthrow_move &operator=(willthrow_move &&) = default;
std::string i;
};
int main() {
std::string s1 = "abcdefghijklmnopqrstuvwxyz";
std::string s2 = "zyxwvutsrqponmlkjihgfedcbaxxx";
tl::expected<no_throw, willthrow_move> a{s1};
tl::expected<no_throw, willthrow_move> b{tl::unexpect, s2};
should_throw = 1;
swap(a, b);
struct no_throw {
no_throw(std::string i) : i(i) {}
std::string i;
};
struct canthrow_move {
canthrow_move(std::string i) : i(i) {}
canthrow_move(canthrow_move const &) = default;
canthrow_move(canthrow_move &&other) noexcept(false) : i(other.i) {}
canthrow_move &operator=(canthrow_move &&) = default;
std::string i;
};
bool should_throw = false;
struct willthrow_move {
willthrow_move(std::string i) : i(i) {}
willthrow_move(willthrow_move const &) = default;
willthrow_move(willthrow_move &&other) : i(other.i) {
if (should_throw)
throw 0;
}
willthrow_move &operator=(willthrow_move &&) = default;
std::string i;
};
int main() {
std::string s1 = "abcdefghijklmnopqrstuvwxyz";
std::string s2 = "zyxwvutsrqponmlkjihgfedcbaxxx";
tl::expected<no_throw, willthrow_move> a{s1};
tl::expected<no_throw, willthrow_move> b{tl::unexpect, s2};
should_throw = 1;
swap(a, b);
}