diff --git a/lib/include/srslte/common/expected.h b/lib/include/srslte/common/expected.h new file mode 100644 index 000000000..09c8ae0e0 --- /dev/null +++ b/lib/include/srslte/common/expected.h @@ -0,0 +1,173 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#ifndef SRSLTE_EXPECTED_H +#define SRSLTE_EXPECTED_H + +#include + +namespace srslte { + +enum class error : uint8_t { success, error }; + +struct default_error_t { +}; + +template +class expected +{ +public: + expected() : has_val(true), val(T{}) {} + expected(T&& t) : has_val(true), val(std::forward(t)) {} + expected(E&& e) : has_val(false), unexpected(std::forward(e)) {} + expected(const expected& other) : has_val(other.has_val) + { + if (has_val) { + construct_val(other.val); + } else { + construct_error(other.unexpected); + } + } + expected(expected&& other) noexcept : has_val(other.has_val) + { + if (has_val) { + construct_val(std::move(other.val)); + } else { + construct_error(std::move(other.unexpected)); + } + } + expected& operator=(const expected& other) + { + if (this != &other) { + expected(other).swap(*this); + } + return *this; + } + expected& operator=(expected&& other) noexcept + { + expected(std::move(other)).swap(*this); + return *this; + } + expected& operator=(const T& other) noexcept + { + if (not has_value()) { + unexpected.~E(); + has_val = true; + } + val = other; + return *this; + } + expected& operator=(T&& other) noexcept + { + if (not has_value()) { + unexpected.~E(); + has_val = true; + } + val = std::move(other); + return *this; + } + ~expected() { destroy(); } + + void set_error() + { + if (has_value()) { + val.~T(); + construct_error(E{}); + } else { + unexpected = E{}; + } + } + template + void set_error(U&& other) + { + if (has_value()) { + val.~T(); + construct_error(std::forward(other)); + } else { + unexpected = std::forward(other); + } + } + operator bool() const { return has_value(); } + bool has_value() const { return has_val; } + T* value() { return has_val ? &val : nullptr; } + const T* value() const { return has_val ? &val : nullptr; } + E* error() { return has_val ? nullptr : &unexpected; } + const E* error() const { return has_val ? nullptr : &unexpected; } + + void swap(expected& other) noexcept + { + using std::swap; + + if (has_value() and other.has_value()) { + swap(val, other.val); + } else if (not has_value() and not other.has_value()) { + swap(unexpected, other.unexpected); + } else if (has_value() and not other.has_value()) { + E err{std::move(other.unexpected)}; + other.unexpected.~E(); + other.construct_val(std::move(val)); + val.~T(); + construct_error(std::move(err)); + } else if (!bool(*this) && bool(other)) { + other.swap(*this); + } + } + +private: + void construct_val(const T& v) noexcept + { + has_val = true; + new (&val) T(v); + } + void construct_val(T&& v) noexcept + { + has_val = true; + new (&val) T(std::move(v)); + } + void construct_error(const E& e) noexcept + { + has_val = false; + new (&unexpected) E(e); + } + void construct_error(E&& e) noexcept + { + has_val = false; + new (&unexpected) E(std::move(e)); + } + void destroy() + { + if (has_value()) { + val.~T(); + } else { + unexpected.~E(); + } + } + + bool has_val = false; + union { + T val; + E unexpected; + }; +}; + +} // namespace srslte + +#endif // SRSLTE_EXPECTED_H diff --git a/lib/test/common/CMakeLists.txt b/lib/test/common/CMakeLists.txt index 61ab20db3..d64e62b54 100644 --- a/lib/test/common/CMakeLists.txt +++ b/lib/test/common/CMakeLists.txt @@ -93,3 +93,7 @@ add_test(fsm_test fsm_test) add_executable(choice_type_test choice_type_test.cc) target_link_libraries(choice_type_test srslte_common) add_test(choice_type_test choice_type_test) + +add_executable(expected_test expected_test.cc) +target_link_libraries(expected_test srslte_common) +add_test(expected_test expected_test) diff --git a/lib/test/common/expected_test.cc b/lib/test/common/expected_test.cc new file mode 100644 index 000000000..4747cba93 --- /dev/null +++ b/lib/test/common/expected_test.cc @@ -0,0 +1,149 @@ +/* + * Copyright 2013-2020 Software Radio Systems Limited + * + * This file is part of srsLTE. + * + * srsLTE is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsLTE is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srslte/common/expected.h" +#include "srslte/common/test_common.h" + +int test_expected_trivial() +{ + srslte::expected exp; + TESTASSERT(exp.has_value()); + TESTASSERT(exp); + + exp = 5; + TESTASSERT(exp.has_value()); + TESTASSERT(*exp.value() == 5); + TESTASSERT(exp); + + exp.set_error(srslte::default_error_t{}); + TESTASSERT(not exp.has_value()); + TESTASSERT(not exp); + + int i = 2; + exp = i; + TESTASSERT(exp); + TESTASSERT(*exp.value() == 2); + + exp.set_error(); + TESTASSERT(not exp); + + exp = 3; + { + srslte::expected exp2 = exp; + TESTASSERT(exp2 and *exp2.value() == 3); + srslte::expected exp3; + exp3 = exp2; + TESTASSERT(exp3 and *exp3.value() == 3); + } + TESTASSERT(exp and *exp.value() == 3); + + exp.set_error(); + { + srslte::expected exp2{exp}; + TESTASSERT(not exp2); + srslte::expected exp3; + exp3 = exp; + TESTASSERT(not exp3); + } + + return SRSLTE_SUCCESS; +} + +struct C { + C() : val(0) { count++; } + C(int v) : val(v) { count++; } + C(const C& other) + { + val = other.val; + count++; + } + C(C&& other) + { + val = other.val; + other.val = 0; + count++; + } + ~C() { count--; } + C& operator=(const C& other) + { + val = other.val; + return *this; + } + C& operator=(C&& other) + { + val = other.val; + other.val = 0; + return *this; + } + int val; + static uint32_t count; +}; +uint32_t C::count = 0; + +int test_expected_struct() +{ + srslte::expected exp; + exp = C{5}; + TESTASSERT(exp and exp.value()->val == 5); + TESTASSERT(C::count == 1); + + { + auto exp2 = exp; + TESTASSERT(exp2 and exp2.value()->val == 5); + TESTASSERT(C::count == 2); + } + TESTASSERT(exp and exp.value()->val == 5); + TESTASSERT(C::count == 1); + + { + auto exp2 = std::move(exp); + TESTASSERT(exp2 and exp2.value()->val == 5); + TESTASSERT(exp and exp.value()->val == 0); + } + + exp.set_error(2); + TESTASSERT(not exp and *exp.error() == 2); + + return SRSLTE_SUCCESS; +} + +int test_unique_ptr() +{ + srslte::expected > exp; + TESTASSERT(exp); + exp.value()->reset(new C{2}); + TESTASSERT(exp.value()->get()->val == 2); + + { + auto exp2 = std::move(exp); + TESTASSERT(exp.value()->get() == nullptr); + TESTASSERT(exp2 and exp2.value()->get()->val == 2); + } + + return SRSLTE_SUCCESS; +} + +int main() +{ + TESTASSERT(test_expected_trivial() == SRSLTE_SUCCESS); + TESTASSERT(test_expected_struct() == SRSLTE_SUCCESS); + TESTASSERT(test_unique_ptr() == SRSLTE_SUCCESS); +}