util/func.hpp

95 lines
2.8 KiB
C++

#pragma once
#include "type.hpp"
#include "util.hpp"
namespace util {
// <functional>
// clang-format off
namespace detail {
template <typename> constexpr bool is_reference_wrapper_v = false;
template <typename U> constexpr bool is_reference_wrapper_v<reference_wrapper<U>> = true;
template <typename T, typename Type, typename T1, typename... Args>
constexpr decltype(auto) INVOKE(Type T::*f, T1 &&t1, Args &&... args)
{
if constexpr (util::is_member_function_pointer_v<decltype(f)>) {
if constexpr (util::is_base_of_v<T, util::decay_t<T1>>)
return (util::forward<T1>(t1).*f)(util::forward<Args>(args)...);
else if constexpr (is_reference_wrapper_v<util::decay_t<T1>>)
return (t1.get().*f)(util::forward<Args>(args)...);
else
return ((*util::forward<T1>(t1)).*f)(util::forward<Args>(args)...);
} else {
static_assert(util::is_member_object_pointer_v<decltype(f)>);
static_assert(sizeof...(args) == 0);
if constexpr (util::is_base_of_v<T, util::decay_t<T1>>)
return util::forward<T1>(t1).*f;
else if constexpr (is_reference_wrapper_v<util::decay_t<T1>>)
return t1.get().*f;
else
return (*util::forward<T1>(t1)).*f;
}
}
template <typename Fn, typename... Args>
constexpr decltype(auto) INVOKE(Fn &&f, Args &&... args)
{
return util::forward<Fn>(f)(util::forward<Args>(args)...);
}
template <typename T> constexpr T &FUN(T &t) noexcept { return t; }
template <typename T> void FUN(T &&) = delete;
} // namespace detail
template <typename Fn, typename... Args>
constexpr util::invoke_result_t<Fn, Args...> invoke(Fn &&f, Args &&... args)
noexcept(util::is_nothrow_invocable_v<Fn, Args...>)
{
return detail::INVOKE(util::forward<Fn>(f), util::forward<Args>(args)...);
}
template <typename T>
class reference_wrapper {
public:
// types
using type = T;
// construct/copy/destroy
template <typename U,
typename = decltype(detail::FUN<T>(util::declval<U>()),
util::enable_if_t<!util::is_same_v<reference_wrapper, util::remove_cvref_t<U>>>())>
constexpr reference_wrapper(U &&u) noexcept(noexcept(detail::FUN<T>(util::forward<U>(u))))
: _ptr(util::addressof(detail::FUN<T>(util::forward<U>(u))))
{}
reference_wrapper(const reference_wrapper &) noexcept = default;
// assignment
reference_wrapper &operator=(const reference_wrapper &) noexcept = default;
// access
constexpr operator T &() const noexcept { return *_ptr; }
constexpr T &get() const noexcept { return *_ptr; }
template <typename... ArgTypes>
constexpr util::invoke_result_t<T &, ArgTypes...> operator()(ArgTypes &&... args) const
{
return invoke(get(), util::forward<ArgTypes>(args)...);
}
private:
T *_ptr;
};
// deduction guides
template <typename T> reference_wrapper(T &) -> reference_wrapper<T>;
// clang-format on
} // namespace util