mirror of
https://github.com/ETLCPP/etl.git
synced 2026-05-01 03:19:10 +08:00
Topic/expected monadic operations (#1219)
* topic/expected-monadic-operations: - added and_then, or_else, transform, and transform_error with simple tests * topic/expected-monadic-operation: - added void TValue specialization operations - updated unit tests to include expected<void, TError> - added is_expected to expected.h, used in and_then to ensure that the returned type is an expected * topic/expected-monadic-operations: - made implementation c++11 compatible * topic/expected-monadic-operations: - started addressing coderabbit feedback * topic/expected-monadic-operations: - adapted invoke to etl - reworked return type deduction - filled in or_else unit tests to be more complete - still need to add invoke unit tests * topic/expected-monadic-operations: -c++14 compliance * topic/expected-monadic-operations: - completed coderabbit suggestions * topic/expected-monadic-operations: - formatting in invoke and expected - added test suite for invoke * topic/expected-monadic-operations: - fixed missing moves for const TValue&& and const TError&& * topic/expected-monadic-operations: - made everything c++11 compatible, very verbose as a result * topic/expected-monadic-operations: - fixed code rabbit rewivew for move semantics in const && overloads * topic/expected-monadic-operations: - moved around a move * topic/expected-monadic-operations: - added etl:: to invoke_result calls that were missing it * topic/expected-monadic-operations: - formatting * topic/expected-monadic-operations: - added invoke for void f() calls for consistency * topic/expected-monadic-operations: - reworked entire thing to be able to handle expected<T,E> to expected<void,E> without even more code duplication * topic/expected-monadic-operations: - added trailing return type to maintain c++11 compatibility * topic/expected-monadic-operations: - fixed mismatch between deduced type and return for a few functions * topic/expected-monadic-operations: - replaced calls to get<TError> and get<TValue> with get<Error_Type> and get<Value_Type> --------- Co-authored-by: Jon Whitfield <jon@volumetrix.com>
This commit is contained in:
parent
520b4a9f46
commit
269f3ffdfd
@ -39,9 +39,20 @@ SOFTWARE.
|
||||
#include "utility.h"
|
||||
#include "variant.h"
|
||||
#include "initializer_list.h"
|
||||
#include "type_traits.h"
|
||||
#include "invoke.h"
|
||||
|
||||
namespace etl
|
||||
{
|
||||
// Forward declaration for is_expected
|
||||
template <typename TValue, typename TError> class expected;
|
||||
|
||||
template <typename T>
|
||||
struct is_expected : etl::false_type {};
|
||||
|
||||
template <typename TValue, typename TError>
|
||||
struct is_expected<expected<TValue,TError>> : etl::true_type {};
|
||||
|
||||
//***************************************************************************
|
||||
/// Base exception for et::expected
|
||||
//***************************************************************************
|
||||
@ -732,6 +743,104 @@ namespace etl
|
||||
}
|
||||
#endif
|
||||
|
||||
#if ETL_USING_CPP11
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, TValue&>::type>::type>
|
||||
auto transform(F&& f) & -> expected<U, TError>
|
||||
{
|
||||
return transform_impl<F, this_type&, U, TValue&>(etl::forward<F>(f), *this);
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, const TValue&>::type>::type>
|
||||
auto transform(F&& f) const& -> expected<U, TError>
|
||||
{
|
||||
return transform_impl<F, const this_type&, U, const TValue&>(etl::forward<F>(f), *this);
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, TValue&&>::type>::type>
|
||||
auto transform(F&& f) && -> expected<U, TError>
|
||||
{
|
||||
return transform_impl<F, this_type&&, U, TValue&&>(etl::forward<F>(f), etl::move(*this));
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, const TValue&&>::type>::type>
|
||||
auto transform(F&& f) const&& -> expected<U, TError>
|
||||
{
|
||||
return transform_impl<F, const this_type&&, U, const TValue&&>(etl::forward<F>(f), etl::move(*this));
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, TValue&>::type>::type>
|
||||
auto and_then(F&& f) & -> U
|
||||
{
|
||||
return and_then_impl<F, this_type&, U, TValue&>(etl::forward<F>(f), *this);
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, const TValue&>::type>::type>
|
||||
auto and_then(F&& f) const& -> U
|
||||
{
|
||||
return and_then_impl<F, const this_type&, U, const TValue&>(etl::forward<F>(f), *this);
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, TValue&&>::type>::type>
|
||||
auto and_then(F&& f) && -> U
|
||||
{
|
||||
return and_then_impl<F, this_type&&, U, TValue&&>(etl::forward<F>(f), etl::move(*this));
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, const TValue&&>::type>::type>
|
||||
auto and_then(F&& f) const&& -> U
|
||||
{
|
||||
return and_then_impl<F, const this_type&&, U, const TValue&&>(etl::forward<F>(f), etl::move(*this));
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, TError&>::type>::type>
|
||||
auto or_else(F&& f) & -> U
|
||||
{
|
||||
return or_else_impl<F, this_type&, U, TError&>(etl::forward<F>(f), *this);
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, const TError&>::type>::type>
|
||||
auto or_else(F&& f) const & -> U
|
||||
{
|
||||
return or_else_impl<F, const this_type&, U, const TError&>(etl::forward<F>(f), *this);
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, TError&&>::type>::type>
|
||||
auto or_else(F&& f) && -> U
|
||||
{
|
||||
return or_else_impl<F, this_type&&, U, TError&&>(etl::forward<F>(f), etl::move(*this));
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, const TError&&>::type>::type>
|
||||
auto or_else(F&& f) const && -> U
|
||||
{
|
||||
return or_else_impl<F, const this_type&&, U, const TError&&>(etl::forward<F>(f), etl::move(*this));
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, TError&>::type>::type>
|
||||
auto transform_error(F&& f) & -> expected<TValue, U>
|
||||
{
|
||||
return transform_error_impl<F, this_type&, U, TError&>(etl::forward<F>(f), *this);
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, const TError&>::type>::type>
|
||||
auto transform_error(F&& f) const & -> expected<TValue, U>
|
||||
{
|
||||
return transform_error_impl<F, const this_type&, U, const TError&>(etl::forward<F>(f), *this);
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, TError&&>::type>::type>
|
||||
auto transform_error(F&& f) && -> expected<TValue, U>
|
||||
{
|
||||
return transform_error_impl<F, this_type&&, U, TError&&>(etl::forward<F>(f), etl::move(*this));
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, const TError&&>::type>::type>
|
||||
auto transform_error(F&& f) const&& -> expected<TValue, U>
|
||||
{
|
||||
return transform_error_impl<F, const this_type&&, U, const TError&&>(etl::forward<F>(f), etl::move(*this));
|
||||
}
|
||||
#endif
|
||||
|
||||
private:
|
||||
|
||||
enum
|
||||
@ -743,6 +852,72 @@ namespace etl
|
||||
|
||||
typedef etl::variant<etl::monostate, value_type, error_type> storage_type;
|
||||
storage_type storage;
|
||||
|
||||
template <typename F, typename TExp, typename TRet, typename TValueRef, typename = typename etl::enable_if<!etl::is_void<TRet>::value>::type>
|
||||
auto transform_impl(F&& f, TExp&& exp) const -> expected<TRet, TError>
|
||||
{
|
||||
if (exp.has_value())
|
||||
{
|
||||
return expected<TRet, TError>(etl::invoke(etl::forward<F>(f), etl::forward<TValueRef>(etl::get<Value_Type>(exp.storage))));
|
||||
}
|
||||
else
|
||||
{
|
||||
return expected<TRet, TError>(unexpected<TError>(etl::forward<TExp>(exp).error()));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F, typename TExp, typename TRet, typename TValueRef, typename = typename etl::enable_if<etl::is_void<TRet>::value>::type>
|
||||
auto transform_impl(F&& f, TExp&& exp) const -> expected<void, TError>
|
||||
{
|
||||
if (exp.has_value())
|
||||
{
|
||||
etl::invoke(etl::forward<F>(f), etl::forward<TValueRef>(etl::get<Value_Type>(exp.storage)));
|
||||
return expected<void, TError>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return expected<void, TError>(unexpected<TError>(etl::forward<TExp>(exp).error()));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F, typename TExp, typename TRet, typename TValueRef, typename = typename etl::enable_if<!etl::is_void<TRet>::value && etl::is_expected<TRet>::value && etl::is_same<typename TRet::error_type, TError>::value>::type>
|
||||
auto and_then_impl(F&& f, TExp&& exp) const -> TRet
|
||||
{
|
||||
if (exp.has_value())
|
||||
{
|
||||
return etl::invoke(etl::forward<F>(f), etl::forward<TValueRef>(etl::get<Value_Type>(exp.storage)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return TRet(unexpected<TError>(etl::forward<TExp>(exp).error()));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F, typename TExp, typename TRet, typename TErrorRef, typename = typename etl::enable_if<!etl::is_void<TRet>::value && etl::is_expected<TRet>::value && etl::is_same<typename TRet::value_type, TValue>::value>::type>
|
||||
auto or_else_impl(F&& f, TExp&& exp) const -> TRet
|
||||
{
|
||||
if (exp.has_value())
|
||||
{
|
||||
return TRet(etl::forward<TExp>(exp).value());
|
||||
}
|
||||
else
|
||||
{
|
||||
return etl::invoke(etl::forward<F>(f), etl::forward<TErrorRef>(etl::get<Error_Type>(exp.storage)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F, typename TExp, typename TRet, typename TErrorRef, typename = typename etl::enable_if<!etl::is_void<TRet>::value>::type>
|
||||
auto transform_error_impl(F&& f, TExp&& exp) const -> expected<TValue, TRet>
|
||||
{
|
||||
if (exp.has_value())
|
||||
{
|
||||
return expected<TValue, TRet>(etl::forward<TExp>(exp).value());
|
||||
}
|
||||
else
|
||||
{
|
||||
return expected<TValue, TRet>(unexpected<TRet>(etl::invoke(etl::forward<F>(f), etl::forward<TErrorRef>(etl::get<Error_Type>(exp.storage)))));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
//*****************************************************************************
|
||||
@ -942,6 +1117,105 @@ namespace etl
|
||||
swap(storage, other.storage);
|
||||
}
|
||||
|
||||
#if ETL_USING_CPP11
|
||||
template<typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void>::type>::type>
|
||||
auto transform(F&& f) & -> expected<U, TError>
|
||||
{
|
||||
return transform_impl<F, this_type&, U>(etl::forward<F>(f), *this);
|
||||
}
|
||||
|
||||
template<typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void>::type>::type>
|
||||
auto transform(F&& f) const & -> expected<U, TError>
|
||||
{
|
||||
return transform_impl<F, const this_type&, U>(etl::forward<F>(f), *this);
|
||||
}
|
||||
|
||||
template<typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void>::type>::type>
|
||||
auto transform(F&& f) && -> expected<U, TError>
|
||||
{
|
||||
return transform_impl<F, this_type&&, U>(etl::forward<F>(f), etl::move(*this));
|
||||
}
|
||||
|
||||
template<typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void>::type>::type>
|
||||
auto transform(F&& f) const && -> expected<U, TError>
|
||||
{
|
||||
return transform_impl<F, const this_type&&, U>(etl::forward<F>(f), etl::move(*this));
|
||||
}
|
||||
|
||||
template<typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void>::type>::type>
|
||||
auto and_then(F&& f) & -> U
|
||||
{
|
||||
return and_then_impl<F, this_type&, U>(etl::forward<F>(f), *this);
|
||||
}
|
||||
|
||||
template<typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void>::type>::type>
|
||||
auto and_then(F&& f) const & -> U
|
||||
{
|
||||
return and_then_impl<F, const this_type&, U>(etl::forward<F>(f), *this);
|
||||
}
|
||||
|
||||
template<typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void>::type>::type>
|
||||
auto and_then(F&& f) && -> U
|
||||
{
|
||||
return and_then_impl<F, this_type&&, U>(etl::forward<F>(f), etl::move(*this));
|
||||
}
|
||||
|
||||
template<typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void>::type>::type>
|
||||
auto and_then(F&& f) const && -> U
|
||||
{
|
||||
return and_then_impl<F, const this_type&&, U>(etl::forward<F>(f), etl::move(*this));
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, TError&>::type>::type>
|
||||
auto or_else(F&& f) & -> U
|
||||
{
|
||||
return or_else_impl<F, this_type&, U, TError&>(etl::forward<F>(f), *this);
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, const TError&>::type>::type>
|
||||
auto or_else(F&& f) const & -> U
|
||||
{
|
||||
return or_else_impl<F, const this_type&, U, const TError&>(etl::forward<F>(f), *this);
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, TError&&>::type>::type>
|
||||
auto or_else(F&& f) && -> U
|
||||
{
|
||||
return or_else_impl<F, this_type&&, U, TError&&>(etl::forward<F>(f), etl::move(*this));
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, const TError&&>::type>::type>
|
||||
auto or_else(F&& f) const && -> U
|
||||
{
|
||||
return or_else_impl<F, const this_type&&, U, const TError&&>(etl::forward<F>(f), etl::move(*this));
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, TError&>::type>::type>
|
||||
auto transform_error(F&& f) & -> expected<void, U>
|
||||
{
|
||||
return transform_error_impl<F, this_type&, U, TError&>(etl::forward<F>(f), *this);
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, const TError&>::type>::type>
|
||||
auto transform_error(F&& f) const & -> expected<void, U>
|
||||
{
|
||||
return transform_error_impl<F, const this_type&, U, const TError&>(etl::forward<F>(f), *this);
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, TError&&>::type>::type>
|
||||
auto transform_error(F&& f) && -> expected<void, U>
|
||||
{
|
||||
return transform_error_impl<F, this_type&&, U, TError&&>(etl::forward<F>(f), etl::move(*this));
|
||||
}
|
||||
|
||||
template <typename F, typename U = typename etl::remove_cvref<typename etl::invoke_result<F, void, const TError&&>::type>::type>
|
||||
auto transform_error(F&& f) const && -> expected<void, U>
|
||||
{
|
||||
return transform_error_impl<F, const this_type&&, U, const TError&&>(etl::forward<F>(f), *this);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
private:
|
||||
|
||||
enum
|
||||
@ -951,6 +1225,72 @@ namespace etl
|
||||
};
|
||||
|
||||
etl::variant<etl::monostate, error_type> storage;
|
||||
|
||||
template <typename F, typename TExp, typename TRet, typename = typename etl::enable_if<!etl::is_void<TRet>::value>::type>
|
||||
auto transform_impl(F&& f, TExp&& exp) const -> expected<TRet, TError>
|
||||
{
|
||||
if (exp.has_value())
|
||||
{
|
||||
return expected<TRet, TError>(etl::invoke(etl::forward<F>(f)));
|
||||
}
|
||||
else
|
||||
{
|
||||
return expected<TRet, TError>(unexpected<TError>(etl::forward<TExp>(exp).error()));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F, typename TExp, typename TRet, typename = typename etl::enable_if<etl::is_void<TRet>::value>::type>
|
||||
auto transform_impl(F&& f, TExp&& exp) const -> expected<void, TError>
|
||||
{
|
||||
if (exp.has_value())
|
||||
{
|
||||
etl::invoke(etl::forward<F>(f));
|
||||
return expected<void, TError>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return expected<void, TError>(unexpected<TError>(etl::forward<TExp>(exp).error()));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F, typename TExp, typename TRet, typename = typename etl::enable_if<!etl::is_void<TRet>::value && etl::is_expected<TRet>::value && etl::is_same<typename TRet::error_type, TError>::value>::type>
|
||||
auto and_then_impl(F&& f, TExp&& exp) const -> TRet
|
||||
{
|
||||
if (exp.has_value())
|
||||
{
|
||||
return etl::invoke(etl::forward<F>(f));
|
||||
}
|
||||
else
|
||||
{
|
||||
return TRet(unexpected<TError>(etl::forward<TExp>(exp).error()));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F, typename TExp, typename TRet, typename TErrorRef, typename = typename etl::enable_if<!etl::is_void<TRet>::value && etl::is_expected<TRet>::value && etl::is_same<typename TRet::value_type, void>::value>::type>
|
||||
auto or_else_impl(F&& f, TExp&& exp) const -> TRet
|
||||
{
|
||||
if (exp.has_value())
|
||||
{
|
||||
return TRet();
|
||||
}
|
||||
else
|
||||
{
|
||||
return etl::invoke(etl::forward<F>(f), etl::forward<TErrorRef>(etl::get<Error_Type>(exp.storage)));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename F, typename TExp, typename TRet, typename TErrorRef, typename = typename etl::enable_if<!etl::is_void<TRet>::value>::type>
|
||||
auto transform_error_impl(F&& f, TExp&& exp) const -> expected<void, TRet>
|
||||
{
|
||||
if (exp.has_value())
|
||||
{
|
||||
return expected<void, TRet>();
|
||||
}
|
||||
else
|
||||
{
|
||||
return expected<void, TRet>(unexpected<TRet>(etl::invoke(etl::forward<F>(f), etl::forward<TErrorRef>(etl::get<Error_Type>(exp.storage)))));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
148
include/etl/invoke.h
Normal file
148
include/etl/invoke.h
Normal file
@ -0,0 +1,148 @@
|
||||
///\file
|
||||
|
||||
/******************************************************************************
|
||||
The MIT License(MIT)
|
||||
|
||||
Embedded Template Library.
|
||||
https://github.com/ETLCPP/etl
|
||||
https://www.etlcpp.com
|
||||
|
||||
Copyright(c) 2025 John Wellbelove
|
||||
|
||||
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_INVOKE_INCLUDED
|
||||
#define ETL_INVOKE_INCLUDED
|
||||
|
||||
#include "platform.h"
|
||||
#include "functional.h"
|
||||
#include "function_traits.h"
|
||||
#include "type_traits.h"
|
||||
#include "utility.h"
|
||||
|
||||
namespace etl {
|
||||
template <typename T> struct logical_not_t : etl::integral_constant<bool, !bool(T::value)> {};
|
||||
|
||||
/// is T a function -- a function cannot be const qualified like a variable
|
||||
template<typename T> struct is_function : public etl::integral_constant<bool, !etl::is_const<const T>::value> { };
|
||||
template<typename T> struct is_function<T&> : public etl::false_type { };
|
||||
template<typename T> struct is_function<T&&> : public etl::false_type { };
|
||||
|
||||
/// is T a member pointer
|
||||
template<typename T> struct is_member_pointer_helper : etl::false_type {};
|
||||
template<typename T, typename C> struct is_member_pointer_helper<T C::*> : etl::true_type {};
|
||||
template<typename T> struct is_member_pointer : is_member_pointer_helper<etl::remove_cv_t<T>> {};
|
||||
|
||||
/// is T a member function pointer
|
||||
template <typename> struct is_member_function_pointer_helper : etl::false_type {};
|
||||
template <typename T, typename C> struct is_member_function_pointer_helper<T C::*> : public etl::is_function<T>::type {};
|
||||
template <typename T> struct is_member_function_pointer : public is_member_function_pointer_helper<etl::remove_cv_t<T>>::type {};
|
||||
|
||||
/// is T a member object pointer
|
||||
template<typename> struct is_member_object_pointer_helper : public etl::false_type { };
|
||||
template<typename T, typename C> struct is_member_object_pointer_helper<T C::*> : public logical_not_t<etl::is_function<T>>::type { };
|
||||
template<typename T> struct is_member_object_pointer : public is_member_object_pointer_helper<etl::remove_cv_t<T>>::type {};
|
||||
|
||||
template <
|
||||
typename F,
|
||||
typename ... TArgs,
|
||||
typename = typename etl::enable_if<
|
||||
!etl::is_member_pointer<etl::decay_t<F>>::value>::type
|
||||
>
|
||||
ETL_CONSTEXPR auto invoke(F&& f, TArgs&& ... args)
|
||||
-> decltype(etl::forward<F>(f)(etl::forward<TArgs>(args)...)) {
|
||||
return etl::forward<F>(f)(etl::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
template <
|
||||
typename F,
|
||||
typename T,
|
||||
typename... TArgs,
|
||||
typename = typename etl::enable_if<
|
||||
etl::is_member_function_pointer<etl::decay_t<F>>::value &&
|
||||
!etl::is_pointer<etl::decay_t<T>>::value
|
||||
>::type
|
||||
>
|
||||
ETL_CONSTEXPR auto invoke(F&& f, T&& t, TArgs&&... args)
|
||||
-> decltype((etl::forward<T>(t).*f)(etl::forward<TArgs>(args)...))
|
||||
{
|
||||
return (etl::forward<T>(t).*f)(etl::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
template <
|
||||
typename F,
|
||||
typename T,
|
||||
typename ... TArgs,
|
||||
typename = typename etl::enable_if<
|
||||
etl::is_member_function_pointer<etl::decay_t<F>>::value &&
|
||||
etl::is_pointer<etl::decay_t<T>>::value
|
||||
>::type
|
||||
>
|
||||
ETL_CONSTEXPR auto invoke(F&& f, T&& t, TArgs&&... args)
|
||||
-> decltype(((*etl::forward<T>(t)).*f)(etl::forward<TArgs>(args)...))
|
||||
{
|
||||
return ((*etl::forward<T>(t)).*f)(etl::forward<TArgs>(args)...);
|
||||
}
|
||||
|
||||
template <
|
||||
typename F,
|
||||
typename T,
|
||||
typename = typename etl::enable_if<
|
||||
etl::is_member_object_pointer<etl::decay_t<F>>::value &&
|
||||
!etl::is_pointer<etl::decay_t<T>>::value
|
||||
>::type
|
||||
>
|
||||
ETL_CONSTEXPR auto invoke(F&& f, T&& t)
|
||||
-> decltype(etl::forward<T>(t).*f)
|
||||
{
|
||||
return etl::forward<T>(t).*f;
|
||||
}
|
||||
|
||||
template <
|
||||
typename F,
|
||||
typename T,
|
||||
typename = typename etl::enable_if<
|
||||
etl::is_member_object_pointer<etl::decay_t<F>>::value &&
|
||||
etl::is_pointer<etl::decay_t<T>>::value
|
||||
>::type
|
||||
>
|
||||
ETL_CONSTEXPR auto invoke(F&& f, T&& t)
|
||||
-> decltype(((*etl::forward<T>(t)).*f))
|
||||
{
|
||||
return ((*etl::forward<T>(t)).*f);
|
||||
}
|
||||
|
||||
template <class F, class, class ... Us> struct invoke_result;
|
||||
|
||||
template <typename F, typename... Us>
|
||||
struct invoke_result<
|
||||
F,
|
||||
etl::void_t<decltype(etl::invoke(etl::declval<F>(), etl::declval<Us>()...))>,
|
||||
Us...
|
||||
> {
|
||||
using type = decltype(etl::invoke(etl::declval<F>(), etl::declval<Us>()...));
|
||||
};
|
||||
|
||||
template <typename F, typename... Us>
|
||||
using invoke_result_t = typename invoke_result<F, void, Us...>::type;
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@ -209,6 +209,7 @@ add_executable(etl_tests
|
||||
test_invert.cpp
|
||||
test_io_port.cpp
|
||||
test_iterator.cpp
|
||||
test_invoke.cpp
|
||||
test_jenkins.cpp
|
||||
test_largest.cpp
|
||||
test_limiter.cpp
|
||||
|
||||
@ -792,5 +792,616 @@ namespace
|
||||
CHECK_TRUE(test_unexp_1_swap == test_unexp_2);
|
||||
CHECK_TRUE(test_unexp_2_swap == test_unexp_1);
|
||||
}
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
template <typename TValue, typename TExpected, typename Enable = void>
|
||||
struct value_type_helper
|
||||
{
|
||||
static bool check(TExpected& expected)
|
||||
{
|
||||
return etl::is_same<typename etl::decay<decltype(expected.value())>::type, TValue>::value;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TValue, typename TExpected>
|
||||
struct value_type_helper<TValue, TExpected, typename etl::enable_if<etl::is_void<TValue>::value>::type>
|
||||
{
|
||||
static bool check(TExpected& expected)
|
||||
{
|
||||
(void)expected;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename TValue, typename TError, typename TExpected>
|
||||
bool check_expected_type_helper(TExpected& expected)
|
||||
{
|
||||
|
||||
bool value_type_ok = value_type_helper<TValue, TExpected>::check(expected);
|
||||
|
||||
bool error_type_ok = etl::is_same<typename etl::decay<decltype(expected.error())>::type, TError>::value;
|
||||
|
||||
bool expected_type_ok = etl::is_same<typename etl::decay<decltype(expected)>::type, etl::expected<TValue, TError> >::value;
|
||||
|
||||
return value_type_ok && error_type_ok && expected_type_ok;
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_or_else)
|
||||
{
|
||||
Expected expected = {Value("or_else_with_value")};
|
||||
Expected expected_error = {Unexpected(Error("or_else_with_error"))};
|
||||
|
||||
const Expected expected_const = {Value("const_or_else_with_value")};
|
||||
const Expected expected_error_const = {Unexpected(Error("const_or_else_with_error"))};
|
||||
|
||||
bool error_generated {false};
|
||||
|
||||
auto expected_out = expected.or_else([&error_generated](Error e)
|
||||
-> Expected
|
||||
{
|
||||
error_generated = true;
|
||||
return Unexpected(e);
|
||||
});
|
||||
|
||||
CHECK_FALSE(error_generated);
|
||||
CHECK_TRUE(expected_out.has_value());
|
||||
CHECK_EQUAL("or_else_with_value", expected_out.value().v);
|
||||
|
||||
auto with_value_type_check = check_expected_type_helper<Value, Error>(expected_out);
|
||||
CHECK_TRUE(with_value_type_check);
|
||||
|
||||
error_generated = false;
|
||||
auto expected_const_out = expected_const.or_else([&error_generated](const Error& e) -> Expected {
|
||||
error_generated = true;
|
||||
return Unexpected(e);
|
||||
});
|
||||
|
||||
CHECK_FALSE(error_generated);
|
||||
CHECK_TRUE(expected_const_out.has_value());
|
||||
CHECK_EQUAL("const_or_else_with_value", expected_const_out.value().v);
|
||||
|
||||
error_generated = false;
|
||||
auto unexpected_out = expected_error.or_else([&error_generated](Error e) -> Expected {
|
||||
error_generated = true;
|
||||
return Unexpected(e);
|
||||
});
|
||||
|
||||
CHECK_TRUE(error_generated);
|
||||
CHECK_FALSE(unexpected_out.has_value());
|
||||
|
||||
auto with_error_type_check = check_expected_type_helper<Value,Error>(unexpected_out);
|
||||
CHECK_TRUE(with_error_type_check);
|
||||
|
||||
CHECK_EQUAL("or_else_with_error", unexpected_out.error().e);
|
||||
|
||||
|
||||
error_generated = false;
|
||||
auto unexpected_const_out = expected_error_const.or_else([&error_generated](const Error& e) -> Expected {
|
||||
error_generated = true;
|
||||
return Unexpected(e);
|
||||
});
|
||||
|
||||
CHECK_TRUE(error_generated);
|
||||
CHECK_FALSE(unexpected_const_out.has_value());
|
||||
|
||||
auto with_error_const_type_check = check_expected_type_helper<Value,Error>(unexpected_const_out);
|
||||
CHECK_TRUE(with_error_const_type_check);
|
||||
|
||||
CHECK_EQUAL("const_or_else_with_error", unexpected_const_out.error().e);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_or_else_move_constructor) {
|
||||
ExpectedM expected = ExpectedM(ValueM("or_else_with_value"));
|
||||
ExpectedM expected_error = ExpectedM(UnexpectedM(ErrorM("or_else_with_error")));
|
||||
bool error_generated {false};
|
||||
|
||||
auto expected_out = etl::move(expected).or_else([&error_generated](ErrorM e) -> ExpectedM {
|
||||
error_generated = true;
|
||||
UnexpectedM unexpected(etl::move(e));
|
||||
return ExpectedM(etl::move(unexpected));
|
||||
});
|
||||
|
||||
CHECK_FALSE(error_generated);
|
||||
CHECK_TRUE(expected_out.has_value());
|
||||
CHECK_EQUAL("or_else_with_value", expected_out.value().v);
|
||||
|
||||
auto with_value_type_check = check_expected_type_helper<ValueM, ErrorM>(expected_out);
|
||||
CHECK_TRUE(with_value_type_check);
|
||||
|
||||
auto unexpected_out = etl::move(expected_error).or_else([&error_generated](ErrorM e) -> ExpectedM {
|
||||
error_generated = true;
|
||||
CHECK_EQUAL("or_else_with_error", e.e);
|
||||
|
||||
UnexpectedM unexpected(etl::move(e));
|
||||
return ExpectedM(etl::move(unexpected));
|
||||
});
|
||||
|
||||
CHECK_TRUE(error_generated);
|
||||
CHECK_FALSE(unexpected_out.has_value());
|
||||
|
||||
auto with_error_type_check = check_expected_type_helper<ValueM,ErrorM>(unexpected_out);
|
||||
CHECK_TRUE(with_error_type_check);
|
||||
|
||||
CHECK_EQUAL("or_else_with_error", unexpected_out.error().e);
|
||||
|
||||
//The following should NOT compile. The const & overload should attempt to copy
|
||||
// const ExpectedM expected_error_const = ExpectedM(ValueM("or_else_with_value"));
|
||||
// expected_error_const.or_else([&error_generated](ErrorM e) -> const ExpectedM {
|
||||
// error_generated = true;
|
||||
// UnexpectedM unexpected(etl::move(e));
|
||||
// return ExpectedM(etl::move(unexpected));
|
||||
// });
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_or_else_void) {
|
||||
ExpectedV expected = ExpectedV();
|
||||
ExpectedV expected_error = ExpectedV(Unexpected(Error("or_else_with_error")));
|
||||
bool error_generated {false};
|
||||
|
||||
auto expected_out = expected.or_else([&error_generated](Error e) -> ExpectedV {
|
||||
error_generated = true;
|
||||
return Unexpected(e);
|
||||
});
|
||||
|
||||
CHECK_FALSE(error_generated);
|
||||
CHECK_TRUE(expected_out.has_value());
|
||||
|
||||
auto with_value_type_check = check_expected_type_helper<void, Error>(expected_out);
|
||||
CHECK_TRUE(with_value_type_check);
|
||||
|
||||
auto unexpected_out = expected_error.or_else([&error_generated](Error e) -> ExpectedV {
|
||||
error_generated = true;
|
||||
CHECK_EQUAL("or_else_with_error", e.e);
|
||||
return Unexpected(e);
|
||||
});
|
||||
|
||||
CHECK_TRUE(error_generated);
|
||||
CHECK_FALSE(unexpected_out.has_value());
|
||||
|
||||
auto with_error_type_check = check_expected_type_helper<void, Error>(unexpected_out);
|
||||
CHECK_TRUE(with_error_type_check);
|
||||
|
||||
CHECK_EQUAL("or_else_with_error", unexpected_out.error().e);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_or_else_change_error) {
|
||||
Expected expected_error = {Unexpected(Error("or_else_with_error"))};
|
||||
ExpectedV expectedV_error = ExpectedV(Unexpected(Error("or_else_with_error")));
|
||||
|
||||
auto change_to_string = expectedV_error.or_else([](Error e) -> etl::expected<void, std::string> {
|
||||
return etl::unexpected<std::string>(e.e.append("_to_string"));
|
||||
});
|
||||
|
||||
auto with_error_type_check = check_expected_type_helper<void,std::string>(change_to_string);
|
||||
CHECK_TRUE(with_error_type_check);
|
||||
|
||||
CHECK_EQUAL("or_else_with_error_to_string", change_to_string.error());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_or_else_change_error_move_constructor) {
|
||||
ExpectedM expected_error = ExpectedM(UnexpectedM(ErrorM("or_else_with_error")));
|
||||
|
||||
auto change_to_string = etl::move(expected_error).or_else([](ErrorM e) -> etl::expected<ValueM, std::string> {
|
||||
return etl::unexpected<std::string>(e.e.append("_to_string"));
|
||||
});
|
||||
|
||||
auto with_error_type_check = check_expected_type_helper<ValueM,std::string>(change_to_string);
|
||||
CHECK_TRUE(with_error_type_check);
|
||||
|
||||
CHECK_EQUAL("or_else_with_error_to_string", change_to_string.error());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_or_else_const_rvalue) {
|
||||
bool error_generated {false};
|
||||
auto temp_expected = Expected(Unexpected(Error("temp_const_error")));
|
||||
|
||||
auto unexpected_out = static_cast<const Expected&&>(temp_expected)
|
||||
.or_else([&error_generated](const Error& e) -> Expected {
|
||||
error_generated = true;
|
||||
CHECK_EQUAL("temp_const_error", e.e);
|
||||
return Expected(Unexpected(etl::move(e)));
|
||||
});
|
||||
|
||||
CHECK_TRUE(error_generated);
|
||||
CHECK_EQUAL("temp_const_error", unexpected_out.error().e);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_transform) {
|
||||
Expected expected = {Value("transform_with_value")};
|
||||
Expected expected_error = {Unexpected(Error("transform_with_error"))};
|
||||
const Expected expected_const = {Value("const_transform_with_value")};
|
||||
|
||||
auto expected_out = expected.transform([](Value v) {
|
||||
auto s = v.v.append("_transformed");
|
||||
return s;
|
||||
});
|
||||
|
||||
CHECK_TRUE(expected_out.has_value());
|
||||
CHECK_EQUAL("transform_with_value_transformed", expected_out.value());
|
||||
|
||||
auto with_value_type_check = check_expected_type_helper<std::string, Error>(expected_out);
|
||||
CHECK_TRUE(with_value_type_check);
|
||||
|
||||
|
||||
auto expected_out_const = expected_const.transform([](const Value& v) {
|
||||
auto s = v;
|
||||
return s.v.append("_transformed");
|
||||
});
|
||||
|
||||
CHECK_TRUE(expected_out_const.has_value());
|
||||
CHECK_EQUAL("const_transform_with_value_transformed", expected_out_const.value());
|
||||
|
||||
auto const_with_value_type_check = check_expected_type_helper<std::string, Error>(expected_out_const);
|
||||
CHECK_TRUE(const_with_value_type_check);
|
||||
|
||||
auto unexpected_out = expected_error.transform([](Value v) {
|
||||
auto s = v.v.append("_transformed");
|
||||
return s;
|
||||
});
|
||||
|
||||
CHECK_FALSE(unexpected_out.has_value());
|
||||
|
||||
auto with_error_type_check = check_expected_type_helper<std::string,Error>(unexpected_out);
|
||||
CHECK_TRUE(with_error_type_check);
|
||||
|
||||
CHECK_EQUAL("transform_with_error", unexpected_out.error().e);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_transform_move_constructor) {
|
||||
ExpectedM expected = {ValueM("transform_with_value")};
|
||||
ExpectedM expected_error = ExpectedM(UnexpectedM(ErrorM("transform_with_error")));
|
||||
|
||||
auto expected_out = etl::move(expected).transform([](ValueM v) {
|
||||
auto s = v.v.append("_transformed");
|
||||
return etl::move(s);
|
||||
});
|
||||
|
||||
CHECK_TRUE(expected_out.has_value());
|
||||
CHECK_EQUAL("transform_with_value_transformed", expected_out.value());
|
||||
|
||||
auto with_value_type_check = check_expected_type_helper<std::string, ErrorM>(expected_out);
|
||||
CHECK_TRUE(with_value_type_check);
|
||||
|
||||
auto unexpected_out = etl::move(expected_error).transform([](ValueM v) {
|
||||
auto s = v.v.append("_transformed");
|
||||
return etl::move(s);
|
||||
});
|
||||
|
||||
CHECK_FALSE(unexpected_out.has_value());
|
||||
|
||||
auto with_error_type_check = check_expected_type_helper<std::string,ErrorM>(unexpected_out);
|
||||
CHECK_TRUE(with_error_type_check);
|
||||
|
||||
CHECK_EQUAL("transform_with_error", unexpected_out.error().e);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_transform_void) {
|
||||
ExpectedV expected;
|
||||
ExpectedV expected_error = {Unexpected(Error("transform_with_error"))};
|
||||
|
||||
auto expected_out = expected.transform([]() {
|
||||
std::string s("_transformed");
|
||||
return s;
|
||||
});
|
||||
|
||||
CHECK_TRUE(expected_out.has_value());
|
||||
CHECK_EQUAL("_transformed", expected_out.value());
|
||||
|
||||
auto with_value_type_check = check_expected_type_helper<std::string, Error>(expected_out);
|
||||
CHECK_TRUE(with_value_type_check);
|
||||
|
||||
auto unexpected_out = expected_error.transform([]() {
|
||||
std::string s("_transformed");
|
||||
return s;
|
||||
});
|
||||
|
||||
CHECK_FALSE(unexpected_out.has_value());
|
||||
|
||||
auto with_error_type_check = check_expected_type_helper<std::string,Error>(unexpected_out);
|
||||
CHECK_TRUE(with_error_type_check);
|
||||
|
||||
CHECK_EQUAL("transform_with_error", unexpected_out.error().e);
|
||||
}
|
||||
|
||||
TEST(test_transform_void_move) {
|
||||
ExpectedVM expected;
|
||||
ExpectedVM expected_error = {UnexpectedM(ErrorM("transform_with_error"))};
|
||||
|
||||
auto expected_out = etl::move(expected).transform([]() {
|
||||
std::string s("_transformed");
|
||||
return s;
|
||||
});
|
||||
|
||||
CHECK_TRUE(expected_out.has_value());
|
||||
CHECK_EQUAL("_transformed", expected_out.value());
|
||||
|
||||
auto with_value_type_check = check_expected_type_helper<std::string, ErrorM>(expected_out);
|
||||
CHECK_TRUE(with_value_type_check);
|
||||
|
||||
auto unexpected_out = etl::move(expected_error).transform([]() {
|
||||
std::string s("_transformed");
|
||||
return s;
|
||||
});
|
||||
|
||||
CHECK_FALSE(unexpected_out.has_value());
|
||||
|
||||
auto with_error_type_check = check_expected_type_helper<std::string,ErrorM>(unexpected_out);
|
||||
CHECK_TRUE(with_error_type_check);
|
||||
|
||||
CHECK_EQUAL("transform_with_error", unexpected_out.error().e);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_transform_to_void) {
|
||||
Expected expected {Value("transform_to_void")};
|
||||
|
||||
bool executed {false};
|
||||
auto expected_out = expected.transform([&executed](Value v) {
|
||||
(void) v;
|
||||
executed = true;
|
||||
CHECK_EQUAL("transform_to_void", v.v);
|
||||
return;
|
||||
});
|
||||
|
||||
auto to_void_type_check = check_expected_type_helper<void, Error>(expected_out);
|
||||
CHECK_TRUE(to_void_type_check);
|
||||
|
||||
CHECK_TRUE(expected_out.has_value());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_and_then) {
|
||||
Expected expected = {Value("and_then_with_value")};
|
||||
Expected expected_error = {Unexpected(Error("and_then_with_error"))};
|
||||
const Expected expected_const = {Value("const_and_then_with_value")};
|
||||
|
||||
auto expected_out = expected.and_then([](Value v) -> Expected {
|
||||
return Value(v.v.append("_and_thened"));
|
||||
});
|
||||
|
||||
CHECK_TRUE(expected_out.has_value());
|
||||
CHECK_EQUAL("and_then_with_value_and_thened", expected_out.value().v);
|
||||
|
||||
auto with_value_type_check = check_expected_type_helper<Value, Error>(expected_out);
|
||||
CHECK_TRUE(with_value_type_check);
|
||||
|
||||
auto expected_out_const = expected_const.and_then([](const Value& v) -> Expected {
|
||||
auto s = v;
|
||||
return Value(s.v.append("_and_thened"));
|
||||
});
|
||||
|
||||
CHECK_TRUE(expected_out_const.has_value());
|
||||
CHECK_EQUAL("const_and_then_with_value_and_thened", expected_out_const.value().v);
|
||||
|
||||
auto const_with_value_type_check = check_expected_type_helper<Value, Error>(expected_out_const);
|
||||
CHECK_TRUE(const_with_value_type_check);
|
||||
|
||||
auto unexpected_out = expected_error.and_then([](Value v) -> Expected {
|
||||
return Value(v.v.append("_and_thened"));
|
||||
});
|
||||
|
||||
CHECK_FALSE(unexpected_out.has_value());
|
||||
|
||||
auto with_error_type_check = check_expected_type_helper<Value,Error>(unexpected_out);
|
||||
CHECK_TRUE(with_error_type_check);
|
||||
|
||||
CHECK_EQUAL("and_then_with_error", unexpected_out.error().e);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_and_then_move_constructor) {
|
||||
ExpectedM expected = ExpectedM(ValueM("and_then_with_value"));
|
||||
ExpectedM expected_error = ExpectedM(UnexpectedM(ErrorM("and_then_with_error")));
|
||||
|
||||
auto expected_out = etl::move(expected).and_then([](ValueM v) -> ExpectedM {
|
||||
return ValueM(etl::move(v.v.append("_and_thened")));
|
||||
});
|
||||
|
||||
CHECK_TRUE(expected_out.has_value());
|
||||
CHECK_EQUAL("and_then_with_value_and_thened", expected_out.value().v);
|
||||
|
||||
auto with_value_type_check = check_expected_type_helper<ValueM, ErrorM>(expected_out);
|
||||
CHECK_TRUE(with_value_type_check);
|
||||
|
||||
auto unexpected_out = etl::move(expected_error).and_then([](ValueM&& v) -> ExpectedM {
|
||||
return ValueM(v.v.append("_and_thened"));
|
||||
});
|
||||
|
||||
CHECK_FALSE(unexpected_out.has_value());
|
||||
|
||||
auto with_error_type_check = check_expected_type_helper<ValueM,ErrorM>(unexpected_out);
|
||||
CHECK_TRUE(with_error_type_check);
|
||||
|
||||
CHECK_EQUAL("and_then_with_error", unexpected_out.error().e);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_and_then_void) {
|
||||
ExpectedV expected;
|
||||
ExpectedV expected_error = {Unexpected(Error("and_then_with_error"))};
|
||||
auto and_thened {false};
|
||||
|
||||
auto expected_out = expected.and_then([&and_thened]() -> ExpectedV {
|
||||
and_thened = true;
|
||||
return ExpectedV();
|
||||
});
|
||||
|
||||
CHECK_TRUE(and_thened);
|
||||
CHECK_TRUE(expected_out.has_value());
|
||||
|
||||
auto with_value_type_check = check_expected_type_helper<void, Error>(expected_out);
|
||||
CHECK_TRUE(with_value_type_check);
|
||||
|
||||
and_thened = false;
|
||||
auto unexpected_out = expected_error.and_then([&and_thened]() -> ExpectedV {
|
||||
and_thened = true;
|
||||
return ExpectedV();
|
||||
});
|
||||
|
||||
CHECK_FALSE(unexpected_out.has_value());
|
||||
|
||||
auto with_error_type_check = check_expected_type_helper<void,Error>(unexpected_out);
|
||||
CHECK_TRUE(with_error_type_check);
|
||||
|
||||
CHECK_EQUAL("and_then_with_error", unexpected_out.error().e);
|
||||
}
|
||||
|
||||
TEST(test_and_then_void_move) {
|
||||
ExpectedVM expected;
|
||||
ExpectedVM expected_error = {UnexpectedM(ErrorM("and_then_with_error"))};
|
||||
auto and_thened {false};
|
||||
|
||||
auto expected_out = etl::move(expected).and_then([&and_thened]() -> ExpectedVM {
|
||||
and_thened = true;
|
||||
return ExpectedVM();
|
||||
});
|
||||
|
||||
CHECK_TRUE(and_thened);
|
||||
CHECK_TRUE(expected_out.has_value());
|
||||
|
||||
auto with_value_type_check = check_expected_type_helper<void, ErrorM>(expected_out);
|
||||
CHECK_TRUE(with_value_type_check);
|
||||
|
||||
and_thened = false;
|
||||
auto unexpected_out = etl::move(expected_error).and_then([&and_thened]() -> ExpectedVM {
|
||||
and_thened = true;
|
||||
return ExpectedVM();
|
||||
});
|
||||
|
||||
CHECK_FALSE(unexpected_out.has_value());
|
||||
|
||||
auto with_error_type_check = check_expected_type_helper<void,ErrorM>(unexpected_out);
|
||||
CHECK_TRUE(with_error_type_check);
|
||||
|
||||
CHECK_EQUAL("and_then_with_error", unexpected_out.error().e);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_transform_error) {
|
||||
Expected expected = {Value("transform_error_with_value")};
|
||||
Expected expected_error = {Unexpected(Error("transform_error_with_error"))};
|
||||
|
||||
auto expected_out = expected.transform_error([](Error e) {
|
||||
auto s = e.e.append("_transformed");
|
||||
return s;
|
||||
});
|
||||
|
||||
CHECK_TRUE(expected_out.has_value());
|
||||
CHECK_EQUAL("transform_error_with_value", expected_out.value().v);
|
||||
|
||||
auto with_value_type_check = check_expected_type_helper<Value, std::string>(expected_out);
|
||||
CHECK_TRUE(with_value_type_check);
|
||||
|
||||
auto unexpected_out = expected_error.transform_error([](Error e) {
|
||||
std::string s = e.e.append("_transformed");
|
||||
return s;
|
||||
});
|
||||
|
||||
CHECK_FALSE(unexpected_out.has_value());
|
||||
|
||||
auto with_error_type_check = check_expected_type_helper<Value,std::string>(unexpected_out);
|
||||
CHECK_TRUE(with_error_type_check);
|
||||
|
||||
CHECK_EQUAL("transform_error_with_error_transformed", unexpected_out.error());
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
|
||||
TEST(test_transform_error_move_constructor) {
|
||||
ExpectedM expected = ExpectedM(ValueM("transform_error_with_value"));
|
||||
ExpectedM expected_error = ExpectedM(UnexpectedM(ErrorM("transform_error_with_error")));
|
||||
|
||||
auto expected_out = etl::move(expected).transform_error([](ErrorM e) {
|
||||
auto s = e.e.append("_transformed");
|
||||
return s;
|
||||
});
|
||||
|
||||
CHECK_TRUE(expected_out.has_value());
|
||||
CHECK_EQUAL("transform_error_with_value", expected_out.value().v);
|
||||
|
||||
auto with_value_type_check = check_expected_type_helper<ValueM, std::string>(expected_out);
|
||||
CHECK_TRUE(with_value_type_check);
|
||||
|
||||
auto unexpected_out = etl::move(expected_error).transform_error([](ErrorM e) {
|
||||
std::string s = e.e.append("_transformed");
|
||||
return s;
|
||||
});
|
||||
|
||||
CHECK_FALSE(unexpected_out.has_value());
|
||||
|
||||
auto with_error_type_check = check_expected_type_helper<ValueM,std::string>(unexpected_out);
|
||||
CHECK_TRUE(with_error_type_check);
|
||||
|
||||
CHECK_EQUAL("transform_error_with_error_transformed", unexpected_out.error());
|
||||
}
|
||||
|
||||
TEST(test_transform_error_const_rvalue) {
|
||||
bool error_generated {false};
|
||||
auto temp_expected = Expected(Unexpected(Error("temp_const_error")));
|
||||
|
||||
auto unexpected_out = static_cast<const Expected&&>(temp_expected)
|
||||
.transform_error([&error_generated](const Error& e) -> std::string {
|
||||
error_generated = true;
|
||||
CHECK_EQUAL("temp_const_error", e.e);
|
||||
|
||||
return e.e;
|
||||
});
|
||||
|
||||
CHECK_TRUE(error_generated);
|
||||
CHECK_EQUAL("temp_const_error", unexpected_out.error());
|
||||
}
|
||||
|
||||
TEST(test_transform_error_void_value) {
|
||||
ExpectedV expected;
|
||||
ExpectedV expected_error = UnexpectedV(Error("transform_error_void_value"));
|
||||
bool executed {false};
|
||||
|
||||
auto expected_out = expected.transform_error([&executed](const Error& e) {
|
||||
executed = true;
|
||||
return e.e;
|
||||
});
|
||||
|
||||
CHECK_FALSE(executed);
|
||||
CHECK_TRUE(expected_out.has_value());
|
||||
|
||||
auto with_value_type_check = check_expected_type_helper<void, std::string>(expected_out);
|
||||
CHECK_TRUE(with_value_type_check);
|
||||
|
||||
auto unexpected_out = expected_error.transform_error([&executed](const Error& e) {
|
||||
executed = true;
|
||||
auto s = e.e;
|
||||
return s.append("_transformed");
|
||||
});
|
||||
|
||||
CHECK_TRUE(executed);
|
||||
CHECK_EQUAL("transform_error_void_value_transformed", unexpected_out.error());
|
||||
|
||||
auto with_error_type_check = check_expected_type_helper<void, std::string>(unexpected_out);
|
||||
CHECK_TRUE(with_error_type_check);
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
184
test/test_invoke.cpp
Normal file
184
test/test_invoke.cpp
Normal file
@ -0,0 +1,184 @@
|
||||
/******************************************************************************
|
||||
The MIT License(MIT)
|
||||
|
||||
Embedded Template Library.
|
||||
https://github.com/ETLCPP/etl
|
||||
https://www.etlcpp.com
|
||||
|
||||
Copyright(c) 2025 John Wellbelove
|
||||
|
||||
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/invoke.h"
|
||||
#include <etl/type_traits.h>
|
||||
|
||||
namespace
|
||||
{
|
||||
struct TestClass
|
||||
{
|
||||
int member_obj = 42;
|
||||
std::string member_str = "hello";
|
||||
|
||||
int get_int()
|
||||
{
|
||||
return member_obj;
|
||||
}
|
||||
|
||||
int const_get_int() const
|
||||
{
|
||||
return member_obj + 10;
|
||||
}
|
||||
void set_int(int v)
|
||||
{
|
||||
member_obj = v;
|
||||
}
|
||||
static int static_func(int x)
|
||||
{
|
||||
return x * 2;
|
||||
}
|
||||
};
|
||||
|
||||
int standalone_func(int a, int b)
|
||||
{
|
||||
return a + b;
|
||||
}
|
||||
|
||||
struct Functor
|
||||
{
|
||||
int operator()(int x) const
|
||||
{
|
||||
return x * 5;
|
||||
}
|
||||
};
|
||||
|
||||
SUITE(test_invoke)
|
||||
{
|
||||
//*************************************************************************
|
||||
TEST(test_type_traits_functions)
|
||||
{
|
||||
CHECK_TRUE(etl::is_function<int(int)>::value);
|
||||
CHECK_TRUE(!etl::is_function<int>::value);
|
||||
CHECK_TRUE(!etl::is_function<int*>::value);
|
||||
CHECK_TRUE(!etl::is_function<int&>::value);
|
||||
CHECK_TRUE(!etl::is_function<int&&>::value);
|
||||
CHECK_TRUE(!etl::logical_not_t<etl::true_type>::value);
|
||||
CHECK_TRUE(etl::logical_not_t<etl::false_type>::value);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_type_traits_member_pointers)
|
||||
{
|
||||
using MemObjPtr = int TestClass::*;
|
||||
using MemFnPtr = int (TestClass::*)();
|
||||
|
||||
CHECK_TRUE(etl::is_member_pointer<MemObjPtr>::value);
|
||||
CHECK_TRUE(etl::is_member_pointer<MemFnPtr>::value);
|
||||
CHECK_TRUE(!etl::is_member_pointer<int*>::value);
|
||||
|
||||
CHECK_TRUE(etl::is_member_object_pointer<MemObjPtr>::value);
|
||||
CHECK_TRUE(!etl::is_member_object_pointer<MemFnPtr>::value);
|
||||
|
||||
CHECK_TRUE(!etl::is_member_function_pointer<MemObjPtr>::value);
|
||||
CHECK_TRUE(etl::is_member_function_pointer<MemFnPtr>::value);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_invoke_callable)
|
||||
{
|
||||
CHECK_EQUAL(30, etl::invoke(standalone_func, 10, 20));
|
||||
|
||||
auto lambda = [](int x)
|
||||
{ return x * 3; };
|
||||
CHECK_EQUAL(15, etl::invoke(lambda, 5));
|
||||
|
||||
Functor f;
|
||||
CHECK_EQUAL(60, etl::invoke(f, 12));
|
||||
|
||||
CHECK_EQUAL(8, etl::invoke(TestClass::static_func, 4));
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_invoke_mem_func_ptr)
|
||||
{
|
||||
TestClass obj;
|
||||
TestClass* ptr = &obj;
|
||||
const TestClass const_obj;
|
||||
|
||||
CHECK_EQUAL(42, etl::invoke(&TestClass::get_int, obj));
|
||||
CHECK_EQUAL(42, etl::invoke(&TestClass::get_int, ptr));
|
||||
CHECK_EQUAL(52, etl::invoke(&TestClass::const_get_int, const_obj));
|
||||
|
||||
etl::invoke(&TestClass::set_int, obj, 99);
|
||||
CHECK_EQUAL(99, obj.member_obj);
|
||||
|
||||
etl::invoke(&TestClass::set_int, ptr, 101);
|
||||
CHECK_EQUAL(101, ptr->member_obj);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_invoke_mem_obj_ptr)
|
||||
{
|
||||
TestClass obj;
|
||||
TestClass* ptr = &obj;
|
||||
|
||||
CHECK_EQUAL(42, etl::invoke(&TestClass::member_obj, obj));
|
||||
CHECK_EQUAL(42, etl::invoke(&TestClass::member_obj, ptr));
|
||||
CHECK_EQUAL("hello", etl::invoke(&TestClass::member_str, obj));
|
||||
|
||||
etl::invoke(&TestClass::member_obj, obj) = 1000;
|
||||
CHECK_EQUAL(1000, obj.member_obj);
|
||||
|
||||
etl::invoke(&TestClass::member_obj, ptr) = 2000;
|
||||
CHECK_EQUAL(2000, ptr->member_obj);
|
||||
}
|
||||
|
||||
//*************************************************************************
|
||||
TEST(test_invoke_result_t)
|
||||
{
|
||||
using MemFnPtr = decltype(&TestClass::get_int);
|
||||
using ConstMemFnPtr = decltype(&TestClass::const_get_int);
|
||||
using MemObjPtr_int = decltype(&TestClass::member_obj);
|
||||
using FnPtr = int (*)(int, int);
|
||||
|
||||
auto val = etl::is_same<etl::invoke_result_t<FnPtr, int, int>, int>::value;
|
||||
CHECK_TRUE(val);
|
||||
|
||||
val = etl::is_same<etl::invoke_result_t<MemFnPtr, TestClass&>, int>::value;
|
||||
CHECK_TRUE(val);
|
||||
|
||||
val = etl::is_same<etl::invoke_result_t<MemFnPtr, TestClass*>, int>::value;
|
||||
CHECK_TRUE(val);
|
||||
|
||||
val = etl::is_same<etl::invoke_result_t<ConstMemFnPtr, const TestClass&>, int>::value;
|
||||
CHECK_TRUE(val);
|
||||
|
||||
val = etl::is_same<etl::invoke_result_t<MemObjPtr_int, TestClass&>, int&>::value;
|
||||
CHECK_TRUE(val);
|
||||
|
||||
val = etl::is_same<etl::invoke_result_t<MemObjPtr_int, const TestClass&>, const int&>::value;
|
||||
CHECK_TRUE(val);
|
||||
|
||||
val = etl::is_same<etl::invoke_result_t<MemObjPtr_int, TestClass*>, int&>::value;
|
||||
CHECK_TRUE(val);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user