serenity/AK/StdLibExtraDetails.h
Ali Mohammad Pur 51c2c69357 AK+Everywhere: Disallow constructing Functions from incompatible types
Previously, AK::Function would accept _any_ callable type, and try to
call it when called, first with the given set of arguments, then with
zero arguments, and if all of those failed, it would simply not call the
function and **return a value-constructed Out type**.
This lead to many, many, many hard to debug situations when someone
forgot a `const` in their lambda argument types, and many cases of
people taking zero arguments in their lambdas to ignore them.
This commit reworks the Function interface to not include any such
surprising behaviour, if your function instance is not callable with
the declared argument set of the Function, it can simply not be
assigned to that Function instance, end of story.
2021-06-06 00:27:30 +04:30

487 lines
12 KiB
C++

/*
* Copyright (c) 2018-2021, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2021, Ali Mohammad Pur <mpfard@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
namespace AK::Detail {
template<bool B, class T = void>
struct EnableIf {
};
template<class T>
struct EnableIf<true, T> {
using Type = T;
};
template<class T, T v>
struct IntegralConstant {
static constexpr T value = v;
using ValueType = T;
using Type = IntegralConstant;
constexpr operator ValueType() const { return value; }
constexpr ValueType operator()() const { return value; }
};
using FalseType = IntegralConstant<bool, false>;
using TrueType = IntegralConstant<bool, true>;
template<class T>
using AddConst = const T;
template<class T>
struct __RemoveConst {
using Type = T;
};
template<class T>
struct __RemoveConst<const T> {
using Type = T;
};
template<class T>
using RemoveConst = typename __RemoveConst<T>::Type;
template<class T>
struct __RemoveVolatile {
using Type = T;
};
template<class T>
struct __RemoveVolatile<volatile T> {
using Type = T;
};
template<typename T>
using RemoveVolatile = typename __RemoveVolatile<T>::Type;
template<class T>
using RemoveCV = RemoveVolatile<RemoveConst<T>>;
template<typename...>
using VoidType = void;
template<class T>
inline constexpr bool IsLvalueReference = false;
template<class T>
inline constexpr bool IsLvalueReference<T&> = true;
template<class T>
inline constexpr bool __IsPointerHelper = false;
template<class T>
inline constexpr bool __IsPointerHelper<T*> = true;
template<class T>
inline constexpr bool IsPointer = __IsPointerHelper<RemoveCV<T>>;
template<class>
inline constexpr bool IsFunction = false;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args...)> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args..., ...)> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args...) const> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args..., ...) const> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args...) volatile> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args..., ...) volatile> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args...) const volatile> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args..., ...) const volatile> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args...)&> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args..., ...)&> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args...) const&> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args..., ...) const&> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args...) volatile&> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args..., ...) volatile&> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args...) const volatile&> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args..., ...) const volatile&> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args...) &&> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args..., ...) &&> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args...) const&&> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args..., ...) const&&> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args...) volatile&&> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args..., ...) volatile&&> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args...) const volatile&&> = true;
template<class Ret, class... Args>
inline constexpr bool IsFunction<Ret(Args..., ...) const volatile&&> = true;
template<class T>
inline constexpr bool IsRvalueReference = false;
template<class T>
inline constexpr bool IsRvalueReference<T&&> = true;
template<class T>
struct __RemovePointer {
using Type = T;
};
template<class T>
struct __RemovePointer<T*> {
using Type = T;
};
template<class T>
struct __RemovePointer<T* const> {
using Type = T;
};
template<class T>
struct __RemovePointer<T* volatile> {
using Type = T;
};
template<class T>
struct __RemovePointer<T* const volatile> {
using Type = T;
};
template<typename T>
using RemovePointer = typename __RemovePointer<T>::Type;
template<typename T, typename U>
inline constexpr bool IsSame = false;
template<typename T>
inline constexpr bool IsSame<T, T> = true;
template<bool condition, class TrueType, class FalseType>
struct __Conditional {
using Type = TrueType;
};
template<class TrueType, class FalseType>
struct __Conditional<false, TrueType, FalseType> {
using Type = FalseType;
};
template<bool condition, class TrueType, class FalseType>
using Conditional = typename __Conditional<condition, TrueType, FalseType>::Type;
template<typename T>
inline constexpr bool IsNullPointer = IsSame<decltype(nullptr), RemoveCV<T>>;
template<typename T>
struct __RemoveReference {
using Type = T;
};
template<class T>
struct __RemoveReference<T&> {
using Type = T;
};
template<class T>
struct __RemoveReference<T&&> {
using Type = T;
};
template<typename T>
using RemoveReference = typename __RemoveReference<T>::Type;
template<typename T>
struct __MakeUnsigned {
using Type = void;
};
template<>
struct __MakeUnsigned<signed char> {
using Type = unsigned char;
};
template<>
struct __MakeUnsigned<short> {
using Type = unsigned short;
};
template<>
struct __MakeUnsigned<int> {
using Type = unsigned int;
};
template<>
struct __MakeUnsigned<long> {
using Type = unsigned long;
};
template<>
struct __MakeUnsigned<long long> {
using Type = unsigned long long;
};
template<>
struct __MakeUnsigned<unsigned char> {
using Type = unsigned char;
};
template<>
struct __MakeUnsigned<unsigned short> {
using Type = unsigned short;
};
template<>
struct __MakeUnsigned<unsigned int> {
using Type = unsigned int;
};
template<>
struct __MakeUnsigned<unsigned long> {
using Type = unsigned long;
};
template<>
struct __MakeUnsigned<unsigned long long> {
using Type = unsigned long long;
};
template<>
struct __MakeUnsigned<char> {
using Type = unsigned char;
};
template<>
struct __MakeUnsigned<char8_t> {
using Type = char8_t;
};
template<>
struct __MakeUnsigned<char16_t> {
using Type = char16_t;
};
template<>
struct __MakeUnsigned<char32_t> {
using Type = char32_t;
};
template<>
struct __MakeUnsigned<bool> {
using Type = bool;
};
template<typename T>
using MakeUnsigned = typename __MakeUnsigned<T>::Type;
template<typename T>
struct __MakeSigned {
using Type = void;
};
template<>
struct __MakeSigned<signed char> {
using Type = signed char;
};
template<>
struct __MakeSigned<short> {
using Type = short;
};
template<>
struct __MakeSigned<int> {
using Type = int;
};
template<>
struct __MakeSigned<long> {
using Type = long;
};
template<>
struct __MakeSigned<long long> {
using Type = long long;
};
template<>
struct __MakeSigned<unsigned char> {
using Type = char;
};
template<>
struct __MakeSigned<unsigned short> {
using Type = short;
};
template<>
struct __MakeSigned<unsigned int> {
using Type = int;
};
template<>
struct __MakeSigned<unsigned long> {
using Type = long;
};
template<>
struct __MakeSigned<unsigned long long> {
using Type = long long;
};
template<>
struct __MakeSigned<char> {
using Type = char;
};
template<typename T>
using MakeSigned = typename __MakeSigned<T>::Type;
template<class T>
inline constexpr bool IsVoid = IsSame<void, RemoveCV<T>>;
template<class T>
inline constexpr bool IsConst = false;
template<class T>
inline constexpr bool IsConst<const T> = true;
template<typename T>
inline constexpr bool IsEnum = __is_enum(T);
template<typename T>
inline constexpr bool IsUnion = __is_union(T);
template<typename T>
inline constexpr bool IsClass = __is_class(T);
template<typename Base, typename Derived>
inline constexpr bool IsBaseOf = __is_base_of(Base, Derived);
template<typename T>
inline constexpr bool __IsIntegral = false;
template<>
inline constexpr bool __IsIntegral<bool> = true;
template<>
inline constexpr bool __IsIntegral<unsigned char> = true;
template<>
inline constexpr bool __IsIntegral<char8_t> = true;
template<>
inline constexpr bool __IsIntegral<char16_t> = true;
template<>
inline constexpr bool __IsIntegral<char32_t> = true;
template<>
inline constexpr bool __IsIntegral<unsigned short> = true;
template<>
inline constexpr bool __IsIntegral<unsigned int> = true;
template<>
inline constexpr bool __IsIntegral<unsigned long> = true;
template<>
inline constexpr bool __IsIntegral<unsigned long long> = true;
template<typename T>
inline constexpr bool IsIntegral = __IsIntegral<MakeUnsigned<RemoveCV<T>>>;
template<typename T>
inline constexpr bool __IsFloatingPoint = false;
template<>
inline constexpr bool __IsFloatingPoint<float> = true;
template<>
inline constexpr bool __IsFloatingPoint<double> = true;
template<>
inline constexpr bool __IsFloatingPoint<long double> = true;
template<typename T>
inline constexpr bool IsFloatingPoint = __IsFloatingPoint<RemoveCV<T>>;
template<typename ReferenceType, typename T>
using CopyConst = Conditional<IsConst<ReferenceType>, AddConst<T>, RemoveConst<T>>;
template<typename... Ts>
using Void = void;
template<typename... _Ignored>
constexpr auto DependentFalse = false;
template<typename T>
inline constexpr bool IsSigned = IsSame<T, MakeSigned<T>>;
template<typename T>
inline constexpr bool IsUnsigned = IsSame<T, MakeUnsigned<T>>;
template<typename T>
inline constexpr bool IsArithmetic = IsIntegral<T> || IsFloatingPoint<T>;
template<typename T>
inline constexpr bool IsFundamental = IsArithmetic<T> || IsVoid<T> || IsNullPointer<T>;
template<typename T, T... Ts>
struct IntegerSequence {
using Type = T;
static constexpr unsigned size() noexcept { return sizeof...(Ts); };
};
template<unsigned... Indices>
using IndexSequence = IntegerSequence<unsigned, Indices...>;
template<typename T, T N, T... Ts>
auto make_integer_sequence_impl()
{
if constexpr (N == 0)
return IntegerSequence<T, Ts...> {};
else
return make_integer_sequence_impl<T, N - 1, N - 1, Ts...>();
}
template<typename T, T N>
using MakeIntegerSequence = decltype(make_integer_sequence_impl<T, N>());
template<unsigned N>
using MakeIndexSequence = MakeIntegerSequence<unsigned, N>;
template<typename T>
struct __IdentityType {
using Type = T;
};
template<typename T>
using IdentityType = typename __IdentityType<T>::Type;
template<class T>
requires(IsEnum<T>) using UnderlyingType = __underlying_type(T);
template<typename T>
inline constexpr bool IsTrivial = __is_trivial(T);
template<typename T>
inline constexpr bool IsTriviallyCopyable = __is_trivially_copyable(T);
template<typename T>
auto declval() -> T;
template<typename T, typename... Args>
inline constexpr bool IsCallableWithArguments = requires(T t) { t(declval<Args>()...); };
}
using AK::Detail::AddConst;
using AK::Detail::Conditional;
using AK::Detail::CopyConst;
using AK::Detail::declval;
using AK::Detail::DependentFalse;
using AK::Detail::EnableIf;
using AK::Detail::FalseType;
using AK::Detail::IdentityType;
using AK::Detail::IndexSequence;
using AK::Detail::IntegerSequence;
using AK::Detail::IsArithmetic;
using AK::Detail::IsBaseOf;
using AK::Detail::IsCallableWithArguments;
using AK::Detail::IsClass;
using AK::Detail::IsConst;
using AK::Detail::IsEnum;
using AK::Detail::IsFloatingPoint;
using AK::Detail::IsFunction;
using AK::Detail::IsFundamental;
using AK::Detail::IsIntegral;
using AK::Detail::IsLvalueReference;
using AK::Detail::IsNullPointer;
using AK::Detail::IsPointer;
using AK::Detail::IsRvalueReference;
using AK::Detail::IsSame;
using AK::Detail::IsSigned;
using AK::Detail::IsTrivial;
using AK::Detail::IsTriviallyCopyable;
using AK::Detail::IsUnion;
using AK::Detail::IsUnsigned;
using AK::Detail::IsVoid;
using AK::Detail::MakeIndexSequence;
using AK::Detail::MakeIntegerSequence;
using AK::Detail::MakeSigned;
using AK::Detail::MakeUnsigned;
using AK::Detail::RemoveConst;
using AK::Detail::RemoveCV;
using AK::Detail::RemovePointer;
using AK::Detail::RemoveReference;
using AK::Detail::RemoveVolatile;
using AK::Detail::TrueType;
using AK::Detail::UnderlyingType;
using AK::Detail::Void;