ADC/common/adc_traits.h
2024-09-15 01:25:58 +03:00

216 lines
5.9 KiB
C++

#pragma once
#include <chrono>
#include <format>
#include <ranges>
#include <tuple>
#include <type_traits>
/*
ABSTRACT DEVICE COMPONENTS LIBRARY
*/
namespace adc::traits
{
// check if type can be used with std::format_to
// (from
// https://stackoverflow.com/questions/72430369/how-to-check-that-a-type-is-formattable-using-type-traits-concepts)
template <typename T>
concept formattable =
requires(T v, std::format_context ctx) { std::formatter<std::remove_cvref_t<T>>().format(v, ctx); };
// range of char/const char
template <typename R, typename CharT = char>
concept adc_char_range =
std::ranges::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 adc_output_char_range =
std::ranges::output_range<R, CharT> && std::same_as<std::remove_cv_t<std::ranges::range_value_t<R>>, CharT>;
// range of char/const char
template <typename R, typename CharT = char>
concept adc_input_char_range =
std::ranges::input_range<R> && std::is_same_v<std::remove_cv_t<std::ranges::range_value_t<R>>, CharT>;
template <typename R>
concept adc_char_view = std::ranges::view<R> && std::same_as<std::ranges::range_value_t<R>, char>;
template <typename R>
concept adc_view_or_output_char_range = adc_char_view<R> || adc_output_char_range<R>;
template <typename R>
concept adc_range_of_view_char_range = std::ranges::range<R> && std::ranges::view<std::ranges::range_value_t<R>> &&
std::same_as<std::ranges::range_value_t<std::ranges::range_value_t<R>>, char>;
template <typename R>
concept adc_range_of_input_char_range =
std::ranges::range<R> && traits::adc_input_char_range<std::ranges::range_value_t<R>>;
template <typename R>
concept adc_range_of_view_or_output_char_range = adc_range_of_view_char_range<R> || adc_output_char_range<R>;
// deduce returned type of callable
// template <typename T>
// using adc_retval_t = std::invoke_result_t<std::remove_cvref_t<T>>;
// helper classes
template <typename... Ts>
struct adc_func_traits_helper_t;
template <typename R>
struct adc_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 adc_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;
};
// callable concept and its signature traits
template <typename T>
concept adc_is_callable = std::is_function_v<T> || (std::is_object_v<T> && requires(T) { &T::operator(); });
// std::is_function_v<T> || (std::is_object_v<T> && requires(T) { std::is_function_v<decltype(&T::operator())>; });
template <typename F>
struct adc_func_traits {
// use of an empty struct here to match std::invoke_result behaivior (at least of GCC)
};
// special case
template <>
struct adc_func_traits<std::nullptr_t> {
using ret_t = std::nullptr_t;
using args_t = std::tuple<>;
using arg1_t = std::nullptr_t;
static constexpr size_t arity = 0;
};
template <typename R, typename... Args>
struct adc_func_traits<R (*)(Args...)> : adc_func_traits_helper_t<R, Args...> {
};
template <typename R, typename... Args>
struct adc_func_traits<R(Args...)> : adc_func_traits_helper_t<R, Args...> {
};
template <typename C, typename R, typename... Args>
struct adc_func_traits<R (C::*)(Args...)> : adc_func_traits_helper_t<R, Args...> {
};
template <typename C, typename R, typename... Args>
struct adc_func_traits<R (C::*)(Args...) const> : adc_func_traits_helper_t<R, Args...> {
};
template <typename F>
requires adc_is_callable<F>
struct adc_func_traits<F> : adc_func_traits<decltype(&F::operator())> {
};
template <typename F>
struct adc_func_traits<F&> : adc_func_traits<F> {
};
template <typename F>
struct adc_func_traits<F&&> : adc_func_traits<F> {
};
template <typename T>
using adc_retval_t = typename adc_func_traits<T>::ret_t;
template <typename T>
using adc_func_arg1_t = typename adc_func_traits<T>::arg1_t;
// deduce type
template <typename T>
using adc_deduced_type =
std::conditional_t<std::is_lvalue_reference_v<T> && !std::is_const_v<std::remove_reference_t<T>>,
T&,
std::remove_cvref_t<T>>;
// perfect-forwarding wrapper
template <typename T, typename... Ts>
constexpr static auto adc_pf_wrapper(T&& v, Ts&&... vs)
{
return std::tuple<adc_deduced_type<T>, adc_deduced_type<Ts>...>(std::forward<T>(v), std::forward<Ts>(vs)...);
}
// check if type T is one of types in sequence Rest (e.g. adc_is_any<int,char,float,int,std::string>::value == true)
template <typename T, typename... Rest>
struct adc_is_any_of : std::disjunction<std::is_same<T, Rest>...> {
};
template <typename T, typename... Rest>
static inline constexpr bool adc_is_any_of_v = std::disjunction_v<std::is_same<T, Rest>...>;
template <typename T>
struct adc_is_tuple : std::false_type {
};
template <typename T>
struct adc_is_tuple<T&> : adc_is_tuple<T> {
};
template <typename T>
struct adc_is_tuple<const T&> : adc_is_tuple<T> {
};
template <typename T>
struct adc_is_tuple<T&&> : adc_is_tuple<T> {
};
template <typename... Ts>
struct adc_is_tuple<std::tuple<Ts...>> : std::true_type {
};
template <typename T1, typename T2>
struct adc_is_tuple<std::pair<T1, T2>> : std::true_type {
};
template <typename T>
static inline constexpr bool adc_is_tuple_v = adc_is_tuple<T>::value;
template <typename T>
concept adc_tuple_like = adc_is_tuple_v<T> == true;
template <typename T>
// from https://stackoverflow.com/questions/74383254/concept-that-models-only-the-stdchrono-duration-types
concept adc_time_duration_c = requires {
[]<class Rep, class Period>(std::type_identity<std::chrono::duration<Rep, Period>>) {
}(std::type_identity<std::remove_cvref_t<T>>());
};
} // namespace adc::traits