Replace std::enable_if with requires

Introduce some concepts for enum and flags handling, goodbye and
thanks for all the fish std::enable_if.
This commit is contained in:
Maxime Coste 2020-11-11 21:43:27 +11:00
parent 04f11c2af3
commit fb4cef5b61
10 changed files with 61 additions and 72 deletions

View File

@ -30,8 +30,8 @@ public:
template<size_t N> template<size_t N>
constexpr ArrayView(T(&array)[N]) : m_pointer(array), m_size(N) {} constexpr ArrayView(T(&array)[N]) : m_pointer(array), m_size(N) {}
template<typename Container, template<typename Container>
typename = std::enable_if_t<sizeof(decltype(*std::declval<Container>().data())) == sizeof(T)>> requires (sizeof(decltype(*std::declval<Container>().data())) == sizeof(T))
constexpr ArrayView(Container&& c) constexpr ArrayView(Container&& c)
: m_pointer(c.data()), m_size(c.size()) {} : m_pointer(c.data()), m_size(c.size()) {}

View File

@ -70,9 +70,8 @@ template<> struct PerArgumentCommandCompleter<>
template<typename Completer, typename... Rest> template<typename Completer, typename... Rest>
struct PerArgumentCommandCompleter<Completer, Rest...> : PerArgumentCommandCompleter<Rest...> struct PerArgumentCommandCompleter<Completer, Rest...> : PerArgumentCommandCompleter<Rest...>
{ {
template<typename C, typename... R, template<typename C, typename... R>
typename = std::enable_if_t<not std::is_base_of<PerArgumentCommandCompleter<>, requires (not std::is_base_of_v<PerArgumentCommandCompleter<>, std::remove_reference_t<C>>)
std::remove_reference_t<C>>::value>>
PerArgumentCommandCompleter(C&& completer, R&&... rest) PerArgumentCommandCompleter(C&& completer, R&&... rest)
: PerArgumentCommandCompleter<Rest...>(std::forward<R>(rest)...), : PerArgumentCommandCompleter<Rest...>(std::forward<R>(rest)...),
m_completer(std::forward<C>(completer)) {} m_completer(std::forward<C>(completer)) {}

View File

@ -2,12 +2,16 @@
#define enum_hh_INCLUDED #define enum_hh_INCLUDED
#include "string.hh" #include "string.hh"
#include "meta.hh"
namespace Kakoune namespace Kakoune
{ {
template<typename T> struct EnumDesc { T value; StringView name; }; template<typename T> struct EnumDesc { T value; StringView name; };
template<typename T>
concept DescribedEnum = requires { enum_desc(Meta::Type<T>{}); };
} }
#endif // enum_hh_INCLUDED #endif // enum_hh_INCLUDED

View File

@ -11,22 +11,19 @@ namespace Kakoune
template<typename Flags> template<typename Flags>
constexpr bool with_bit_ops(Meta::Type<Flags>) { return false; } constexpr bool with_bit_ops(Meta::Type<Flags>) { return false; }
template<typename Flags>
concept WithBitOps = with_bit_ops(Meta::Type<Flags>{});
template<typename Flags> template<typename Flags>
using UnderlyingType = std::underlying_type_t<Flags>; using UnderlyingType = std::underlying_type_t<Flags>;
template<typename Flags, typename T = void> template<WithBitOps Flags>
using EnableIfWithBitOps = std::enable_if_t<with_bit_ops(Meta::Type<Flags>{}), T>;
template<typename Flags, typename T = void>
using EnableIfWithoutBitOps = std::enable_if_t<not with_bit_ops(Meta::Type<Flags>{}), T>;
template<typename Flags, typename = EnableIfWithBitOps<Flags>>
constexpr Flags operator|(Flags lhs, Flags rhs) constexpr Flags operator|(Flags lhs, Flags rhs)
{ {
return (Flags)((UnderlyingType<Flags>) lhs | (UnderlyingType<Flags>) rhs); return (Flags)((UnderlyingType<Flags>) lhs | (UnderlyingType<Flags>) rhs);
} }
template<typename Flags, typename = EnableIfWithBitOps<Flags>> template<WithBitOps Flags>
constexpr Flags& operator|=(Flags& lhs, Flags rhs) constexpr Flags& operator|=(Flags& lhs, Flags rhs)
{ {
(UnderlyingType<Flags>&) lhs |= (UnderlyingType<Flags>) rhs; (UnderlyingType<Flags>&) lhs |= (UnderlyingType<Flags>) rhs;
@ -45,32 +42,32 @@ struct TestableFlags
constexpr bool operator!=(const TestableFlags<Flags>& other) const { return value != other.value; } constexpr bool operator!=(const TestableFlags<Flags>& other) const { return value != other.value; }
}; };
template<typename Flags, typename = EnableIfWithBitOps<Flags>> template<WithBitOps Flags>
constexpr TestableFlags<Flags> operator&(Flags lhs, Flags rhs) constexpr TestableFlags<Flags> operator&(Flags lhs, Flags rhs)
{ {
return { (Flags)((UnderlyingType<Flags>) lhs & (UnderlyingType<Flags>) rhs) }; return { (Flags)((UnderlyingType<Flags>) lhs & (UnderlyingType<Flags>) rhs) };
} }
template<typename Flags, typename = EnableIfWithBitOps<Flags>> template<WithBitOps Flags>
constexpr Flags& operator&=(Flags& lhs, Flags rhs) constexpr Flags& operator&=(Flags& lhs, Flags rhs)
{ {
(UnderlyingType<Flags>&) lhs &= (UnderlyingType<Flags>) rhs; (UnderlyingType<Flags>&) lhs &= (UnderlyingType<Flags>) rhs;
return lhs; return lhs;
} }
template<typename Flags, typename = EnableIfWithBitOps<Flags>> template<WithBitOps Flags>
constexpr Flags operator~(Flags lhs) constexpr Flags operator~(Flags lhs)
{ {
return (Flags)(~(UnderlyingType<Flags>)lhs); return (Flags)(~(UnderlyingType<Flags>)lhs);
} }
template<typename Flags, typename = EnableIfWithBitOps<Flags>> template<WithBitOps Flags>
constexpr Flags operator^(Flags lhs, Flags rhs) constexpr Flags operator^(Flags lhs, Flags rhs)
{ {
return (Flags)((UnderlyingType<Flags>) lhs ^ (UnderlyingType<Flags>) rhs); return (Flags)((UnderlyingType<Flags>) lhs ^ (UnderlyingType<Flags>) rhs);
} }
template<typename Flags, typename = EnableIfWithBitOps<Flags>> template<WithBitOps Flags>
constexpr Flags& operator^=(Flags& lhs, Flags rhs) constexpr Flags& operator^=(Flags& lhs, Flags rhs)
{ {
(UnderlyingType<Flags>&) lhs ^= (UnderlyingType<Flags>) rhs; (UnderlyingType<Flags>&) lhs ^= (UnderlyingType<Flags>) rhs;

View File

@ -11,16 +11,14 @@ namespace Kakoune
size_t hash_data(const char* data, size_t len); size_t hash_data(const char* data, size_t len);
template<typename Type> template<typename Type> requires std::is_integral_v<Type>
std::enable_if_t<std::is_integral<Type>::value, size_t> constexpr size_t hash_value(const Type& val)
constexpr hash_value(const Type& val)
{ {
return (size_t)val; return (size_t)val;
} }
template<typename Type> template<typename Type> requires std::is_enum_v<Type>
std::enable_if_t<std::is_enum<Type>::value, size_t> constexpr size_t hash_value(const Type& val)
constexpr hash_value(const Type& val)
{ {
return hash_value((std::underlying_type_t<Type>)val); return hash_value((std::underlying_type_t<Type>)val);
} }

View File

@ -182,12 +182,7 @@ struct HashMap
return m_items.back().value; return m_items.back().value;
} }
template<typename KeyType> template<typename KeyType> requires IsHashCompatible<Key, KeyType>
using EnableIfHashCompatible = std::enable_if_t<
IsHashCompatible<Key, std::decay_t<KeyType>>
>;
template<typename KeyType, typename = EnableIfHashCompatible<KeyType>>
constexpr int find_index(const KeyType& key, size_t hash) const constexpr int find_index(const KeyType& key, size_t hash) const
{ {
for (auto slot = m_index.compute_slot(hash); slot < m_index.size(); ++slot) for (auto slot = m_index.compute_slot(hash); slot < m_index.size(); ++slot)
@ -201,13 +196,13 @@ struct HashMap
return -1; return -1;
} }
template<typename KeyType, typename = EnableIfHashCompatible<KeyType>> template<typename KeyType> requires IsHashCompatible<Key, KeyType>
constexpr int find_index(const KeyType& key) const { return find_index(key, hash_value(key)); } constexpr int find_index(const KeyType& key) const { return find_index(key, hash_value(key)); }
template<typename KeyType, typename = EnableIfHashCompatible<KeyType>> template<typename KeyType> requires IsHashCompatible<Key, KeyType>
constexpr bool contains(const KeyType& key) const { return find_index(key) >= 0; } constexpr bool contains(const KeyType& key) const { return find_index(key) >= 0; }
template<typename KeyType, typename = EnableIfHashCompatible<KeyType>> template<typename KeyType> requires IsHashCompatible<Key, std::remove_cvref_t<KeyType>>
constexpr Value& operator[](KeyType&& key) constexpr Value& operator[](KeyType&& key)
{ {
const auto hash = hash_value(key); const auto hash = hash_value(key);
@ -221,7 +216,7 @@ struct HashMap
return m_items.back().value; return m_items.back().value;
} }
template<typename KeyType, typename = EnableIfHashCompatible<KeyType>> template<typename KeyType> requires IsHashCompatible<Key, KeyType>
constexpr void remove(const KeyType& key) constexpr void remove(const KeyType& key)
{ {
const auto hash = hash_value(key); const auto hash = hash_value(key);
@ -234,7 +229,7 @@ struct HashMap
} }
} }
template<typename KeyType, typename = EnableIfHashCompatible<KeyType>> template<typename KeyType> requires IsHashCompatible<Key, KeyType>
constexpr void unordered_remove(const KeyType& key) constexpr void unordered_remove(const KeyType& key)
{ {
const auto hash = hash_value(key); const auto hash = hash_value(key);
@ -249,10 +244,10 @@ struct HashMap
} }
} }
template<typename KeyType, typename = EnableIfHashCompatible<KeyType>> template<typename KeyType> requires IsHashCompatible<Key, KeyType>
constexpr void erase(const KeyType& key) { unordered_remove(key); } constexpr void erase(const KeyType& key) { unordered_remove(key); }
template<typename KeyType, typename = EnableIfHashCompatible<KeyType>> template<typename KeyType> requires IsHashCompatible<Key, KeyType>
constexpr void remove_all(const KeyType& key) constexpr void remove_all(const KeyType& key)
{ {
const auto hash = hash_value(key); const auto hash = hash_value(key);
@ -275,14 +270,14 @@ struct HashMap
const Item& item(size_t index) const { return m_items[index]; } const Item& item(size_t index) const { return m_items[index]; }
template<typename KeyType, typename = EnableIfHashCompatible<KeyType>> template<typename KeyType> requires IsHashCompatible<Key, KeyType>
constexpr iterator find(const KeyType& key) constexpr iterator find(const KeyType& key)
{ {
auto index = find_index(key); auto index = find_index(key);
return index >= 0 ? begin() + index : end(); return index >= 0 ? begin() + index : end();
} }
template<typename KeyType, typename = EnableIfHashCompatible<KeyType>> template<typename KeyType> requires IsHashCompatible<Key, KeyType>
constexpr const_iterator find(const KeyType& key) const constexpr const_iterator find(const KeyType& key) const
{ {
return const_cast<HashMap*>(this)->find(key); return const_cast<HashMap*>(this)->find(key);

View File

@ -19,8 +19,8 @@ namespace Kakoune
{ {
template<typename T> template<typename T>
std::enable_if_t<std::is_same<decltype(option_to_string(std::declval<T>())), String>::value, String> String option_to_string(const T& value, Quoting)
option_to_string(const T& value, Quoting) requires std::is_same_v<decltype(option_to_string(std::declval<T>())), String>
{ {
return option_to_string(value); return option_to_string(value);
} }
@ -31,9 +31,8 @@ constexpr decltype(T::option_type_name) option_type_name(Meta::Type<T>)
return T::option_type_name; return T::option_type_name;
} }
template<typename Enum> template<typename Enum> requires std::is_enum_v<Enum>
std::enable_if_t<std::is_enum<Enum>::value, String> String option_type_name(Meta::Type<Enum>)
option_type_name(Meta::Type<Enum>)
{ {
return format("{}({})", with_bit_ops(Meta::Type<Enum>{}) ? "flags" : "enum", return format("{}({})", with_bit_ops(Meta::Type<Enum>{}) ? "flags" : "enum",
join(enum_desc(Meta::Type<Enum>{}) | join(enum_desc(Meta::Type<Enum>{}) |
@ -256,8 +255,8 @@ inline String option_to_string(const StronglyTypedNumber<RealType, ValueType>& o
} }
template<typename Number> template<typename Number>
std::enable_if_t<std::is_base_of<StronglyTypedNumber<Number, int>, Number>::value, Number> requires std::is_base_of_v<StronglyTypedNumber<Number, int>, Number>
option_from_string(Meta::Type<Number>, StringView str) Number option_from_string(Meta::Type<Number>, StringView str)
{ {
return Number{str_to_int(str)}; return Number{str_to_int(str)};
} }
@ -290,8 +289,8 @@ inline void option_update(WorstMatch, const Context&)
} }
template<typename Coord> template<typename Coord>
std::enable_if_t<std::is_base_of<LineAndColumn<Coord, decltype(Coord::line), decltype(Coord::column)>, Coord>::value, Coord> requires std::is_base_of_v<LineAndColumn<Coord, decltype(Coord::line), decltype(Coord::column)>, Coord>
option_from_string(Meta::Type<Coord>, StringView str) Coord option_from_string(Meta::Type<Coord>, StringView str)
{ {
struct error : runtime_error { error(size_t) : runtime_error{"expected <line>,<column>"} {} }; struct error : runtime_error { error(size_t) : runtime_error{"expected <line>,<column>"} {} };
auto vals = str | split<StringView>(',') auto vals = str | split<StringView>(',')
@ -305,8 +304,8 @@ inline String option_to_string(const LineAndColumn<EffectiveType, LineType, Colu
return format("{},{}", opt.line, opt.column); return format("{},{}", opt.line, opt.column);
} }
template<typename Flags, typename = decltype(enum_desc(Meta::Type<Flags>{}))> template<DescribedEnum Flags> requires WithBitOps<Flags>
EnableIfWithBitOps<Flags, String> option_to_string(Flags flags) String option_to_string(Flags flags)
{ {
constexpr auto desc = enum_desc(Meta::Type<Flags>{}); constexpr auto desc = enum_desc(Meta::Type<Flags>{});
String res; String res;
@ -321,8 +320,8 @@ EnableIfWithBitOps<Flags, String> option_to_string(Flags flags)
return res; return res;
} }
template<typename Enum, typename = decltype(enum_desc(Meta::Type<Enum>{}))> template<DescribedEnum Enum> requires (not WithBitOps<Enum>)
EnableIfWithoutBitOps<Enum, String> option_to_string(Enum e) String option_to_string(Enum e)
{ {
constexpr auto desc = enum_desc(Meta::Type<Enum>{}); constexpr auto desc = enum_desc(Meta::Type<Enum>{});
auto it = find_if(desc, [e](const EnumDesc<Enum>& d) { return d.value == e; }); auto it = find_if(desc, [e](const EnumDesc<Enum>& d) { return d.value == e; });
@ -332,8 +331,8 @@ EnableIfWithoutBitOps<Enum, String> option_to_string(Enum e)
return {}; return {};
} }
template<typename Flags, typename = decltype(enum_desc(Meta::Type<Flags>{}))> template<DescribedEnum Flags> requires WithBitOps<Flags>
EnableIfWithBitOps<Flags, Flags> option_from_string(Meta::Type<Flags>, StringView str) Flags option_from_string(Meta::Type<Flags>, StringView str)
{ {
constexpr auto desc = enum_desc(Meta::Type<Flags>{}); constexpr auto desc = enum_desc(Meta::Type<Flags>{});
Flags flags{}; Flags flags{};
@ -347,8 +346,8 @@ EnableIfWithBitOps<Flags, Flags> option_from_string(Meta::Type<Flags>, StringVie
return flags; return flags;
} }
template<typename Enum, typename = decltype(enum_desc(Meta::Type<Enum>{}))> template<DescribedEnum Enum> requires (not WithBitOps<Enum>)
EnableIfWithoutBitOps<Enum, Enum> option_from_string(Meta::Type<Enum>, StringView str) Enum option_from_string(Meta::Type<Enum>, StringView str)
{ {
constexpr auto desc = enum_desc(Meta::Type<Enum>{}); constexpr auto desc = enum_desc(Meta::Type<Enum>{});
auto it = find_if(desc, [str](const EnumDesc<Enum>& d) { return d.name == str; }); auto it = find_if(desc, [str](const EnumDesc<Enum>& d) { return d.name == str; });
@ -357,16 +356,16 @@ EnableIfWithoutBitOps<Enum, Enum> option_from_string(Meta::Type<Enum>, StringVie
return it->value; return it->value;
} }
template<typename Flags, typename = decltype(enum_desc(Meta::Type<Flags>{}))> template<DescribedEnum Flags> requires WithBitOps<Flags>
EnableIfWithBitOps<Flags, bool> option_add(Flags& opt, StringView str) bool option_add(Flags& opt, StringView str)
{ {
const Flags old = opt; const Flags old = opt;
opt |= option_from_string(Meta::Type<Flags>{}, str); opt |= option_from_string(Meta::Type<Flags>{}, str);
return opt != old; return opt != old;
} }
template<typename Flags, typename = decltype(enum_desc(Meta::Type<Flags>{}))> template<DescribedEnum Flags> requires WithBitOps<Flags>
EnableIfWithBitOps<Flags, bool> option_remove(Flags& opt, StringView str) bool option_remove(Flags& opt, StringView str)
{ {
const Flags old = opt; const Flags old = opt;
opt &= ~option_from_string(Meta::Type<Flags>{}, str); opt &= ~option_from_string(Meta::Type<Flags>{}, str);

View File

@ -136,14 +136,12 @@ decltype(auto) to_string(const StronglyTypedNumber<RealType, ValueType>& val)
namespace detail namespace detail
{ {
template<typename T> constexpr bool is_string = std::is_convertible<T, StringView>::value; template<typename T> requires std::is_convertible_v<T, StringView>
template<typename T, class = std::enable_if_t<not is_string<T>>>
decltype(auto) format_param(const T& val) { return to_string(val); }
template<typename T, class = std::enable_if_t<is_string<T>>>
StringView format_param(const T& val) { return val; } StringView format_param(const T& val) { return val; }
template<typename T> requires (not std::is_convertible_v<T, StringView>)
decltype(auto) format_param(const T& val) { return to_string(val); }
} }
String format(StringView fmt, ArrayView<const StringView> params); String format(StringView fmt, ArrayView<const StringView> params);

View File

@ -109,12 +109,12 @@ public:
bool operator>= (const iterator& other) const noexcept { return m_it >= other.m_it; } bool operator>= (const iterator& other) const noexcept { return m_it >= other.m_it; }
template<typename T> template<typename T>
std::enable_if_t<std::is_same<T, BaseIt>::value or std::is_same<T, Sentinel>::value, bool> requires std::is_same_v<T, BaseIt> or std::is_same_v<T, Sentinel>
operator==(const T& other) const noexcept { return m_it == other; } bool operator==(const T& other) const noexcept { return m_it == other; }
template<typename T> template<typename T>
std::enable_if_t<std::is_same<T, BaseIt>::value or std::is_same<T, Sentinel>::value, bool> requires std::is_same_v<T, BaseIt> or std::is_same_v<T, Sentinel>
operator!=(const T& other) const noexcept { return m_it != other; } bool operator!=(const T& other) const noexcept { return m_it != other; }
bool operator< (const BaseIt& other) const noexcept { return m_it < other; } bool operator< (const BaseIt& other) const noexcept { return m_it < other; }
bool operator<= (const BaseIt& other) const noexcept { return m_it <= other; } bool operator<= (const BaseIt& other) const noexcept { return m_it <= other; }

View File

@ -16,8 +16,7 @@ struct Value
{ {
Value() = default; Value() = default;
template<typename T, template<typename T> requires (not std::is_same_v<Value, T>)
typename = std::enable_if_t<not std::is_same<Value, T>::value>>
Value(T&& val) Value(T&& val)
: m_value{new Model<std::decay_t<T>>{std::forward<T>(val)}} {} : m_value{new Model<std::decay_t<T>>{std::forward<T>(val)}} {}