297 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			297 lines
		
	
	
		
			9.4 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
 | 
						|
#pragma once
 | 
						|
 | 
						|
#if !defined(MIJIN_UTIL_TRAITS_HPP_INCLUDED)
 | 
						|
#define MIJIN_UTIL_TRAITS_HPP_INCLUDED 1
 | 
						|
 | 
						|
#include <tuple>
 | 
						|
 | 
						|
namespace mijin
 | 
						|
{
 | 
						|
 | 
						|
//
 | 
						|
// public defines
 | 
						|
//
 | 
						|
 | 
						|
//
 | 
						|
// public constants
 | 
						|
//
 | 
						|
 | 
						|
//
 | 
						|
// public types
 | 
						|
//
 | 
						|
 | 
						|
struct Type_; // use as typevar
 | 
						|
struct Any_; // use as placeholder in templates
 | 
						|
 | 
						|
template<typename T>
 | 
						|
struct always_false
 | 
						|
{
 | 
						|
    static constexpr bool value = false;
 | 
						|
};
 | 
						|
 | 
						|
template<typename T>
 | 
						|
inline constexpr bool always_false_v = always_false<T>::value;
 | 
						|
 | 
						|
template<auto V>
 | 
						|
struct always_false_val
 | 
						|
{
 | 
						|
    static constexpr bool value = false;
 | 
						|
};
 | 
						|
 | 
						|
template<auto V>
 | 
						|
inline constexpr bool always_false_val_v = always_false_val<V>::value;
 | 
						|
 | 
						|
template<template<typename> typename TFilter, typename... TArgs>
 | 
						|
struct TypeFilter;
 | 
						|
 | 
						|
template<template<typename> typename TFilter>
 | 
						|
struct TypeFilter<TFilter>
 | 
						|
{
 | 
						|
    using type_t = std::tuple<>;
 | 
						|
};
 | 
						|
 | 
						|
template<template<typename> typename TFilter, typename TFirst, typename... TArgs>
 | 
						|
struct TypeFilter<TFilter, TFirst, TArgs...>
 | 
						|
{
 | 
						|
    static consteval auto makeTypeHelper()
 | 
						|
    {
 | 
						|
        using base_t = typename TypeFilter<TFilter, TArgs...>::type_t;
 | 
						|
 | 
						|
        // note: not using decltype, as the compiler might think it is being evaluated
 | 
						|
        if constexpr (!TFilter<TFirst>::value) {
 | 
						|
            return static_cast<base_t*>(nullptr);
 | 
						|
        }
 | 
						|
        else {
 | 
						|
            auto wrapper = []<typename... TArgsInner>(std::tuple<TArgsInner...>*)
 | 
						|
            {
 | 
						|
                return static_cast<std::tuple<TFirst, TArgsInner...>*>(nullptr);
 | 
						|
            };
 | 
						|
            return wrapper(static_cast<base_t*>(nullptr));
 | 
						|
        }
 | 
						|
    }
 | 
						|
 | 
						|
    using type_t = std::remove_pointer_t<decltype(makeTypeHelper())>;
 | 
						|
};
 | 
						|
 | 
						|
template<template<typename> typename TFilter, typename... TArgs>
 | 
						|
auto typeFilterHelper(std::tuple<TArgs...>*)
 | 
						|
{
 | 
						|
    return static_cast<typename TypeFilter<TFilter, TArgs...>::type_t*>(nullptr);
 | 
						|
}
 | 
						|
 | 
						|
template<template<typename> typename TFilter, typename TTuple>
 | 
						|
using filter_types_t = std::remove_pointer_t<decltype(typeFilterHelper<TFilter>(static_cast<TTuple*>(nullptr)))>;
 | 
						|
 | 
						|
template<template<typename> typename TPredicate, typename... TArgs>
 | 
						|
auto mapTypesHelper(std::tuple<TArgs...>)
 | 
						|
{
 | 
						|
    return static_cast<std::tuple<TPredicate<TArgs>...>*>(nullptr);
 | 
						|
}
 | 
						|
 | 
						|
template<template<typename> typename TPredicate, typename TTuple>
 | 
						|
using map_types_t = std::remove_pointer_t<decltype(mapTypesHelper<TPredicate>(std::declval<TTuple>()))>;
 | 
						|
 | 
						|
template<template<typename> typename TPredicate, template<typename> typename TTemplate>
 | 
						|
struct map_template {
 | 
						|
    template<typename T>
 | 
						|
    using type_t = TTemplate<TPredicate<T>>;
 | 
						|
};
 | 
						|
 | 
						|
template<template<typename...> typename TTemplate, typename TType>
 | 
						|
struct is_template_instance : std::false_type {};
 | 
						|
 | 
						|
template<template<typename...> typename TTemplate, typename... TArgs>
 | 
						|
struct is_template_instance<TTemplate, TTemplate<TArgs...>> : std::true_type {};
 | 
						|
 | 
						|
template<template<typename...> typename TTemplate, typename TType>
 | 
						|
constexpr bool is_template_instance_v = is_template_instance<TTemplate, TType>::value;
 | 
						|
 | 
						|
namespace impl
 | 
						|
{
 | 
						|
template<template<typename...> typename TTemplate, typename... TArgsTTmpl>
 | 
						|
struct tmpl_param_comparator
 | 
						|
{
 | 
						|
    template<typename TTmpl, typename TInstance>
 | 
						|
    static constexpr bool compare_single = std::is_same_v<TTmpl, TInstance> or std::is_same_v<TTmpl, Any_>;
 | 
						|
 | 
						|
    template<typename T>
 | 
						|
    struct matches : std::false_type {};
 | 
						|
 | 
						|
    // original (which MSVC didn't like for some reason :/)
 | 
						|
    // template<typename... TArgsInstance>
 | 
						|
    // struct matches<TTemplate<TArgsInstance...>> : std::bool_constant<(compare_single<TArgsTTmpl, TArgsInstance> && ...)> {};
 | 
						|
 | 
						|
    template<template<typename...> typename TOtherTemplate, typename... TArgsInstance> requires(is_template_instance_v<TTemplate, TOtherTemplate<TArgsInstance...>>)
 | 
						|
    struct matches<TOtherTemplate<TArgsInstance...>> : std::bool_constant<(compare_single<TArgsTTmpl, TArgsInstance> && ...)> {};
 | 
						|
 | 
						|
    template<typename T>
 | 
						|
    using matches_t = matches<T>;
 | 
						|
};
 | 
						|
}
 | 
						|
 | 
						|
template<typename TTemplate, typename TType>
 | 
						|
struct match_template_type : std::false_type {};
 | 
						|
 | 
						|
template<template<typename...> typename TTemplate, typename TType, typename... TArgs>
 | 
						|
struct match_template_type<TTemplate<TArgs...>, TType> : impl::tmpl_param_comparator<TTemplate, TArgs...>::template matches_t<TType> {};
 | 
						|
 | 
						|
template<typename TTemplate, typename TType>
 | 
						|
constexpr bool match_template_type_v = match_template_type<TTemplate, TType>::value;
 | 
						|
 | 
						|
// similar to std::is_same, but allows placeholders
 | 
						|
//   - is_type_or_impl<some_tmpl<Type_>, some_type> resolves to some_tmpl<some_type>
 | 
						|
//   - is_type_or_impl<some_tmpl<Any_, int>, some_type> checks if some_type is an instance of some_tmpl with int as 2nd parameter
 | 
						|
template<typename TMatch, typename TConcrete>
 | 
						|
struct type_matches
 | 
						|
{
 | 
						|
    using type = std::is_same<TMatch, TConcrete>;
 | 
						|
};
 | 
						|
 | 
						|
template<template<typename> typename TTmplMatch, typename TConcrete>
 | 
						|
struct type_matches<TTmplMatch<Type_>, TConcrete>
 | 
						|
{
 | 
						|
    using type = TTmplMatch<TConcrete>;
 | 
						|
};
 | 
						|
 | 
						|
template<template<typename...> typename TTmplMatch, typename TConcrete, typename... TArgs>
 | 
						|
struct type_matches<TTmplMatch<TArgs...>, TConcrete>
 | 
						|
{
 | 
						|
    using type = match_template_type<TTmplMatch<TArgs...>, TConcrete>;
 | 
						|
};
 | 
						|
 | 
						|
template<typename TMatch, typename TConcrete>
 | 
						|
using type_matches_t = type_matches<TMatch, TConcrete>::type;
 | 
						|
 | 
						|
template<typename TMatch, typename TConcrete>
 | 
						|
inline constexpr bool type_matches_v = type_matches_t<TMatch, TConcrete>::value;
 | 
						|
 | 
						|
template<typename TConcrete, typename... TMatches>
 | 
						|
struct is_any_type : std::disjunction<std::is_same<TConcrete, TMatches>...> {};
 | 
						|
 | 
						|
template<typename TConcrete, typename... TMatches>
 | 
						|
static constexpr bool is_any_type_v = is_any_type<TConcrete, TMatches...>::value;
 | 
						|
 | 
						|
template<typename TConcrete, typename... TMatches>
 | 
						|
struct match_any_type : std::disjunction<type_matches_t<TMatches, TConcrete>...> {};
 | 
						|
 | 
						|
template<typename TConcrete, typename... TMatches>
 | 
						|
static constexpr bool match_any_type_v = match_any_type<TConcrete, TMatches...>::value;
 | 
						|
 | 
						|
template<typename T, typename... Types>
 | 
						|
concept union_type = match_any_type_v<T, Types...>;
 | 
						|
 | 
						|
template<typename TElement, typename TCollection>
 | 
						|
struct is_type_member;
 | 
						|
 | 
						|
template<typename TElement, template<typename...> typename TCollection, typename... Ts>
 | 
						|
struct is_type_member<TElement, TCollection<Ts...>>
 | 
						|
  : std::bool_constant<(... || std::is_same<TElement, Ts>{})>
 | 
						|
{};
 | 
						|
 | 
						|
template<typename TElement, typename TCollection>
 | 
						|
constexpr bool is_type_member_v = is_type_member<TElement, TCollection>::value;
 | 
						|
 | 
						|
template<typename TFrom, typename TTo>
 | 
						|
using copy_const_t = std::conditional_t<std::is_const_v<TFrom>, std::add_const_t<TTo>, std::remove_const_t<TTo>>;
 | 
						|
 | 
						|
template<typename TFrom, typename TTo>
 | 
						|
using copy_volatile_t = std::conditional_t<std::is_volatile_v<TFrom>, std::add_volatile_t<TTo>, std::remove_volatile_t<TTo>>;
 | 
						|
 | 
						|
template<typename TFrom, typename TTo>
 | 
						|
using copy_cv_t = copy_const_t<TFrom, copy_volatile_t<TFrom, TTo>>;
 | 
						|
 | 
						|
template<typename TActual, typename TDelay>
 | 
						|
using delay_type_t = TActual;
 | 
						|
 | 
						|
 | 
						|
template<std::size_t I, typename TArg, typename... TArgs>
 | 
						|
struct TypeAtHelper
 | 
						|
{
 | 
						|
    using type_t = typename TypeAtHelper<I-1, TArgs...>::type_t;
 | 
						|
};
 | 
						|
 | 
						|
template<typename TArg, typename... TArgs>
 | 
						|
struct TypeAtHelper<0, TArg, TArgs...>
 | 
						|
{
 | 
						|
    using type_t = TArg;
 | 
						|
};
 | 
						|
template<std::size_t I, typename... TArgs>
 | 
						|
using type_at_t = TypeAtHelper<I, TArgs...>::type_t;
 | 
						|
 | 
						|
template<typename TDefault, template<typename...> typename TOper, typename... TArgs>
 | 
						|
struct detect_or
 | 
						|
{
 | 
						|
    using type = TDefault;
 | 
						|
    static constexpr bool detected = false;
 | 
						|
};
 | 
						|
 | 
						|
template<typename TDefault, template<typename...> typename TOper, typename... TArgs>
 | 
						|
    requires requires { typename TOper<TArgs...>; }
 | 
						|
struct detect_or<TDefault, TOper, TArgs...>
 | 
						|
{
 | 
						|
    using type = TOper<TArgs...>;
 | 
						|
    static constexpr bool detected = true;
 | 
						|
};
 | 
						|
template<typename TDefault, template<typename...> typename TOper, typename... TArgs>
 | 
						|
using detect_or_t = detect_or<TDefault, TOper, TArgs...>::type;
 | 
						|
 | 
						|
struct empty_type {};
 | 
						|
 | 
						|
template<typename T, bool enable>
 | 
						|
struct optional_base
 | 
						|
{
 | 
						|
    using type = T;
 | 
						|
};
 | 
						|
 | 
						|
template<typename T>
 | 
						|
struct optional_base<T, false>
 | 
						|
{
 | 
						|
    using type = empty_type;
 | 
						|
};
 | 
						|
 | 
						|
template<typename T, bool enable>
 | 
						|
using optional_base_t = optional_base<T, enable>::type;
 | 
						|
 | 
						|
//
 | 
						|
// public functions
 | 
						|
//
 | 
						|
 | 
						|
template<typename THelper, typename TType>
 | 
						|
decltype(auto) delayEvaluation(TType&& value)
 | 
						|
{
 | 
						|
    return static_cast<TType&&>(value);
 | 
						|
}
 | 
						|
 | 
						|
#define MIJIN_STATIC_TESTS 1
 | 
						|
#if MIJIN_STATIC_TESTS
 | 
						|
namespace test
 | 
						|
{
 | 
						|
template<typename T, typename U>
 | 
						|
struct MyTemplate {};
 | 
						|
 | 
						|
static_assert(match_template_type_v<MyTemplate<Any_, int>, MyTemplate<int, int>>);
 | 
						|
static_assert(match_template_type_v<MyTemplate<Any_, Any_>, MyTemplate<int, int>>);
 | 
						|
static_assert(type_matches_v<MyTemplate<Any_, int>, MyTemplate<int, int>>);
 | 
						|
static_assert(type_matches_v<MyTemplate<Any_, Any_>, MyTemplate<int, int>>);
 | 
						|
static_assert(!type_matches_v<MyTemplate<double, int>, MyTemplate<int, int>>);
 | 
						|
static_assert(type_matches_v<MyTemplate<Any_, Any_>, MyTemplate<int, double>>);
 | 
						|
static_assert(type_matches_v<std::is_pointer<Type_>, void*>);
 | 
						|
 | 
						|
static_assert(union_type<int, int>);
 | 
						|
static_assert(!union_type<int>);
 | 
						|
static_assert(!union_type<int, double>);
 | 
						|
static_assert(union_type<MyTemplate<int, int>, MyTemplate<int, int>>);
 | 
						|
static_assert(union_type<MyTemplate<int, int>, MyTemplate<Any_, int>>);
 | 
						|
static_assert(union_type<MyTemplate<int, int>, MyTemplate<Any_, Any_>>);
 | 
						|
static_assert(union_type<MyTemplate<int, int>, MyTemplate<double, double>, MyTemplate<Any_, Any_>>);
 | 
						|
static_assert(!union_type<int*, int>);
 | 
						|
static_assert(union_type<int*, std::is_pointer<Type_>>);
 | 
						|
static_assert(!union_type<int, std::is_pointer<Type_>>);
 | 
						|
}
 | 
						|
#endif
 | 
						|
 | 
						|
} // namespace mijin
 | 
						|
 | 
						|
#endif // !defined(MIJIN_UTIL_TRAITS_HPP_INCLUDED)
 |