diff --git a/.travis.yml b/.travis.yml index 2359d7b..ec79ead 100644 --- a/.travis.yml +++ b/.travis.yml @@ -254,4 +254,4 @@ install: - if [ "$CXX" = "clang++" ]; then export CXX="$COMPILER -stdlib=libc++"; fi - if [ "$CXX" = "g++" ]; then export CXX="$COMPILER"; fi -script: mkdir build && cd build && cmake .. && make && ./tests +script: mkdir build && cd build && cmake -DCXXSTD=$CXXSTD .. && make && ./tests diff --git a/conanfile.py b/conanfile.py new file mode 100644 index 0000000..ffb5eb4 --- /dev/null +++ b/conanfile.py @@ -0,0 +1,34 @@ +from conans import ConanFile, CMake, tools + +class ExpectedConan(ConanFile): + name = "expected" + version = "master" + license = "CC0-1.0" + author = "Simon Brand " + url = "https://github.com/TartanLlama/expected" + description = "C++11/14/17 std::expected with functional-style extensions" + settings = "os", "compiler", "build_type", "arch" + generators = "cmake" + exports_sources = "*" + + def source(self): + tools.replace_in_file('CMakeLists.txt', 'project(expected)', + '''project(expected) +include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) +conan_basic_setup() + ''') + + def configure_cmake(self): + cmake = CMake(self) + cmake.configure() + return cmake + + def build(self): + cmake = self.configure_cmake() + cmake.build() + + if not tools.cross_building(self.settings): + 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 diff --git a/test_package/CMakeLists.txt b/test_package/CMakeLists.txt new file mode 100644 index 0000000..f9b762f --- /dev/null +++ b/test_package/CMakeLists.txt @@ -0,0 +1,14 @@ +cmake_minimum_required(VERSION 2.8.12) +project(PackageTest CXX) + +include(${CMAKE_BINARY_DIR}/conanbuildinfo.cmake) +conan_basic_setup() + +add_executable(example example.cpp) +target_link_libraries(example ${CONAN_LIBS}) + +# CTest is a testing tool that can be used to test your project. +# enable_testing() +# add_test(NAME example +# WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/bin +# COMMAND example) \ No newline at end of file diff --git a/test_package/conanfile.py b/test_package/conanfile.py new file mode 100644 index 0000000..6647df7 --- /dev/null +++ b/test_package/conanfile.py @@ -0,0 +1,25 @@ +import os + +from conans import ConanFile, CMake, tools + + +class ExpectedTestConan(ConanFile): + settings = "os", "compiler", "build_type", "arch" + generators = "cmake" + + def build(self): + cmake = CMake(self) + # Current dir is "test_package/build/" and CMakeLists.txt is + # in "test_package" + cmake.configure() + cmake.build() + + def imports(self): + self.copy("*.dll", dst="bin", src="bin") + self.copy("*.dylib*", dst="bin", src="lib") + self.copy('*.so*', dst='bin', src='lib') + + def test(self): + if not tools.cross_building(self.settings): + os.chdir("bin") + self.run(".%sexample" % os.sep) \ No newline at end of file diff --git a/test_package/example.cpp b/test_package/example.cpp new file mode 100644 index 0000000..ae139d8 --- /dev/null +++ b/test_package/example.cpp @@ -0,0 +1,16 @@ +#include + +tl::expected maybe_do_something(int i) { + if (i < 5) { + return 0; + } + else { + return tl::make_unexpected("Uh oh"); + } +} + +int main(int argc, char** argv) { + (void)argv; + + return maybe_do_something(0).value_or(-1); +} \ No newline at end of file diff --git a/tests/issues.cpp b/tests/issues.cpp index b271140..688a63d 100644 --- a/tests/issues.cpp +++ b/tests/issues.cpp @@ -105,4 +105,25 @@ TEST_CASE("Issue 42", "[issues.42]") { TEST_CASE("Issue 43", "[issues.43]") { auto result = tl::expected{}; result = tl::make_unexpected(std::string{ "foo" }); -} \ No newline at end of file +} + +#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/test.cpp b/tests/test.cpp new file mode 100644 index 0000000..e80f477 --- /dev/null +++ b/tests/test.cpp @@ -0,0 +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); +} \ No newline at end of file diff --git a/tl/expected.hpp b/tl/expected.hpp index ad8dc81..6b1adb4 100644 --- a/tl/expected.hpp +++ b/tl/expected.hpp @@ -460,9 +460,9 @@ struct expected_storage_base { } } union { - char m_no_init; T m_val; unexpected m_unexpect; + char m_no_init; }; bool m_has_val; }; @@ -501,9 +501,9 @@ template struct expected_storage_base { ~expected_storage_base() = default; union { - char m_no_init; T m_val; unexpected m_unexpect; + char m_no_init; }; bool m_has_val; }; @@ -547,9 +547,9 @@ template struct expected_storage_base { } union { - char m_no_init; T m_val; unexpected m_unexpect; + char m_no_init; }; bool m_has_val; }; @@ -591,9 +591,9 @@ template struct expected_storage_base { } } union { - char m_no_init; T m_val; unexpected m_unexpect; + char m_no_init; }; bool m_has_val; }; @@ -622,8 +622,8 @@ template struct expected_storage_base { ~expected_storage_base() = default; struct dummy {}; union { - dummy m_val; unexpected m_unexpect; + dummy m_val; }; bool m_has_val; }; @@ -656,8 +656,8 @@ template struct expected_storage_base { } union { - char m_dummy; unexpected m_unexpect; + char m_dummy; }; bool m_has_val; }; @@ -837,7 +837,7 @@ struct expected_operations_base : expected_storage_base { } #endif - constexpr void destroy_val() { + TL_EXPECTED_11_CONSTEXPR void destroy_val() { get().~T(); } }; @@ -892,7 +892,7 @@ struct expected_operations_base : expected_storage_base { } #endif - constexpr void destroy_val() { + TL_EXPECTED_11_CONSTEXPR void destroy_val() { //no-op } }; @@ -1229,17 +1229,17 @@ class expected : private detail::expected_move_assign_base, template ::value> * = nullptr> - U &val() { + TL_EXPECTED_11_CONSTEXPR U &val() { return this->m_val; } - unexpected &err() { return this->m_unexpect; } + TL_EXPECTED_11_CONSTEXPR unexpected &err() { return this->m_unexpect; } template ::value> * = nullptr> - const U &val() const { + constexpr const U &val() const { return this->m_val; } - const unexpected &err() const { return this->m_unexpect; } + constexpr const unexpected &err() const { return this->m_unexpect; } using impl_base = detail::expected_move_assign_base; using ctor_base = detail::expected_default_ctor_base; @@ -1296,7 +1296,7 @@ public: /// \synopsis template \nconstexpr auto and_then(F &&f) &; template TL_EXPECTED_11_CONSTEXPR auto - and_then(F &&f) & -> decltype(and_then_impl(*this, std::forward(f))) { + and_then(F &&f) & -> decltype(and_then_impl(std::declval(), std::forward(f))) { return and_then_impl(*this, std::forward(f)); } @@ -1304,7 +1304,7 @@ public: /// \synopsis template \nconstexpr auto and_then(F &&f) &&; template TL_EXPECTED_11_CONSTEXPR auto and_then(F &&f) && -> decltype( - and_then_impl(std::move(*this), std::forward(f))) { + and_then_impl(std::declval(), std::forward(f))) { return and_then_impl(std::move(*this), std::forward(f)); } @@ -1312,7 +1312,7 @@ public: /// \synopsis template \nconstexpr auto and_then(F &&f) const &; template constexpr auto and_then(F &&f) const & -> decltype( - and_then_impl(*this, std::forward(f))) { + and_then_impl(std::declval(), std::forward(f))) { return and_then_impl(*this, std::forward(f)); } @@ -1321,7 +1321,7 @@ public: /// \synopsis template \nconstexpr auto and_then(F &&f) const &&; template constexpr auto and_then(F &&f) const && -> decltype( - and_then_impl(std::move(*this), std::forward(f))) { + and_then_impl(std::declval(), std::forward(f))) { return and_then_impl(std::move(*this), std::forward(f)); } #endif @@ -2053,7 +2053,7 @@ constexpr auto and_then_impl(Exp &&exp, F &&f) { return exp.has_value() ? detail::invoke(std::forward(f), *std::forward(exp)) - : Ret(unexpect, exp.error()); + : Ret(unexpect, std::forward(exp).error()); } template ::value, "F must return an expected"); return exp.has_value() ? detail::invoke(std::forward(f)) - : Ret(unexpect, exp.error()); + : Ret(unexpect, std::forward(exp).error()); } #else template struct TC; @@ -2076,7 +2076,7 @@ auto and_then_impl(Exp &&exp, F &&f) -> Ret { return exp.has_value() ? detail::invoke(std::forward(f), *std::forward(exp)) - : Ret(unexpect, exp.error()); + : Ret(unexpect, std::forward(exp).error()); } template Ret { static_assert(detail::is_expected::value, "F must return an expected"); return exp.has_value() ? detail::invoke(std::forward(f)) - : Ret(unexpect, exp.error()); + : Ret(unexpect, std::forward(exp).error()); } #endif