diff --git a/include/etl/utility.h b/include/etl/utility.h index 0016aacc..9b1e648d 100644 --- a/include/etl/utility.h +++ b/include/etl/utility.h @@ -68,6 +68,55 @@ namespace etl ETL_STATIC_ASSERT(!etl::is_lvalue_reference::value, "Invalid rvalue to lvalue conversion"); return static_cast(t); } + + //****************************************************************************** + /// See std::forward_like https://en.cppreference.com/w/cpp/utility/forward_like + /// Returns a reference to x which has similar properties to T&&. + ///\return + /// If etl::remove_reference_t is const then returns a const reference if U is an lvalue, otherwise a const rvalue reference. + /// If etl::remove_reference_t is not const then returns a reference if U is an lvalue, otherwise an rvalue reference. + //****************************************************************************** + template + ETL_NODISCARD + ETL_CONSTEXPR14 + auto&& forward_like(U&& u) ETL_NOEXCEPT + { + using TType = etl::remove_reference_t; + using UType = etl::remove_reference_t; + + if ETL_IF_CONSTEXPR (etl::is_const::value) + { + // For const TType + if ETL_IF_CONSTEXPR (etl::is_lvalue_reference::value) + { + // const lvalue + return static_cast(u); + } + else + { + // const rvalue + return static_cast(u); + } + } + else + { + // For non-const TType + if ETL_IF_CONSTEXPR (etl::is_lvalue_reference::value) + { + // lvalue + return static_cast(u); + } + else + { + // rvalue + return static_cast(u); + } + } + } + + // Defines the type that forward_like would cast to. + template + using forward_like_t = decltype(etl::forward_like(etl::declval())); #endif // We can't have std::swap and etl::swap templates coexisting in the unit tests @@ -612,4 +661,3 @@ namespace etl } #endif - diff --git a/test/test_utility.cpp b/test/test_utility.cpp index 5b42d9d6..cb386b2f 100644 --- a/test/test_utility.cpp +++ b/test/test_utility.cpp @@ -432,5 +432,238 @@ namespace CHECK_EQUAL(2, pf(1)); } + + //************************************************************************* + struct SF + { + + }; + + //********************************* + enum class forward_call_type + { + LValue, + ConstLValue, + RValue, + ConstRValue + }; + + //********************************* + std::ostream& operator << (std::ostream& os, forward_call_type type) + { + switch (type) + { + case forward_call_type::LValue: + { + os << "LValue"; + break; + } + + case forward_call_type::ConstLValue: + { + os << "ConstLValue"; + break; + } + + case forward_call_type::RValue: + { + os << "RValue"; + break; + } + + case forward_call_type::ConstRValue: + { + os << "ConstRValue"; + break; + } + + default: + { + os << "Unknown type"; + break; + } + } + + return os; + } + + //********************************* + forward_call_type function_f(SF&) + { + return forward_call_type::LValue; + } + + //********************************* + forward_call_type function_f(const SF&) + { + return forward_call_type::ConstLValue; + } + + //********************************* + forward_call_type function_f(SF&&) + { + return forward_call_type::RValue; + } + + //********************************* + forward_call_type function_f(const SF&&) + { + return forward_call_type::ConstRValue; + } + + //********************************* + template + forward_call_type template_function_f(T&& t) + { + return function_f(etl::forward(t)); + } + + //********************************* + TEST(test_forward) + { + SF s1; + const SF s2; + + CHECK_EQUAL(forward_call_type::LValue, template_function_f(s1)); + CHECK_EQUAL(forward_call_type::RValue, template_function_f(etl::move(s1))); + CHECK_EQUAL(forward_call_type::ConstLValue, template_function_f(s2)); + CHECK_EQUAL(forward_call_type::ConstRValue, template_function_f(etl::move(s2))); + } + + //************************************************************************* + struct TFL + { + }; + + struct UFL + { + }; + + enum class forward_like_call_type + { + LValue, + ConstLValue, + RValue, + ConstRValue + }; + + //********************************* + std::ostream& operator << (std::ostream& os, forward_like_call_type type) + { + switch (type) + { + case forward_like_call_type::LValue: + { + os << "LValue"; + break; + } + + case forward_like_call_type::ConstLValue: + { + os << "ConstLValue"; + break; + } + + case forward_like_call_type::RValue: + { + os << "RValue"; + break; + } + + case forward_like_call_type::ConstRValue: + { + os << "ConstRValue"; + break; + } + + default: + { + os << "Unknown type"; + break; + } + } + + return os; + } + + //********************************* + forward_like_call_type function_fl(UFL&) + { + return forward_like_call_type::LValue; + } + + //********************************* + forward_like_call_type function_fl(const UFL&) + { + return forward_like_call_type::ConstLValue; + } + + //********************************* + forward_like_call_type function_fl(UFL&&) + { + return forward_like_call_type::RValue; + } + + //********************************* + forward_like_call_type function_fl(const UFL&&) + { + return forward_like_call_type::ConstRValue; + } + + //********************************* + template + forward_like_call_type template_function_fl(U&& u) + { + return function_fl(etl::forward_like(u)); + } + + //********************************* + TEST(test_forward_like) + { + UFL u1; + const UFL u2; + UFL& u3 = u1; + const UFL& u4 = u2; + + CHECK_EQUAL(forward_like_call_type::LValue, template_function_fl(u1)); + CHECK_EQUAL(forward_like_call_type::ConstLValue, template_function_fl(u1)); + CHECK_EQUAL(forward_like_call_type::RValue, template_function_fl(u1)); + CHECK_EQUAL(forward_like_call_type::ConstRValue, template_function_fl(u1)); + + CHECK_EQUAL(forward_like_call_type::LValue, template_function_fl(etl::move(u1))); + CHECK_EQUAL(forward_like_call_type::ConstLValue, template_function_fl(etl::move(u1))); + CHECK_EQUAL(forward_like_call_type::RValue, template_function_fl(etl::move(u1))); + CHECK_EQUAL(forward_like_call_type::ConstRValue, template_function_fl(etl::move(u1))); + + CHECK_EQUAL(forward_like_call_type::ConstLValue, template_function_fl(u2)); + CHECK_EQUAL(forward_like_call_type::ConstLValue, template_function_fl(u2)); + CHECK_EQUAL(forward_like_call_type::ConstRValue, template_function_fl(u2)); + CHECK_EQUAL(forward_like_call_type::ConstRValue, template_function_fl(u2)); + + CHECK_EQUAL(forward_like_call_type::ConstLValue, template_function_fl(etl::move(u2))); + CHECK_EQUAL(forward_like_call_type::ConstLValue, template_function_fl(etl::move(u2))); + CHECK_EQUAL(forward_like_call_type::ConstRValue, template_function_fl(etl::move(u2))); + CHECK_EQUAL(forward_like_call_type::ConstRValue, template_function_fl(etl::move(u2))); + + CHECK_EQUAL(forward_like_call_type::LValue, template_function_fl(u3)); + CHECK_EQUAL(forward_like_call_type::ConstLValue, template_function_fl(u3)); + CHECK_EQUAL(forward_like_call_type::RValue, template_function_fl(u3)); + CHECK_EQUAL(forward_like_call_type::ConstRValue, template_function_fl(u3)); + + CHECK_EQUAL(forward_like_call_type::LValue, template_function_fl(etl::move(u3))); + CHECK_EQUAL(forward_like_call_type::ConstLValue, template_function_fl(etl::move(u3))); + CHECK_EQUAL(forward_like_call_type::RValue, template_function_fl(etl::move(u3))); + CHECK_EQUAL(forward_like_call_type::ConstRValue, template_function_fl(etl::move(u3))); + + CHECK_EQUAL(forward_like_call_type::ConstLValue, template_function_fl(u4)); + CHECK_EQUAL(forward_like_call_type::ConstLValue, template_function_fl(u4)); + CHECK_EQUAL(forward_like_call_type::ConstRValue, template_function_fl(u4)); + CHECK_EQUAL(forward_like_call_type::ConstRValue, template_function_fl(u4)); + + CHECK_EQUAL(forward_like_call_type::ConstLValue, template_function_fl(etl::move(u4))); + CHECK_EQUAL(forward_like_call_type::ConstLValue, template_function_fl(etl::move(u4))); + CHECK_EQUAL(forward_like_call_type::ConstRValue, template_function_fl(etl::move(u4))); + CHECK_EQUAL(forward_like_call_type::ConstRValue, template_function_fl(etl::move(u4))); + } }; }