diff --git a/CMakeLists.txt b/CMakeLists.txt index 80d1b08d..7b27b5f4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -280,6 +280,7 @@ list ( except.cpp except.hpp exe.hpp + expected.hpp extent.cpp extent.hpp fixed.cpp @@ -558,6 +559,7 @@ if (TESTS) encode/base endian exe + expected extent fixed float diff --git a/expected.hpp b/expected.hpp new file mode 100644 index 00000000..6f617421 --- /dev/null +++ b/expected.hpp @@ -0,0 +1,98 @@ +/* + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * Copyright 2019 Danny Robson + */ + +#include + + +/////////////////////////////////////////////////////////////////////////////// +namespace cruft { + class bad_expected_access : public std::exception { + public: + char const* what (void) const noexcept override + { + return "bad_expected_access"; + } + }; + + + template + struct expected { + using value_type = ValueT; + using error_type = ErrorT; + + expected (ValueT &&_value) + { + ::new (&m_store.value) ValueT (std::move (_value)); + m_valid = true; + } + + expected (ErrorT &&_error) + { + ::new (&m_store.error) ErrorT (std::move (_error)); + } + + ~expected () + { + if (m_valid) { + m_store.value.~ValueT (); + m_valid = false; + } else { + m_store.error.~ErrorT (); + } + } + + ValueT& + value (void)& + { + if (!m_valid) + throw bad_expected_access {}; + return m_store.value; + } + + ValueT&& + value (void)&& + { + if (!m_valid) + throw bad_expected_access {}; + return std::move (m_store.value); + } + + ErrorT& + error (void)& + { + if (m_valid) + throw bad_expected_access {}; + return m_store.error; + } + + ErrorT&& + error (void)&& + { + if (m_valid) + throw bad_expected_access {}; + return std::move (m_store.error); + } + + ValueT* operator-> (void)& { return &value (); } + ValueT& operator* (void)& { return value (); } + + operator bool() const noexcept; + + private: + union storage_t { + storage_t () {}; + ~storage_t () {}; + + char defer; + ValueT value; + ErrorT error; + } m_store; + + bool m_valid = false; + }; +}; \ No newline at end of file diff --git a/test/expected.cpp b/test/expected.cpp new file mode 100644 index 00000000..f45ee40b --- /dev/null +++ b/test/expected.cpp @@ -0,0 +1,34 @@ +#include "../expected.hpp" + +#include "../tap.hpp" + + + +int main () +{ + cruft::TAP::logger tap; + + tap.expect_nothrow ([] () { cruft::expected { "foo" }; }, "value construction succeeds"); + tap.expect_nothrow ([] () { cruft::expected { EPERM }; }, "error construction succeeds"); + + tap.expect_eq (cruft::expected { "foo" }.value (), "foo", "value matches"); + tap.expect_eq (cruft::expected { EPERM }.error (), EPERM, "error matches"); + + tap.expect_throw ( + [] () { + cruft::expected val { EPERM }; + val.value (); + }, + "value access fails when holding an error" + ); + + tap.expect_throw ( + [] () { + cruft::expected val { "foo" }; + val.error (); + }, + "error access fails when holding a value" + ); + + return tap.status (); +} \ No newline at end of file