diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..e6488b5 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "cmake/tl-cmake"] + path = cmake/tl-cmake + url = https://github.com/TartanLlama/tl-cmake.git diff --git a/CMakeLists.txt b/CMakeLists.txt index b8b58ab..e61a1b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,12 +1,14 @@ -cmake_minimum_required(VERSION 3.0) +cmake_minimum_required(VERSION 3.8) -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) +set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/tl-cmake" ${CMAKE_MODULE_PATH}) +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) diff --git a/cmake/tl-cmake b/cmake/tl-cmake new file mode 160000 index 0000000..284c6a3 --- /dev/null +++ b/cmake/tl-cmake @@ -0,0 +1 @@ +Subproject commit 284c6a3f0f61823cc3871b0f193e8df699e2c4ce diff --git a/cmake/tl-expected-config.cmake.in b/cmake/tl-expected-config.cmake.in new file mode 100644 index 0000000..12e3c35 --- /dev/null +++ b/cmake/tl-expected-config.cmake.in @@ -0,0 +1,3 @@ +@PACKAGE_INIT@ + +include("${CMAKE_CURRENT_LIST_DIR}/tl-expected-targets.cmake") \ No newline at end of file diff --git a/conanfile.py b/conanfile.py index ffb5eb4..2526c27 100644 --- a/conanfile.py +++ b/conanfile.py @@ -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') \ No newline at end of file + self.copy('*.hpp', dst='include/tl', src='include/tl') \ No newline at end of file diff --git a/tl/expected.hpp b/include/tl/expected.hpp similarity index 100% rename from tl/expected.hpp rename to include/tl/expected.hpp diff --git a/include/tl/expected.hpp.gch b/include/tl/expected.hpp.gch new file mode 100644 index 0000000..0e5ee44 Binary files /dev/null and b/include/tl/expected.hpp.gch differ diff --git a/tests/assignment.cpp b/tests/assignment.cpp index 6d22d1d..a674416 100644 --- a/tests/assignment.cpp +++ b/tests/assignment.cpp @@ -1,5 +1,5 @@ #include "catch.hpp" -#include "expected.hpp" +#include TEST_CASE("Simple assignment", "[assignment.simple]") { tl::expected e1 = 42; diff --git a/tests/bases.cpp b/tests/bases.cpp index dbe41eb..3f215af 100644 --- a/tests/bases.cpp +++ b/tests/bases.cpp @@ -1,5 +1,5 @@ #include "catch.hpp" -#include "expected.hpp" +#include #include diff --git a/tests/constexpr.cpp b/tests/constexpr.cpp index 0fc0433..c9f3510 100644 --- a/tests/constexpr.cpp +++ b/tests/constexpr.cpp @@ -1,5 +1,5 @@ #include "catch.hpp" -#include "expected.hpp" +#include TEST_CASE("Constexpr", "[constexpr]") { //TODO diff --git a/tests/constructors.cpp b/tests/constructors.cpp index 3f3ce03..b5c2b29 100644 --- a/tests/constructors.cpp +++ b/tests/constructors.cpp @@ -1,5 +1,5 @@ #include "catch.hpp" -#include "expected.hpp" +#include #include #include diff --git a/tests/emplace.cpp b/tests/emplace.cpp index c3b6023..1f8ec74 100644 --- a/tests/emplace.cpp +++ b/tests/emplace.cpp @@ -1,5 +1,5 @@ #include "catch.hpp" -#include "expected.hpp" +#include #include #include #include diff --git a/tests/extensions.cpp b/tests/extensions.cpp index 0d9a85b..18ca701 100644 --- a/tests/extensions.cpp +++ b/tests/extensions.cpp @@ -1,5 +1,5 @@ #include "catch.hpp" -#include "expected.hpp" +#include #define TOKENPASTE(x, y) x##y #define TOKENPASTE2(x, y) TOKENPASTE(x, y) diff --git a/tests/issues.cpp b/tests/issues.cpp index 688a63d..c2847ea 100644 --- a/tests/issues.cpp +++ b/tests/issues.cpp @@ -1,129 +1,129 @@ -#include "catch.hpp" -#include "expected.hpp" - -#include - -using std::string; - -tl::expected getInt3(int val) { return val; } - -tl::expected getInt2(int val) { return val; } - -tl::expected getInt1() { return getInt2(5).and_then(getInt3); } - -TEST_CASE("Issue 1", "[issues.1]") { getInt1(); } - -tl::expected operation1() { return 42; } - -tl::expected 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, int> { - return tl::make_unexpected(0); -} - -TEST_CASE("Issue 23", "[issues.23]") { - tl::expected, int> msg = doit(); - REQUIRE(!msg.has_value()); -} - -TEST_CASE("Issue 26", "[issues.26]") { - tl::expected exp = tl::expected(tl::unexpect, 0); - REQUIRE(!exp.has_value()); -} - -struct foo { - foo() = default; - foo(foo &) = delete; - foo(foo &&){}; -}; - -TEST_CASE("Issue 29", "[issues.29]") { - std::vector v; - v.emplace_back(); - tl::expected, int> ov = std::move(v); - REQUIRE(ov->size() == 1); -} - -tl::expected 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 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 res {tl::unexpect, 0}; - REQUIRE(!res); - res = res.map_error([](int i) { return 42; }); - REQUIRE(res.error() == 42); -} - - -tl::expected voidWork() { return {}; } -tl::expected work2() { return 42; } -void errorhandling(std::string){} - -TEST_CASE("Issue 34", "[issues.34]") { - tl::expected 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{}.map([](non_copyable) {}); -} - -TEST_CASE("Issue 43", "[issues.43]") { - auto result = tl::expected{}; - result = tl::make_unexpected(std::string{ "foo" }); -} - -#if !(__GNUC__ <= 5) -#include - -using MaybeDataPtr = tl::expected>; - -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 + +#include + +using std::string; + +tl::expected getInt3(int val) { return val; } + +tl::expected getInt2(int val) { return val; } + +tl::expected getInt1() { return getInt2(5).and_then(getInt3); } + +TEST_CASE("Issue 1", "[issues.1]") { getInt1(); } + +tl::expected operation1() { return 42; } + +tl::expected 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, int> { + return tl::make_unexpected(0); +} + +TEST_CASE("Issue 23", "[issues.23]") { + tl::expected, int> msg = doit(); + REQUIRE(!msg.has_value()); +} + +TEST_CASE("Issue 26", "[issues.26]") { + tl::expected exp = tl::expected(tl::unexpect, 0); + REQUIRE(!exp.has_value()); +} + +struct foo { + foo() = default; + foo(foo &) = delete; + foo(foo &&){}; +}; + +TEST_CASE("Issue 29", "[issues.29]") { + std::vector v; + v.emplace_back(); + tl::expected, int> ov = std::move(v); + REQUIRE(ov->size() == 1); +} + +tl::expected 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 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 res {tl::unexpect, 0}; + REQUIRE(!res); + res = res.map_error([](int i) { return 42; }); + REQUIRE(res.error() == 42); +} + + +tl::expected voidWork() { return {}; } +tl::expected work2() { return 42; } +void errorhandling(std::string){} + +TEST_CASE("Issue 34", "[issues.34]") { + tl::expected 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{}.map([](non_copyable) {}); +} + +TEST_CASE("Issue 43", "[issues.43]") { + auto result = tl::expected{}; + result = tl::make_unexpected(std::string{ "foo" }); +} + +#if !(__GNUC__ <= 5) +#include + +using MaybeDataPtr = tl::expected>; + +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 \ No newline at end of file diff --git a/tests/noexcept.cpp b/tests/noexcept.cpp index 5d1c1a0..cfa7489 100644 --- a/tests/noexcept.cpp +++ b/tests/noexcept.cpp @@ -1,5 +1,5 @@ #include "catch.hpp" -#include "expected.hpp" +#include TEST_CASE("Noexcept", "[noexcept]") { //TODO diff --git a/tests/observers.cpp b/tests/observers.cpp index 080e6be..eb9308e 100644 --- a/tests/observers.cpp +++ b/tests/observers.cpp @@ -1,5 +1,5 @@ #include "catch.hpp" -#include "expected.hpp" +#include struct move_detector { move_detector() = default; diff --git a/tests/relops.cpp b/tests/relops.cpp index 2314cee..de369d5 100644 --- a/tests/relops.cpp +++ b/tests/relops.cpp @@ -1,5 +1,5 @@ #include "catch.hpp" -#include "expected.hpp" +#include TEST_CASE("Relational operators", "[relops]") { //TODO diff --git a/tests/swap.cpp b/tests/swap.cpp index f031ce6..65314bf 100644 --- a/tests/swap.cpp +++ b/tests/swap.cpp @@ -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::value, ""); - -template void swap_test() { - std::string s1 = "abcdefghijklmnopqrstuvwxyz"; - std::string s2 = "zyxwvutsrqponmlkjihgfedcba"; - - tl::expected a{s1}; - tl::expected b{s2}; - swap(a, b); - REQUIRE(a->i == s2); - REQUIRE(b->i == s1); - - a = s1; - b = tl::unexpected(s2); - swap(a, b); - REQUIRE(a.error().i == s2); - REQUIRE(b->i == s1); - - a = tl::unexpected(s1); - b = s2; - swap(a, b); - REQUIRE(a->i == s2); - REQUIRE(b.error().i == s1); - - a = tl::unexpected(s1); - b = tl::unexpected(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(s2); - a.swap(b); - REQUIRE(a.error().i == s2); - REQUIRE(b->i == s1); - - a = tl::unexpected(s1); - b = s2; - a.swap(b); - REQUIRE(a->i == s2); - REQUIRE(b.error().i == s1); - - a = tl::unexpected(s1); - b = tl::unexpected(s2); - a.swap(b); - REQUIRE(a.error().i == s2); - REQUIRE(b.error().i == s1); -} - -TEST_CASE("swap") { - - swap_test(); - swap_test(); - swap_test(); - - std::string s1 = "abcdefghijklmnopqrstuvwxyz"; - std::string s2 = "zyxwvutsrqponmlkjihgfedcbaxxx"; - tl::expected a{s1}; - tl::expected 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 + +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::value, ""); + +template void swap_test() { + std::string s1 = "abcdefghijklmnopqrstuvwxyz"; + std::string s2 = "zyxwvutsrqponmlkjihgfedcba"; + + tl::expected a{s1}; + tl::expected b{s2}; + swap(a, b); + REQUIRE(a->i == s2); + REQUIRE(b->i == s1); + + a = s1; + b = tl::unexpected(s2); + swap(a, b); + REQUIRE(a.error().i == s2); + REQUIRE(b->i == s1); + + a = tl::unexpected(s1); + b = s2; + swap(a, b); + REQUIRE(a->i == s2); + REQUIRE(b.error().i == s1); + + a = tl::unexpected(s1); + b = tl::unexpected(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(s2); + a.swap(b); + REQUIRE(a.error().i == s2); + REQUIRE(b->i == s1); + + a = tl::unexpected(s1); + b = s2; + a.swap(b); + REQUIRE(a->i == s2); + REQUIRE(b.error().i == s1); + + a = tl::unexpected(s1); + b = tl::unexpected(s2); + a.swap(b); + REQUIRE(a.error().i == s2); + REQUIRE(b.error().i == s1); +} + +TEST_CASE("swap") { + + swap_test(); + swap_test(); + swap_test(); + + std::string s1 = "abcdefghijklmnopqrstuvwxyz"; + std::string s2 = "zyxwvutsrqponmlkjihgfedcbaxxx"; + tl::expected a{s1}; + tl::expected 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); } \ No newline at end of file diff --git a/tests/test.cpp b/tests/test.cpp index e80f477..0338ccb 100644 --- a/tests/test.cpp +++ b/tests/test.cpp @@ -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 a{s1}; - tl::expected 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 a{s1}; + tl::expected b{tl::unexpect, s2}; + should_throw = 1; + swap(a, b); } \ No newline at end of file