From 81807b2fd2689097145c7c5ed5b495c1903bfee8 Mon Sep 17 00:00:00 2001 From: John Wellbelove Date: Sat, 4 Sep 2021 13:34:02 +0100 Subject: [PATCH] Added result type --- .../etl/generators/type_traits_generator.h | 29 +- include/etl/private/variant_variadic.h | 44 +-- include/etl/result.h | 326 +++++++++++++++++ include/etl/type_traits.h | 29 +- test/test_result.cpp | 341 ++++++++++++++++++ test/vs2019/etl.vcxproj | 2 + test/vs2019/etl.vcxproj.filters | 8 +- 7 files changed, 753 insertions(+), 26 deletions(-) create mode 100644 include/etl/result.h create mode 100644 test/test_result.cpp diff --git a/include/etl/generators/type_traits_generator.h b/include/etl/generators/type_traits_generator.h index 9f734c44..e3bcafae 100644 --- a/include/etl/generators/type_traits_generator.h +++ b/include/etl/generators/type_traits_generator.h @@ -256,6 +256,18 @@ namespace etl using add_cv_t = typename add_cv::type; #endif + //*************************************************************************** + /// remove_cvref + template struct remove_cvref + { + typedef typename remove_cv::type>::type type; + }; + +#if ETL_CPP11_SUPPORTED + template + using remove_cvref_t = typename remove_cvref::type; +#endif + //*************************************************************************** /// is_integral template struct is_integral : false_type {}; @@ -590,8 +602,8 @@ namespace etl { typedef typename etl::remove_reference::type U; typedef typename etl::conditional::value, - typename etl::remove_extent::type*, - typename etl::remove_cv::type>::type type; + typename etl::remove_extent::type*, + typename etl::remove_cv::type>::type type; }; #if ETL_CPP11_SUPPORTED @@ -896,6 +908,19 @@ namespace etl using add_cv_t = typename std::add_cv::type; #endif + //*************************************************************************** + /// remove_cvref + ///\ingroup type_traits + template struct remove_cvref + { + typedef typename std::remove_cv::type>::type type; + }; + +#if ETL_CPP11_SUPPORTED + template + using remove_cvref_t = typename etl::remove_cvref::type; +#endif + //*************************************************************************** /// is_integral ///\ingroup type_traits diff --git a/include/etl/private/variant_variadic.h b/include/etl/private/variant_variadic.h index e0d39f53..b134c692 100644 --- a/include/etl/private/variant_variadic.h +++ b/include/etl/private/variant_variadic.h @@ -44,6 +44,8 @@ SOFTWARE. #include "../visitor.h" #include "../memory.h" +#include + #if defined(ETL_COMPILER_KEIL) #pragma diag_suppress 940 #pragma diag_suppress 111 @@ -87,7 +89,7 @@ namespace etl { private: - using type = etl::remove_reference_t; + using type = etl::remove_cvref_t; //*********************************** template @@ -528,15 +530,15 @@ namespace etl //*************************************************************************** /// Constructor from a value. //*************************************************************************** - template , variant>::value, int> = 0> + template , variant>::value, int> = 0> ETL_CONSTEXPR14 variant(T&& value) : data() - , operation(operation_type, etl::is_copy_constructible>::value, etl::is_move_constructible>::value>::do_operation) - , type_id(etl::private_variant::parameter_pack::template index_of_type>::value) + , operation(operation_type, etl::is_copy_constructible>::value, etl::is_move_constructible>::value>::do_operation) + , type_id(etl::private_variant::parameter_pack::template index_of_type>::value) { - static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); + static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); - construct_in_place>(data, etl::forward(value)); + construct_in_place>(data, etl::forward(value)); } //*************************************************************************** @@ -545,12 +547,12 @@ namespace etl template ETL_CONSTEXPR14 explicit variant(etl::in_place_type_t, TArgs&&... args) : data() - , operation(operation_type, etl::is_copy_constructible>::value, etl::is_move_constructible>::value>::do_operation) - , type_id(etl::private_variant::parameter_pack::template index_of_type>::value) + , operation(operation_type, etl::is_copy_constructible>::value, etl::is_move_constructible>::value>::do_operation) + , type_id(etl::private_variant::parameter_pack::template index_of_type>::value) { - static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); + static_assert(etl::is_one_of, TTypes...>::value, "Unsupported type"); - construct_in_place_args>(data, etl::forward(args)...); + construct_in_place_args>(data, etl::forward(args)...); } //*************************************************************************** @@ -576,12 +578,12 @@ namespace etl template ETL_CONSTEXPR14 explicit variant(etl::in_place_type_t, std::initializer_list init, TArgs&&... args) : data() - , operation(operation_type, etl::is_copy_constructible>::value, etl::is_move_constructible>::value>::do_operation) - , type_id(private_variant::parameter_pack:: template index_of_type>::value) + , operation(operation_type, etl::is_copy_constructible>::value, etl::is_move_constructible>::value>::do_operation) + , type_id(private_variant::parameter_pack:: template index_of_type>::value) { - static_assert(etl::is_one_of, TTypes...> ::value, "Unsupported type"); + static_assert(etl::is_one_of, TTypes...> ::value, "Unsupported type"); - construct_in_place_args>(data, init, etl::forward(args)...); + construct_in_place_args>(data, init, etl::forward(args)...); } //*************************************************************************** @@ -671,7 +673,7 @@ namespace etl { static_assert(etl::is_one_of::value, "Unsupported type"); - using type = etl::remove_reference_t; + using type = etl::remove_cvref_t; operation(private_variant::Destroy, data, nullptr); @@ -688,10 +690,10 @@ namespace etl /// Move assignment operator for type. ///\param value The value to assign. //*************************************************************************** - template , variant>::value, int> = 0> + template , variant>::value, int> = 0> variant& operator =(T&& value) { - using type = etl::remove_reference_t; + using type = etl::remove_cvref_t; static_assert(etl::is_one_of::value, "Unsupported type"); @@ -820,7 +822,7 @@ namespace etl template static void construct_in_place(char* pstorage, const T& value) { - using type = etl::remove_reference_t; + using type = etl::remove_cvref_t; ::new (pstorage) type(value); } @@ -831,7 +833,7 @@ namespace etl template static void construct_in_place(char* pstorage, T&& value) { - using type = etl::remove_reference_t; + using type = etl::remove_cvref_t; ::new (pstorage) type(etl::move(value)); } @@ -842,7 +844,7 @@ namespace etl template static void construct_in_place_args(char* pstorage, TArgs&&... args) { - using type = etl::remove_reference_t; + using type = etl::remove_cvref_t; ::new (pstorage) type(etl::forward(args)...); } @@ -853,7 +855,7 @@ namespace etl template static void default_construct_in_place(char* pstorage) { - using type = etl::remove_reference_t; + using type = etl::remove_cvref_t; ::new (pstorage) type(); } diff --git a/include/etl/result.h b/include/etl/result.h new file mode 100644 index 00000000..f25a4985 --- /dev/null +++ b/include/etl/result.h @@ -0,0 +1,326 @@ +///\file + +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2021 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#ifndef ETL_RESULT_INCLUDED +#define ETL_RESULT_INCLUDED + +///\defgroup result result +///\ingroup utilities + +#include "platform.h" +#include "optional.h" +#include "variant.h" + +#if ETL_CPP11_NOT_SUPPORTED + #if !defined(ETL_IN_UNIT_TEST) + #error NOT SUPPORTED FOR C++03 OR BELOW + #endif +#else + +namespace etl +{ + //***************************************************************************** + /// Result type. + //***************************************************************************** + template + class result + { + public: + + //******************************************* + /// Cannot be default constructed + //******************************************* + result() = delete; + + //******************************************* + /// Copy constructor + //******************************************* + result(const result& other) + : data(other.data) + { + } + + //******************************************* + /// Move constructor + //******************************************* + result(result&& other) + : data(etl::move(other.data)) + { + } + + //******************************************* + // Construct from a value + //******************************************* + result(const TValue& value) + : data(value) + { + } + + //******************************************* + // Move construct from a value + //******************************************* + result(TValue&& value) + : data(etl::move(value)) + { + } + + //******************************************* + /// Construct from error + //******************************************* + result(const TError& err) + : data(err) + { + } + + //******************************************* + /// Move construct from error + //******************************************* + result(TError&& err) + : data(etl::move(err)) + { + } + + //******************************************* + /// Copy assign + //******************************************* + result& operator =(const result& other) + { + data = other.data; + return *this; + } + + //******************************************* + /// Move assign + //******************************************* + result& operator =(result&& other) + { + data = etl::move(other.data); + return *this; + } + + //******************************************* + /// Copy assign from value + //******************************************* + result& operator =(const TValue& value) + { + data = value; + return *this; + } + + //******************************************* + /// Move assign from value + //******************************************* + result& operator =(TValue&& value) + { + data = etl::move(value); + return *this; + } + + //******************************************* + /// Copy assign from error + //******************************************* + result& operator =(const TError& err) + { + data = err; + return *this; + } + + //******************************************* + /// Move assign from error + //******************************************* + result& operator =(TError&& err) + { + data = etl::move(err); + return *this; + } + + //******************************************* + /// true if result contains a value + //******************************************* + bool is_value() const + { + return (data.index() == 0U); + } + + //******************************************* + /// true if result contains an error + //******************************************* + bool is_error() const + { + return (data.index() == 1U); + } + + //******************************************* + /// Returns a const reference to the value. + /// Undefined if the result does not contain an value. + //******************************************* + const TValue& value() const + { + return etl::get(data); + } + + //******************************************* + /// Returns an rvalue reference to the value. + /// Undefined if the result does not contain an value. + //******************************************* + TValue&& value() + { + return etl::get(etl::move(data)); + } + + //******************************************* + /// Returns a const reference to the error. + /// Undefined if the result does not contain an error. + //******************************************* + const TError& error() const + { + return etl::get(data); + } + + //******************************************* + /// Returns an rvalue reference to the error. + /// Undefined if the result does not contain an error. + //******************************************* + TError&& error() + { + return etl::get(etl::move(data)); + } + + private: + + etl::variant data; + }; + + //***************************************************************************** + /// Result type. + /// Specialisation for void value type. + //***************************************************************************** + template + class result + { + public: + + //******************************************* + /// Cannot be default constructed + //******************************************* + result() = delete; + + //******************************************* + /// Copy constructor + //******************************************* + result(const result& other) + : data(other.data) + { + } + + //******************************************* + /// Move constructor + //******************************************* + result(result&& other) + : data(etl::move(other.data)) + { + } + + //******************************************* + /// Construct from error + //******************************************* + result(const TError& err) + : data(err) + { + } + + //******************************************* + /// Move construct from error + //******************************************* + result(TError&& err) + : data(etl::move(err)) + { + } + + //******************************************* + /// Copy assign from error + //******************************************* + result& operator =(const TError& err) + { + data = err; + return *this; + } + + //******************************************* + /// Move assign from error + //******************************************* + result& operator =(TError&& err) + { + data = etl::move(err); + return *this; + } + + //******************************************* + /// true if result contains a value + //******************************************* + bool is_value() const + { + return false; + } + + //******************************************* + /// true if result contains an error + //******************************************* + bool is_error() const + { + return true; + } + + //******************************************* + /// Returns a const reference to the error. + /// Undefined if the result does not contain an error. + //******************************************* + const TError& error() const + { + return etl::get(data); + } + + //******************************************* + /// Returns an rvalue reference to the error. + /// Undefined if the result does not contain an error. + //******************************************* + TError&& error() + { + return etl::get(etl::move(data)); + } + + private: + + etl::variant data; + }; +} + +#endif +#endif diff --git a/include/etl/type_traits.h b/include/etl/type_traits.h index a96a9baf..2940986f 100644 --- a/include/etl/type_traits.h +++ b/include/etl/type_traits.h @@ -244,6 +244,18 @@ namespace etl using add_cv_t = typename add_cv::type; #endif + //*************************************************************************** + /// remove_cvref + template struct remove_cvref + { + typedef typename remove_cv::type>::type type; + }; + +#if ETL_CPP11_SUPPORTED + template + using remove_cvref_t = typename remove_cvref::type; +#endif + //*************************************************************************** /// is_integral template struct is_integral : false_type {}; @@ -578,8 +590,8 @@ namespace etl { typedef typename etl::remove_reference::type U; typedef typename etl::conditional::value, - typename etl::remove_extent::type*, - typename etl::remove_cv::type>::type type; + typename etl::remove_extent::type*, + typename etl::remove_cv::type>::type type; }; #if ETL_CPP11_SUPPORTED @@ -884,6 +896,19 @@ namespace etl using add_cv_t = typename std::add_cv::type; #endif + //*************************************************************************** + /// remove_cvref + ///\ingroup type_traits + template struct remove_cvref + { + typedef typename std::remove_cv::type>::type type; + }; + +#if ETL_CPP11_SUPPORTED + template + using remove_cvref_t = typename etl::remove_cvref::type; +#endif + //*************************************************************************** /// is_integral ///\ingroup type_traits diff --git a/test/test_result.cpp b/test/test_result.cpp new file mode 100644 index 00000000..8d6597c5 --- /dev/null +++ b/test/test_result.cpp @@ -0,0 +1,341 @@ +/****************************************************************************** +The MIT License(MIT) + +Embedded Template Library. +https://github.com/ETLCPP/etl +https://www.etlcpp.com + +Copyright(c) 2017 jwellbelove + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files(the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and / or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions : + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +******************************************************************************/ + +#include "unit_test_framework.h" + +#include "etl/result.h" + +#include + +namespace +{ + SUITE(test_result) + { + struct Value + { + std::string v; + }; + + struct ValueM + { + ValueM() + { + } + + ValueM(const std::string& v_) : v(v_) + { + } + + ValueM(ValueM&& other) + : v(etl::move(other.v)) + { + } + + ValueM& operator =(ValueM&& rhs) + { + v = etl::move(rhs.v); + return *this; + } + + //ValueM(const ValueM&) = delete; + //ValueM& operator =(const ValueM&) = delete; + + ValueM(const ValueM& other) + : v(other.v) + { + } + + ValueM& operator =(const ValueM& rhs) + { + v = rhs.v; + return *this; + } + + std::string v; + }; + + struct Error + { + std::string e; + }; + + struct ErrorM + { + ErrorM() + { + } + + ErrorM(const std::string& e_) + : e(e_) + { + } + + ErrorM(ErrorM&& other) + : e(etl::move(other.e)) + { + } + + ErrorM& operator =(ErrorM&& rhs) + { + e = etl::move(rhs.e); + return *this; + } + + ErrorM(const ErrorM& other) + : e(other.e) + { + } + + ErrorM& operator =(const ErrorM& rhs) + { + e = rhs.e; + return *this; + } + + std::string e; + }; + + using Result = etl::result; + using ResultV = etl::result; + using ResultM = etl::result; + + //************************************************************************* + TEST(test_constructor_for_result_with_value) + { + Value input = { "value 1" }; + Result result(input); + + Value output = result.value(); + + CHECK(result.is_value()); + CHECK(!result.is_error()); + CHECK(output.v == input.v); + } + + //************************************************************************* + TEST(test_constructor_for_const_result_with_value) + { + Value input = { "value 1" }; + const Result result(input); + + const Value& output = result.value(); + + CHECK(result.is_value()); + CHECK(!result.is_error()); + CHECK(output.v == input.v); + } + + //************************************************************************* + TEST(test_constructor_for_moveable_result_with_value) + { + ValueM input = { "value 1" }; + ResultM result(etl::move(input)); + + ValueM output = etl::move(result.value()); + + CHECK(result.is_value()); + CHECK(!result.is_error()); + CHECK(output.v == std::string("value 1")); + } + + //************************************************************************* + TEST(test_constructor_for_moveable_const_result_with_value) + { + ValueM input = { "value 1" }; + const ResultM result(etl::move(input)); + + ValueM output = etl::move(result.value()); + + CHECK(result.is_value()); + CHECK(!result.is_error()); + CHECK(output.v == std::string("value 1")); + } + + //************************************************************************* + TEST(test_constructor_for_result_with_error) + { + Error input = { "error 1" }; + Result result(input); + + Error output = result.error(); + + CHECK(!result.is_value()); + CHECK(result.is_error()); + CHECK(output.e == input.e); + } + + //************************************************************************* + TEST(test_constructor_for_const_result_with_error) + { + Error input = { "error 1" }; + const Result result(input); + + const Error& output = result.error(); + + CHECK(!result.is_value()); + CHECK(result.is_error()); + CHECK(output.e == input.e); + } + + //************************************************************************* + TEST(test_constructor_for_moveable_result_with_error) + { + ErrorM input = { "error 1" }; + ResultM result(etl::move(input)); + + ErrorM output = etl::move(result.error()); + + CHECK(!result.is_value()); + CHECK(result.is_error()); + CHECK(output.e == std::string("error 1")); + } + + //************************************************************************* + TEST(test_constructor_for_moveable_const_result_with_error) + { + ErrorM input = { "error 1" }; + const ResultM result(etl::move(input)); + + ErrorM output = etl::move(result.error()); + + CHECK(!result.is_value()); + CHECK(result.is_error()); + CHECK(output.e == std::string("error 1")); + } + + //************************************************************************* + TEST(test_constructor_for_result_void_value_with_error) + { + Error input = { "error 1" }; + ResultV result(input); + + Error output = result.error(); + + CHECK(!result.is_value()); + CHECK(result.is_error()); + CHECK(output.e == input.e); + } + + //************************************************************************* + TEST(test_constructor_for_const_result_void_value_with_error) + { + Error input = { "error 1" }; + const ResultV result(input); + + const Error& output = result.error(); + + CHECK(!result.is_value()); + CHECK(result.is_error()); + CHECK(output.e == input.e); + } + + //************************************************************************* + TEST(test_constructor_for_moveable_result_void_value_with_error) + { + Error input = { "error 1" }; + ResultV result(etl::move(input)); + + Error output = etl::move(result.error()); + + CHECK(!result.is_value()); + CHECK(result.is_error()); + CHECK(output.e == std::string("error 1")); + } + + //************************************************************************* + TEST(test_constructor_for_moveable_const_result_void_value_with_error) + { + Error input = { "error 1" }; + const ResultV result(etl::move(input)); + + Error output = etl::move(result.error()); + + CHECK(!result.is_value()); + CHECK(result.is_error()); + CHECK(output.e == std::string("error 1")); + } + + //************************************************************************* + TEST(test_copy_construct_result) + { + Value input = { "value 1" }; + Result result(input); + + Result result2(result); + + Value output = result2.value(); + + CHECK(result.is_value()); + CHECK(!result.is_error()); + CHECK(result2.is_value()); + CHECK(!result2.is_error()); + CHECK(output.v == input.v); + } + + //************************************************************************* + TEST(test_move_construct_result) + { + Value input = { "value 1" }; + Result result(input); + + Result result2(etl::move(result)); + + Value output = result.value(); + Value output2 = result2.value(); + + CHECK(result.is_value()); + CHECK(!result.is_error()); + CHECK(result2.is_value()); + CHECK(!result2.is_error()); + CHECK(output.v != input.v); + CHECK(output2.v == input.v); + } + + //************************************************************************* + TEST(test_move_assign_result) + { + Value input = { "value 1" }; + Result result(input); + + Value input2 = { "value 2" }; + Result result2(result); + + result2 = etl::move(result); + + Value output = result.value(); + Value output2 = result2.value(); + + CHECK(result.is_value()); + CHECK(!result.is_error()); + CHECK(result2.is_value()); + CHECK(!result2.is_error()); + CHECK(output.v != input.v); + CHECK(output2.v == input.v); + } + }; +} diff --git a/test/vs2019/etl.vcxproj b/test/vs2019/etl.vcxproj index 272ecd92..9b1cf6f1 100644 --- a/test/vs2019/etl.vcxproj +++ b/test/vs2019/etl.vcxproj @@ -1852,6 +1852,7 @@ + @@ -8277,6 +8278,7 @@ + diff --git a/test/vs2019/etl.vcxproj.filters b/test/vs2019/etl.vcxproj.filters index e8cb325a..f36d226d 100644 --- a/test/vs2019/etl.vcxproj.filters +++ b/test/vs2019/etl.vcxproj.filters @@ -1143,6 +1143,9 @@ ETL\Utilities + + ETL\Utilities + @@ -3005,6 +3008,9 @@ Source Files\Sanity Checks + + Source Files + @@ -3149,4 +3155,4 @@ Resource Files - + \ No newline at end of file