246 lines
7.1 KiB
C++
246 lines
7.1 KiB
C++
#pragma once
|
|
|
|
#include <chrono>
|
|
#include <format>
|
|
#include <ranges>
|
|
|
|
namespace mcc::traits
|
|
{
|
|
|
|
|
|
template <std::ranges::range R>
|
|
static constexpr size_t mcc_range_size(const R& r)
|
|
{
|
|
if constexpr (std::ranges::sized_range<R>) {
|
|
return r.size();
|
|
} else {
|
|
return std::ranges::distance(r.begin(), r.end());
|
|
}
|
|
}
|
|
|
|
|
|
template <typename R>
|
|
concept mcc_char_view = std::ranges::view<R> && std::same_as<std::ranges::range_value_t<R>, char>;
|
|
|
|
|
|
// range of char/const char
|
|
template <typename R, typename CharT = char>
|
|
concept mcc_char_range = std::ranges::range<R> && std::same_as<std::remove_cv_t<std::ranges::range_value_t<R>>, CharT>;
|
|
|
|
|
|
// input range of char/const char
|
|
template <typename R, typename CharT = char>
|
|
concept mcc_input_char_range =
|
|
std::ranges::input_range<R> && std::is_same_v<std::remove_cv_t<std::ranges::range_value_t<R>>, CharT>;
|
|
|
|
|
|
// output range of char/const char
|
|
template <typename R, typename CharT = char>
|
|
concept mcc_output_char_range =
|
|
std::ranges::output_range<R, CharT> && std::same_as<std::remove_cv_t<std::ranges::range_value_t<R>>, CharT>;
|
|
|
|
|
|
template <typename R>
|
|
concept mcc_view_or_output_char_range = mcc_char_view<R> || mcc_output_char_range<R>;
|
|
|
|
|
|
template <typename R>
|
|
concept mcc_range_of_char_range = std::ranges::range<R> && mcc_char_range<std::ranges::range_value_t<R>>;
|
|
|
|
template <typename R>
|
|
concept mcc_range_of_input_char_range =
|
|
std::ranges::range<R> && traits::mcc_input_char_range<std::ranges::range_value_t<R>>;
|
|
|
|
|
|
template <typename R>
|
|
concept mcc_range_of_output_char_range =
|
|
std::ranges::range<R> && traits::mcc_output_char_range<std::ranges::range_value_t<R>>;
|
|
|
|
|
|
|
|
// https://stackoverflow.com/questions/72430369/how-to-check-that-a-type-is-formattable-using-type-traits-concepts)
|
|
template <typename T>
|
|
concept mcc_formattable =
|
|
requires(T v, std::format_context ctx) { std::formatter<std::remove_cvref_t<T>>().format(v, ctx); };
|
|
|
|
|
|
// from https://stackoverflow.com/questions/74383254/concept-that-models-only-the-stdchrono-duration-types
|
|
template <typename T>
|
|
concept mcc_time_duration_c = requires {
|
|
[]<class Rep, class Period>(std::type_identity<std::chrono::duration<Rep, Period>>) {
|
|
|
|
}(std::type_identity<std::remove_cvref_t<T>>());
|
|
};
|
|
|
|
|
|
template <typename T>
|
|
concept mcc_systime_c = requires {
|
|
[]<class DT>(std::type_identity<std::chrono::sys_time<DT>>) {}(std::type_identity<std::remove_cvref_t<T>>());
|
|
};
|
|
|
|
|
|
template <typename R>
|
|
concept mcc_output_duration_range_c =
|
|
std::ranges::output_range<R, std::ranges::range_value_t<R>> && mcc_time_duration_c<std::ranges::range_value_t<R>>;
|
|
// concept mcc_output_duration_range_c = std::ranges::range<R> && requires(R r) {
|
|
// []<class Rep, class Period>(std::type_identity<std::chrono::duration<Rep, Period>>) {
|
|
|
|
// }(std::ranges::range_value_t<R>());
|
|
|
|
// };
|
|
|
|
/* a callable concept and its signature traits */
|
|
|
|
template <typename T>
|
|
concept mcc_is_callable = std::is_function_v<T> || (std::is_object_v<T> && requires(T) { &T::operator(); });
|
|
|
|
template <typename T>
|
|
concept mcc_callable_c = std::is_function_v<T> || (std::is_object_v<T> && requires(T) { &T::operator(); });
|
|
|
|
|
|
// helper classes for callable signature deducing
|
|
template <typename... Ts>
|
|
struct mcc_func_traits_helper_t;
|
|
|
|
template <typename R>
|
|
struct mcc_func_traits_helper_t<R> {
|
|
using ret_t = R;
|
|
using args_t = std::tuple<>;
|
|
using arg1_t = void;
|
|
static constexpr size_t arity = 0;
|
|
};
|
|
|
|
template <typename R, typename Arg, typename... Args>
|
|
struct mcc_func_traits_helper_t<R, Arg, Args...> {
|
|
using ret_t = R;
|
|
using args_t = std::tuple<Arg, Args...>;
|
|
using arg1_t = Arg;
|
|
static constexpr size_t arity = sizeof...(Args) + 1;
|
|
};
|
|
|
|
template <typename F>
|
|
struct mcc_func_traits {
|
|
// use of an empty struct here to match std::invoke_result behaivior (at least of GCC)
|
|
};
|
|
|
|
template <typename R, typename... Args>
|
|
struct mcc_func_traits<R (*)(Args...)> : mcc_func_traits_helper_t<R, Args...> {
|
|
};
|
|
|
|
template <typename R, typename... Args>
|
|
struct mcc_func_traits<R(Args...)> : mcc_func_traits_helper_t<R, Args...> {
|
|
};
|
|
|
|
template <typename C, typename R, typename... Args>
|
|
struct mcc_func_traits<R (C::*)(Args...)> : mcc_func_traits_helper_t<R, Args...> {
|
|
};
|
|
|
|
template <typename C, typename R, typename... Args>
|
|
struct mcc_func_traits<R (C::*)(Args...) const> : mcc_func_traits_helper_t<R, Args...> {
|
|
};
|
|
|
|
template <typename F>
|
|
requires mcc_is_callable<F>
|
|
struct mcc_func_traits<F> : mcc_func_traits<decltype(&F::operator())> {
|
|
};
|
|
|
|
// reference/const ref and rvalue helpers
|
|
template <typename F>
|
|
struct mcc_func_traits<F&> : mcc_func_traits<F> {
|
|
};
|
|
|
|
template <typename F>
|
|
struct mcc_func_traits<const F&> : mcc_func_traits<F> {
|
|
};
|
|
|
|
template <typename F>
|
|
struct mcc_func_traits<F&&> : mcc_func_traits<F> {
|
|
};
|
|
|
|
// type of the returned value
|
|
template <typename T>
|
|
using mcc_retval_t = typename mcc_func_traits<T>::ret_t;
|
|
|
|
// type of the first argument of callable
|
|
template <typename T>
|
|
using mcc_func_arg1_t = typename mcc_func_traits<T>::arg1_t;
|
|
|
|
// type of the N-th argument of callable
|
|
// NOTE: starts from 1 not from 0!!!
|
|
template <typename T, size_t N = 1>
|
|
using mcc_func_argN_t = std::conditional_t<N >= mcc_func_traits<T>::arity,
|
|
std::tuple_element_t<N - 1, typename mcc_func_traits<T>::args_t>,
|
|
void>;
|
|
|
|
|
|
// non-const lvalue reference, constructible from CtorArgTs (an output argument of function)
|
|
template <typename T, typename... CtorArgTs>
|
|
concept mcc_output_arg_c = !std::is_const_v<std::remove_reference_t<T>> && std::is_lvalue_reference_v<T> &&
|
|
std::constructible_from<std::remove_reference_t<T>, CtorArgTs...>;
|
|
|
|
|
|
|
|
// std::tuple or std::pair
|
|
template <typename T>
|
|
concept mcc_tuple_c = requires {
|
|
requires requires {
|
|
[]<typename... Ts>(std::type_identity<std::tuple<Ts...>>) {}(std::type_identity<std::remove_cvref_t<T>>());
|
|
} || requires {
|
|
[]<typename T1, typename T2>(std::type_identity<std::pair<T1, T2>>) {
|
|
}(std::type_identity<std::remove_cvref_t<T>>());
|
|
};
|
|
};
|
|
|
|
|
|
template <typename T>
|
|
concept mcc_nonconst_lvref = std::is_lvalue_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>;
|
|
|
|
|
|
template <typename T>
|
|
concept mcc_error_c = std::convertible_to<T, bool> || requires(const T t) {
|
|
{ t.operator bool() };
|
|
};
|
|
|
|
namespace details
|
|
{
|
|
|
|
// compile-time hash for type
|
|
// (from https://stackoverflow.com/questions/56292104/hashing-types-at-compile-time-in-c17-c2a)
|
|
// WARNING: it does not work for unnamed struct!!!
|
|
template <typename T>
|
|
static consteval size_t Hash()
|
|
{
|
|
size_t result{};
|
|
|
|
#ifdef _MSC_VER
|
|
for (const auto& c : __FUNCSIG__)
|
|
#else // GCC and clang
|
|
for (const auto& c : __PRETTY_FUNCTION__)
|
|
#endif
|
|
(result ^= c) <<= 1;
|
|
|
|
return result;
|
|
}
|
|
|
|
} // namespace details
|
|
|
|
template <typename T>
|
|
static constexpr size_t mcc_type_hash = details::Hash<T>();
|
|
|
|
static constexpr size_t mcc_hash_combine(size_t lhs, size_t rhs)
|
|
{
|
|
constexpr size_t v_const = sizeof(size_t) >= 8 ? 0x517cc1b727220a95 : 0x9e3779b9;
|
|
|
|
lhs ^= rhs + v_const + (lhs << 6) + (lhs >> 2);
|
|
|
|
return lhs;
|
|
}
|
|
|
|
template <typename T1, typename T2>
|
|
static constexpr size_t mcc_type_pair_hash()
|
|
{
|
|
return mcc_hash_combine(mcc_type_hash<T1>, mcc_type_hash<T2>);
|
|
}
|
|
|
|
} // namespace mcc::traits
|