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)
 |