diff --git a/include/etl/optional.h b/include/etl/optional.h index 4cff15ab..48289116 100644 --- a/include/etl/optional.h +++ b/include/etl/optional.h @@ -42,6 +42,12 @@ SOFTWARE. namespace etl { + //***************************************************************************** + // Forward declaration of etl::optional + //***************************************************************************** + template + class optional; + //***************************************************************************** /// A null option type. ///\ingroup utilities @@ -102,7 +108,7 @@ namespace etl //***************************************************************************** namespace private_optional { - template ::value> + template ::value && !etl::is_const::value> class optional_impl; //***************************************************************************** @@ -114,6 +120,7 @@ namespace etl protected: typedef T value_type; + typedef optional_impl this_type; //*************************************************************************** /// Constructor. @@ -299,7 +306,7 @@ namespace etl ETL_CONSTEXPR20_STL T* operator ->() { -#if ETL_IS_DEBUG_BUILD && !(ETL_USING_CPP20 && ETL_USING_STL) +#if ETL_IS_DEBUG_BUILD ETL_ASSERT(has_value(), ETL_ERROR(optional_invalid)); #endif @@ -312,7 +319,7 @@ namespace etl ETL_CONSTEXPR20_STL const T* operator ->() const { -#if ETL_IS_DEBUG_BUILD && !(ETL_USING_CPP20 && ETL_USING_STL) +#if ETL_IS_DEBUG_BUILD ETL_ASSERT(has_value(), ETL_ERROR(optional_invalid)); #endif @@ -325,7 +332,7 @@ namespace etl ETL_CONSTEXPR20_STL T& operator *() ETL_LVALUE_REF_QUALIFIER { -#if ETL_IS_DEBUG_BUILD && !(ETL_USING_CPP20 && ETL_USING_STL) +#if ETL_IS_DEBUG_BUILD ETL_ASSERT(has_value(), ETL_ERROR(optional_invalid)); #endif @@ -338,7 +345,7 @@ namespace etl ETL_CONSTEXPR20_STL const T& operator *() const ETL_LVALUE_REF_QUALIFIER { -#if ETL_IS_DEBUG_BUILD && !(ETL_USING_CPP20 && ETL_USING_STL) +#if ETL_IS_DEBUG_BUILD ETL_ASSERT(has_value(), ETL_ERROR(optional_invalid)); #endif @@ -352,7 +359,7 @@ namespace etl ETL_CONSTEXPR20_STL T&& operator *()&& { -#if ETL_IS_DEBUG_BUILD && !(ETL_USING_CPP20 && ETL_USING_STL) +#if ETL_IS_DEBUG_BUILD ETL_ASSERT(has_value(), ETL_ERROR(optional_invalid)); #endif @@ -365,7 +372,7 @@ namespace etl ETL_CONSTEXPR20_STL const T&& operator *() const&& { -#if ETL_IS_DEBUG_BUILD && !(ETL_USING_CPP20 && ETL_USING_STL) +#if ETL_IS_DEBUG_BUILD ETL_ASSERT(has_value(), ETL_ERROR(optional_invalid)); #endif @@ -496,16 +503,36 @@ namespace etl storage.destroy(); } + //************************************************************************* + /// + //************************************************************************* + ETL_CONSTEXPR20_STL + T& emplace(const optional_impl& other) + { +#if ETL_IS_DEBUG_BUILD + ETL_ASSERT(other.has_value(), ETL_ERROR(optional_invalid)); +#endif + + storage.construct(other.value()); + + return storage.u.value; + } + #if ETL_USING_CPP11 && ETL_NOT_USING_STLPORT && !defined(ETL_OPTIONAL_FORCE_CPP03_IMPLEMENTATION) //************************************************************************* - /// Emplaces a value. - ///\param args The arguments to construct with. + /// Emplaces a value from arbitrary constructor arguments. + /// Disabled (via SFINAE) if the first argument is an optional_impl (or a + /// derived type such as etl::optional) so that the dedicated + /// emplace(const optional_impl&) overload is selected instead. //************************************************************************* - template + template ::type>::type>::value, int>::type = 0> ETL_CONSTEXPR20_STL - T& emplace(TArgs&& ... args) + T& emplace(U&& first, URest&&... rest) { - storage.construct(etl::forward(args)...); + storage.construct(etl::forward(first), etl::forward(rest)...); return storage.u.value; } @@ -533,7 +560,9 @@ namespace etl /// 1 parameter. //************************************************************************* template - T& emplace(const T1& value1) + typename etl::enable_if::type>::type>::value && + !etl::is_same, typename etl::remove_cv::type>::type>::value, T&>::type + emplace(const T1& value1) { if (has_value()) { @@ -614,6 +643,8 @@ namespace etl //************************************* struct storage_type { + typedef typename etl::remove_const::type* pointer_type; + //******************************* ETL_CONSTEXPR20_STL storage_type() @@ -626,12 +657,9 @@ namespace etl ETL_CONSTEXPR20_STL void construct(const T& value_) { - if (valid) - { - etl::destroy_at(&u.value); - } + destroy(); - etl::construct_at(&u.value, value_); + etl::construct_at(const_cast(&u.value), value_); valid = true; } @@ -640,12 +668,9 @@ namespace etl ETL_CONSTEXPR20_STL void construct(T&& value_) { - if (valid) - { - etl::destroy_at(&u.value); - } + destroy(); - etl::construct_at(&u.value, etl::move(value_)); + etl::construct_at(const_cast(&u.value), etl::move(value_)); valid = true; } @@ -654,12 +679,9 @@ namespace etl ETL_CONSTEXPR20_STL void construct(TArgs&&... args) { - if (valid) - { - etl::destroy_at(&u.value); - } + destroy(); - etl::construct_at(&u.value, etl::forward(args)...); + etl::construct_at(const_cast(&u.value), etl::forward(args)...); valid = true; } #endif @@ -670,7 +692,7 @@ namespace etl { if (valid) { - etl::destroy_at(&u.value); + etl::destroy_at(const_cast(&u.value)); valid = false; } } @@ -708,6 +730,7 @@ namespace etl protected: typedef T value_type; + typedef optional_impl this_type; //*************************************************************************** /// Constructor. @@ -884,7 +907,7 @@ namespace etl ETL_CONSTEXPR14 T* operator ->() { -#if ETL_IS_DEBUG_BUILD && !(ETL_USING_CPP20 && ETL_USING_STL) +#if ETL_IS_DEBUG_BUILD ETL_ASSERT(has_value(), ETL_ERROR(optional_invalid)); #endif @@ -897,7 +920,7 @@ namespace etl ETL_CONSTEXPR14 const T* operator ->() const { -#if ETL_IS_DEBUG_BUILD && !(ETL_USING_CPP20 && ETL_USING_STL) +#if ETL_IS_DEBUG_BUILD ETL_ASSERT(has_value(), ETL_ERROR(optional_invalid)); #endif @@ -910,7 +933,7 @@ namespace etl ETL_CONSTEXPR14 T& operator *() ETL_LVALUE_REF_QUALIFIER { -#if ETL_IS_DEBUG_BUILD && !(ETL_USING_CPP20 && ETL_USING_STL) +#if ETL_IS_DEBUG_BUILD ETL_ASSERT(has_value(), ETL_ERROR(optional_invalid)); #endif @@ -923,7 +946,7 @@ namespace etl ETL_CONSTEXPR14 const T& operator *() const ETL_LVALUE_REF_QUALIFIER { -#if ETL_IS_DEBUG_BUILD && !(ETL_USING_CPP20 && ETL_USING_STL) +#if ETL_IS_DEBUG_BUILD ETL_ASSERT(has_value(), ETL_ERROR(optional_invalid)); #endif @@ -937,7 +960,7 @@ namespace etl ETL_CONSTEXPR14 T&& operator *()&& { -#if ETL_IS_DEBUG_BUILD && !(ETL_USING_CPP20 && ETL_USING_STL) +#if ETL_IS_DEBUG_BUILD ETL_ASSERT(has_value(), ETL_ERROR(optional_invalid)); #endif @@ -950,7 +973,7 @@ namespace etl ETL_CONSTEXPR14 const T&& operator *() const&& { -#if ETL_IS_DEBUG_BUILD && !(ETL_USING_CPP20 && ETL_USING_STL) +#if ETL_IS_DEBUG_BUILD ETL_ASSERT(has_value(), ETL_ERROR(optional_invalid)); #endif @@ -1081,6 +1104,21 @@ namespace etl storage.destroy(); } + //************************************************************************* + /// + //************************************************************************* + ETL_CONSTEXPR20_STL + T& emplace(const optional_impl& other) + { +#if ETL_IS_DEBUG_BUILD + ETL_ASSERT(other.has_value(), ETL_ERROR(optional_invalid)); +#endif + + storage.construct(other.value()); + + return storage.u.value; + } + #if ETL_USING_CPP11 && ETL_NOT_USING_STLPORT && !defined(ETL_OPTIONAL_FORCE_CPP03_IMPLEMENTATION) //************************************************************************* /// Emplaces a value. @@ -1118,7 +1156,9 @@ namespace etl /// 1 parameter. //************************************************************************* template - T& emplace(const T1& value1) + typename etl::enable_if::type>::type>::value && + !etl::is_same, typename etl::remove_cv::type>::type>::value, T&>::type + emplace(const T1& value1) { if (has_value()) { @@ -1247,11 +1287,11 @@ namespace etl }; } -#define ETL_OPTIONAL_ENABLE_CPP14 typename etl::enable_if< etl::is_pod::value, int>::type = 0 -#define ETL_OPTIONAL_ENABLE_CPP20_STL typename etl::enable_if::value, int>::type = 0 +#define ETL_OPTIONAL_ENABLE_CPP14 typename etl::enable_if< etl::is_pod::type>::value, int>::type = 0 +#define ETL_OPTIONAL_ENABLE_CPP20_STL typename etl::enable_if::type>::value, int>::type = 0 -#define ETL_OPTIONAL_ENABLE_CONSTEXPR_BOOL_RETURN_CPP14 ETL_CONSTEXPR14 typename etl::enable_if< etl::is_pod::value, bool>::type -#define ETL_OPTIONAL_ENABLE_CONSTEXPR_BOOL_RETURN_CPP20_STL ETL_CONSTEXPR20_STL typename etl::enable_if::value, bool>::type +#define ETL_OPTIONAL_ENABLE_CONSTEXPR_BOOL_RETURN_CPP14 ETL_CONSTEXPR14 typename etl::enable_if< etl::is_pod::type>::value, bool>::type +#define ETL_OPTIONAL_ENABLE_CONSTEXPR_BOOL_RETURN_CPP20_STL ETL_CONSTEXPR20_STL typename etl::enable_if::type>::value, bool>::type //***************************************************************************** /// An optional type. diff --git a/test/test_optional.cpp b/test/test_optional.cpp index 8b6c47ee..e9623b87 100644 --- a/test/test_optional.cpp +++ b/test/test_optional.cpp @@ -976,5 +976,36 @@ namespace } }; } + + //************************************************************************* + using ItemType = etl::array; + + etl::optional create_optional_issue_1171() + { + ItemType t; + t[0] = 1; + t[1] = 20; + + return etl::optional(t); + } + + TEST(test_optional_issue_1171) + { + etl::optional opt1 = create_optional_issue_1171(); + CHECK_TRUE(opt1.has_value()); + CHECK_EQUAL(1, (*opt1)[0]); + CHECK_EQUAL(20, (*opt1)[1]); + + etl::optional opt2(create_optional_issue_1171()); + CHECK_TRUE(opt2.has_value()); + CHECK_EQUAL(1, (*opt2)[0]); + CHECK_EQUAL(20, (*opt2)[1]); + + etl::optional opt3; + opt3.emplace(create_optional_issue_1171()); + CHECK_TRUE(opt3.has_value()); + CHECK_EQUAL(1, (*opt3)[0]); + CHECK_EQUAL(20, (*opt3)[1]); + } }; }