sparrow 0.9.0
Loading...
Searching...
No Matches
mp_utils.hpp
Go to the documentation of this file.
1// Copyright 2024 Man Group Operations Limited
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15#pragma once
16
17#include <concepts>
18#include <cstdint>
19#include <iterator>
20#include <memory>
21#include <ranges>
22#include <tuple>
23#include <type_traits>
24
25namespace sparrow::mpl
26{
27
52 template <class... T>
53 struct dependent_false : std::false_type
54 {
55 };
56
65 template <class L, template <class...> class U>
66 struct is_type_instance_of : std::false_type
67 {
68 };
69
80 template <template <class...> class L, template <class...> class U, class... T>
81 requires std::is_same_v<L<T...>, U<T...>>
82 struct is_type_instance_of<L<T...>, U> : std::true_type
83 {
84 };
85
101 template <class T, template <class...> class U>
103
121 template <class... T>
122 struct typelist
123 {
124 };
125
147 template <class... Ts, class... Us>
149 consteval auto append(typelist<Ts...>, Us...)
150 {
151 return typelist<Ts..., Us...>{};
152 }
153
173 template <class... Ts, class... Us>
174 consteval auto append(typelist<Ts...>, typelist<Us...>) // TODO: Handle several typelists
175 {
176 return typelist<Ts..., Us...>{};
177 }
178
196 template <class TypeList, class... Us>
198 using append_t = decltype(append(TypeList{}, Us{}...));
199
215 template <class... T>
216 constexpr std::size_t size(typelist<T...> = {})
217 {
218 return sizeof...(T);
219 }
220
236 template <typename TList>
238
239 namespace impl
240 {
241 template <class From, template <class...> class To>
243
244 template <template <class...> class From, template <class...> class To, class... T>
245 struct rename_impl<From<T...>, To>
246 {
247 using type = To<T...>;
248 };
249 }
250
267 template <class From, template <class...> class To>
269
272
285 template <template <class...> class W, class T>
286 concept type_wrapper = std::same_as<W<T>, typelist<T>> or std::same_as<W<T>, std::type_identity_t<T>>;
287
304 template <template <class> class P, class T>
305 concept ct_type_predicate = requires {
306 { P<T>::value } -> std::convertible_to<bool>;
307 };
308
327 template< class P, class T >
328 concept callable_type_predicate = std::semiregular<std::decay_t<P>>
329 and (
330 requires(std::decay_t<P> predicate)
331 {
332 { predicate(typelist<T>{}) } -> std::convertible_to<bool>;
333 }
334 or
335 requires(std::decay_t<P> predicate)
336 {
337 { predicate(std::type_identity_t<T>{}) } -> std::convertible_to<bool>;
338 }
339 )
340 ;
341
352 template <class T, template <class> class P>
354 consteval bool evaluate(P<T>)
355 {
356 return P<T>::value;
357 }
358
370 template <class T, callable_type_predicate<T> P>
371 [[nodiscard]] consteval bool evaluate(P predicate)
372 {
373 if constexpr (requires(std::decay_t<P> p) {
374 { p(typelist<T>{}) } -> std::same_as<bool>;
375 })
376 {
377 return predicate(typelist<T>{});
378 }
379 else
380 {
381 return predicate(std::type_identity_t<T>{});
382 }
383 }
384
385 namespace predicate
386 {
387
394 template <class T>
395 struct same_as
396 {
397 template <template <class...> class W, class X>
398 requires type_wrapper<W, X>
399 consteval bool operator()(W<X>) const
400 {
401 return std::same_as<T, X>;
402 }
403 };
404 }
405
406 template <template <class> class P>
408 {
409 template <template <class...> class W, class T>
411 consteval bool operator()(W<T>) const
412 {
413 return P<T>::value;
414 }
415 };
416
419 template <template <class> class P>
420 consteval auto as_predicate()
421 {
423 };
424
427
448 template <class Predicate, template <class...> class L, class... T>
449 requires any_typelist<L<T...>> and (callable_type_predicate<Predicate, T> && ...)
450 [[nodiscard]] consteval bool any_of(L<T...>, [[maybe_unused]] Predicate predicate = {})
451 {
452 return (evaluate<T>(predicate) || ... || false);
453 }
454
469 template <template <class> class Predicate, template <class...> class L, class... T>
470 requires any_typelist<L<T...>> and (ct_type_predicate<Predicate, T> && ...)
471 [[nodiscard]] consteval bool any_of(L<T...> list)
472 {
473 return any_of(list, as_predicate<Predicate>());
474 }
475
490 template <class Predicate, template <class...> class L, class... T>
491 requires any_typelist<L<T...>> and (callable_type_predicate<Predicate, T> && ...)
492 [[nodiscard]] consteval bool all_of(L<T...>, [[maybe_unused]] Predicate predicate)
493 {
494 return (evaluate<T>(predicate) && ... && true);
495 }
496
511 template <template <class> class Predicate, template <class...> class L, class... T>
512 requires any_typelist<L<T...>> and (ct_type_predicate<Predicate, T> && ...)
513 [[nodiscard]] consteval bool all_of(L<T...> list)
514 {
515 return all_of(list, as_predicate<Predicate>());
516 }
517
533 template <class Predicate, template <class...> class L, class... T>
534 requires any_typelist<L<T...>> and (callable_type_predicate<Predicate, T> && ...)
535 [[nodiscard]] consteval std::size_t find_if(L<T...>, Predicate predicate)
536 {
537 std::size_t idx = 0;
538 auto check = [&](bool match_success)
539 {
540 if (match_success)
541 {
542 return true;
543 }
544 else
545 {
546 ++idx;
547 return false;
548 }
549 };
550
551 (check(evaluate<T>(predicate)) || ...);
552
553 return idx;
554 }
555
570 template <template <class> class Predicate, template <class...> class L, class... T>
571 requires any_typelist<L<T...>> and (ct_type_predicate<Predicate, T> && ...)
572 [[nodiscard]] consteval std::size_t find_if(L<T...> list)
573 {
574 return find_if(list, as_predicate<Predicate>());
575 }
576
589 template <class TypeToFind, any_typelist L>
590 [[nodiscard]] consteval std::size_t find(L list)
591 {
593 }
594
595 namespace impl
596 {
597 template <class L, class T>
599
600 template <template <class...> class L, class T>
601 struct contains_impl<L<>, T> : std::false_type
602 {
603 };
604
605 template <template <class...> class L, class T, class... U>
606 struct contains_impl<L<T, U...>, T> : std::true_type
607 {
608 };
609
610 template <template <class...> class L, class V, class... U, class T>
611 struct contains_impl<L<V, U...>, T> : contains_impl<L<U...>, T>
612 {
613 };
614 }
615
632 template <any_typelist L, class V>
633 [[nodiscard]] consteval bool contains()
634 {
636 }
637
638 namespace impl
639 {
640 template <template <class...> class F, class... L>
642 {
643 static_assert(dependent_false<L...>::value, "transform can apply to typelist-like types only");
644 };
645
646 template <template <class...> class F, template <class...> class L, class... T>
647 struct transform_impl<F, L<T...>>
648 {
649 using type = L<F<T>...>;
650 };
651
652 template <template <class...> class F, template <class...> class L1, class... T1, template <class...> class L2, class... T2>
653 struct transform_impl<F, L1<T1...>, L2<T2...>>
654 {
655 using type = L1<F<T1, T2>...>;
656 };
657 }
658
681 template <template <class> class F, class... L>
682 using transform = typename impl::transform_impl<F, L...>::type;
683
684 namespace impl
685 {
686 template <class S1, class S2>
688
689 template <template <class...> class L, class... T>
690 struct merge_set_impl<L<T...>, L<>>
691 {
692 using type = L<T...>;
693 };
694
695 template <template <class...> class L, class... T, class U, class... V>
696 struct merge_set_impl<L<T...>, L<U, V...>>
697 {
698 using first_arg = std::conditional_t<contains<L<T...>, U>(), L<T...>, L<T..., U>>;
699 using type = typename merge_set_impl<first_arg, L<V...>>::type;
700 };
701 }
702
721 template <class L1, class L2>
723
724 namespace impl
725 {
726 template <class L>
728
729 template <template <class...> class L, class... T>
730 struct unique_impl<L<T...>>
731 {
732 using type = merge_set<L<>, L<T...>>;
733 };
734 }
735
739 template <class L>
741
744
753 template <class T>
754 struct add_const_lvalue_reference : std::add_lvalue_reference<std::add_const_t<T>>
755 {
756 };
757
765 template <class T>
767
777 template <typename T, bool is_const>
778 struct constify
779 {
780 using type = std::conditional_t<is_const, const T, T>;
781 };
782
789 template <typename T, bool is_const>
790 struct constify<T&, is_const>
791 {
792 using type = std::conditional_t<is_const, const T&, T&>;
793 };
794
806 template <class T, bool is_const>
808
819 template <class T>
820 using iter_const_reference_t = std::common_reference_t<const std::iter_value_t<T>&&, std::iter_reference_t<T>>;
821
838 template <class T>
839 concept constant_iterator = std::input_iterator<T>
840 && std::same_as<iter_const_reference_t<T>, std::iter_reference_t<T>>;
841
858 template <class T>
859 concept constant_range = std::ranges::input_range<T> && constant_iterator<std::ranges::iterator_t<T>>;
860
882 [[noreturn]] inline void unreachable()
883 {
884 // Uses compiler specific extensions if possible.
885 // Even if no extension is used, undefined behavior is still raised by
886 // an empty function body and the noreturn attribute.
887#if defined(_MSC_VER) && !defined(__clang__) // MSVC
888 __assume(false);
889#else // GCC, Clang
890 __builtin_unreachable();
891#endif
892 }
893
901 template <class BoolRange>
902 concept bool_convertible_range = std::ranges::range<BoolRange>
903 && std::convertible_to<std::ranges::range_value_t<BoolRange>, bool>;
904
915 template <class T>
916 concept boolean_like = std::is_assignable_v<std::add_lvalue_reference_t<std::decay_t<T>>, bool>
917 and requires { static_cast<bool>(std::declval<T>()); };
918
930 template <class From, class To>
931 concept convertible_ranges = std::convertible_to<std::ranges::range_value_t<From>, std::ranges::range_value_t<To>>;
932
940 template <typename T>
942
950 template <typename T>
952 || std::derived_from<T, std::unique_ptr<typename T::element_type>>;
953
961 template <typename T>
963
971 template <typename T>
973 || std::derived_from<T, std::shared_ptr<typename T::element_type>>;
974
982 template <typename T>
984
992 template <typename T>
994
1002 template <class T>
1003 concept testable = requires(T t) { t ? true : false; };
1004
1017 template <typename T, typename Y, template <typename> typename Qualifier>
1018 concept T_matches_qualifier_if_Y_is = Qualifier<T>::value || !Qualifier<Y>::value;
1019
1031 template <class CLS, class... ARGS>
1033 {
1034 static constexpr bool value = true;
1035 };
1036
1042 template <class CLS>
1044 {
1045 static constexpr bool value = true;
1046 };
1047
1056 template <class CLS, class T>
1058 {
1059 static constexpr bool value = !std::is_same_v<CLS, std::remove_cvref_t<T>>;
1060 };
1061
1068 template <class CLS, class... ARGS>
1069 constexpr bool excludes_copy_and_move_ctor_v = excludes_copy_and_move_ctor<CLS, ARGS...>::value;
1070
1083 template <typename I, typename T>
1084 concept iterator_of_type = std::input_iterator<I>
1085 && std::same_as<typename std::iterator_traits<I>::value_type, T>;
1086
1099 template <class T>
1100 concept char_like = std::same_as<T, char> || std::same_as<T, std::byte> || std::same_as<T, uint8_t>;
1101
1112 template <typename T>
1113 concept std_array = requires {
1114 typename std::remove_cvref_t<T>::value_type;
1115 requires std::same_as<
1116 std::array<typename std::remove_cvref_t<T>::value_type, std::tuple_size<std::remove_cvref_t<T>>::value>,
1117 std::remove_cvref_t<T>>;
1118 };
1119
1120 // Concept to check if the instances of two types can be compared with operator==.
1121 // Notice that this concept is less restrictive than std::equality_comparable_with,
1122 // which requires the existence of a common refefenrece type for T and U. This additional
1123 // restriction makes it impossible to use it in the context of sparrow, where we want to
1124 // compare objects that are logically similar while being "physically" different.
1125 template <class T, class U>
1127 const std::remove_reference_t<T>& t,
1128 const std::remove_reference_t<U>& u
1129 ) {
1130 { t == u } -> std::convertible_to<bool>;
1131 { t != u } -> std::convertible_to<bool>;
1132 { u == t } -> std::convertible_to<bool>;
1133 { u != t } -> std::convertible_to<bool>;
1134 };
1135}
Concept for matching qualifier requirements.
Concept that matches any typelist instantiation.
Definition mp_utils.hpp:237
Concept for ranges whose elements are convertible to bool.
Definition mp_utils.hpp:902
Concept for boolean-like types.
Definition mp_utils.hpp:916
Concept for callable type predicates.
Definition mp_utils.hpp:328
Concept for character-like types.
Concept for constant iterators.
Definition mp_utils.hpp:839
Concept for constant ranges.
Definition mp_utils.hpp:859
Concept for convertible range types.
Definition mp_utils.hpp:931
Concept for compile-time type predicates.
Definition mp_utils.hpp:305
Concept for iterators of a specific value type.
Concept for shared_ptr or derived types.
Definition mp_utils.hpp:972
Concept for shared_ptr instances.
Definition mp_utils.hpp:962
Concept for smart pointers and derived types.
Definition mp_utils.hpp:993
Concept for any smart pointer type.
Definition mp_utils.hpp:983
Concept for std::array types.
Concept for testable types in boolean contexts.
Concept for template types that can wrap a single type.
Definition mp_utils.hpp:286
Concept for unique_ptr or derived types.
Definition mp_utils.hpp:951
Concept for unique_ptr instances.
Definition mp_utils.hpp:941
consteval bool all_of(L< T... >, Predicate predicate)
Checks if all types in the typelist satisfy the predicate.
Definition mp_utils.hpp:492
consteval bool any_of(L< T... >, Predicate predicate={})
Checks if at least one type in the typelist satisfies the predicate.
Definition mp_utils.hpp:450
consteval bool evaluate(P< T >)
Evaluates a compile-time template predicate.
Definition mp_utils.hpp:354
decltype(append(TypeList{}, Us{}...)) append_t
Type alias for appending types or typelists to a given typelist.
Definition mp_utils.hpp:198
std::common_reference_t< const std::iter_value_t< T > &&, std::iter_reference_t< T > > iter_const_reference_t
Computes the const reference type of an iterator.
Definition mp_utils.hpp:820
consteval std::size_t find_if(L< T... >, Predicate predicate)
Finds the index of the first type satisfying the predicate.
Definition mp_utils.hpp:535
constexpr std::size_t size(typelist< T... >={})
Gets the count of types contained in a typelist.
Definition mp_utils.hpp:216
consteval auto append(typelist< Ts... >, Us...)
Appends individual types to a typelist.
Definition mp_utils.hpp:149
consteval auto as_predicate()
Definition mp_utils.hpp:420
constexpr bool excludes_copy_and_move_ctor_v
Convenience variable template for excludes_copy_and_move_ctor.
typename impl::merge_set_impl< L1, L2 >::type merge_set
Generates the union of two typelists, removing duplicates.
Definition mp_utils.hpp:722
constexpr bool is_type_instance_of_v
Variable template for convenient access to is_type_instance_of.
Definition mp_utils.hpp:102
typename impl::unique_impl< L >::type unique
Removes all duplicated types in the given typelist Example: unique<typelist<int, float,...
Definition mp_utils.hpp:740
void unreachable()
Invokes undefined behavior for optimization purposes.
Definition mp_utils.hpp:882
typename constify< T, is_const >::type constify_t
Convenience alias for constify.
Definition mp_utils.hpp:807
typename impl::rename_impl< From, To >::type rename
Changes the template type of a type list.
Definition mp_utils.hpp:268
consteval bool contains()
Checks if a typelist contains a specific type.
Definition mp_utils.hpp:633
consteval std::size_t find(L list)
Finds the index of a specific type in the typelist.
Definition mp_utils.hpp:590
typename add_const_lvalue_reference< T >::type add_const_lvalue_reference_t
Convenience alias for add_const_lvalue_reference.
Definition mp_utils.hpp:766
typename impl::transform_impl< F, L... >::type transform
Applies a metafunction to each element of typelists.
Definition mp_utils.hpp:682
Adds const and lvalue reference to a type.
Definition mp_utils.hpp:755
std::conditional_t< is_const, const T &, T & > type
Definition mp_utils.hpp:792
Conditionally adds const to a type.
Definition mp_utils.hpp:779
std::conditional_t< is_const, const T, T > type
Definition mp_utils.hpp:780
consteval bool operator()(W< T >) const
Definition mp_utils.hpp:411
Workaround to replace static_assert(false) in template code.
Definition mp_utils.hpp:54
Helper to exclude copy and move constructors from variadic templates.
typename merge_set_impl< first_arg, L< V... > >::type type
Definition mp_utils.hpp:699
std::conditional_t< contains< L< T... >, U >(), L< T... >, L< T..., U > > first_arg
Definition mp_utils.hpp:698
Type trait to check if a type is an instantiation of a template.
Definition mp_utils.hpp:67
Compile-time type predicate: true if the evaluated type is the same as T.
Definition mp_utils.hpp:396
consteval bool operator()(W< X >) const
Definition mp_utils.hpp:399
A sequence of types used for metaprogramming operations.
Definition mp_utils.hpp:123