Zrythm v2.0.0-DEV
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
traits.h
1// SPDX-FileCopyrightText: © 2024-2025 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include <array>
7#include <memory>
8#include <optional>
9#include <ranges>
10#include <type_traits>
11#include <variant>
12#include <vector>
13
14#include <type_safe/strong_typedef.hpp>
15
16// Helper to detect if DerivedT inherits from any specialization of BaseTemplateT
17template <template <typename...> class BaseTemplateT, typename DerivedT>
19{
20 template <typename... Args>
21 static constexpr std::true_type
22 internal_test (const BaseTemplateT<Args...> *);
23
24 static constexpr std::false_type internal_test (...);
25
26 static constexpr bool value =
27 decltype (internal_test (std::declval<DerivedT *> ()))::value;
28};
29
30// Convenience variable template (C++17+)
31template <template <typename...> class BaseTemplateT, typename DerivedT>
32inline constexpr bool is_derived_from_template_v =
33 is_derived_from_template<BaseTemplateT, DerivedT>::value;
34
35namespace internal_test
36{
37template <typename T> class MyTemplateBase
38{
39};
40template <typename T> class OtherTemplateBase
41{
42};
44{
45};
46class Derived : public MyTemplateBase<int>
47{
48};
49
50static_assert (is_derived_from_template_v<MyTemplateBase, Derived>);
51static_assert (!is_derived_from_template_v<MyTemplateBase, int>);
52static_assert (!is_derived_from_template_v<MyTemplateBase, OtherBase>);
53static_assert (!is_derived_from_template_v<OtherTemplateBase, Derived>);
54}
55
56// Helper concept to check if a type is a container
57template <typename T>
58concept ContainerType = requires (T t) {
59 typename T::value_type;
60 typename T::iterator;
61 typename T::const_iterator;
62 { t.begin () } -> std::same_as<typename T::iterator>;
63 { t.end () } -> std::same_as<typename T::iterator>;
64 { t.size () } -> std::convertible_to<std::size_t>;
65};
66
67// Dependent false (for preventing static_assertions from being executed before
68// template instantiations)
69template <typename...> struct dependent_false : std::false_type
70{
71};
72
73template <typename... Ts>
74using dependent_false_t = typename dependent_false<Ts...>::type;
75
76template <typename... Ts>
77constexpr auto dependent_false_v = dependent_false<Ts...>::value;
78
79// Concept that checks if a type is a final class
80template <typename T>
81concept FinalClass = std::is_final_v<T> && std::is_class_v<T>;
82
83template <typename T>
84concept CompleteType = requires { sizeof (T); };
85
86// Concept to check if a type is a raw pointer
87template <typename T>
88concept IsRawPointer = std::is_pointer_v<T>;
89
90template <typename T>
91concept StdVariant = requires {
92 requires requires (T v) {
93 []<typename... Vs> (std::variant<Vs...> &) { }(v);
94 };
95};
97static_assert (StdVariant<std::variant<>>);
98static_assert (!StdVariant<float>);
99
100// Concept to check if a type is a variant of pointers (allows std::nullptr_t)
101template <typename T>
102concept VariantOfPointers = requires (T v) {
103 requires StdVariant<T>;
104 []<typename... Vs> (std::variant<Vs...> &)
105 requires (std::is_pointer_v<Vs> && ...)
106 { }(v);
107};
108
111
112template <typename T>
113concept OptionalType = requires {
114 typename T::value_type;
115 requires std::same_as<T, std::optional<typename T::value_type>>;
116};
117
118template <typename T>
119concept StrongTypedef = requires (T t) { typename T::strong_typedef; };
120
121// Primary template
122template <typename T> struct remove_smart_pointer
123{
124 using type = T;
125};
126
127// Specialization for std::unique_ptr
128template <typename T, typename Deleter>
129struct remove_smart_pointer<std::unique_ptr<T, Deleter>>
130{
131 using type = T;
132};
133
134// Specialization for std::shared_ptr
135template <typename T> struct remove_smart_pointer<std::shared_ptr<T>>
136{
137 using type = T;
138};
139
140// Specialization for std::weak_ptr
141template <typename T> struct remove_smart_pointer<std::weak_ptr<T>>
142{
143 using type = T;
144};
145
146// Helper alias template (C++11 and later)
147template <typename T>
148using remove_smart_pointer_t = typename remove_smart_pointer<T>::type;
149
152template <typename T>
153using base_type = std::remove_cvref_t<
154 remove_smart_pointer_t<std::remove_pointer_t<std::decay_t<T>>>>;
155static_assert (std::is_same_v<base_type<int * const &>, int>);
156static_assert (std::is_same_v<base_type<std::shared_ptr<int> const &>, int>);
157
158template <typename T>
159concept StdArray = requires {
160 typename std::array<typename T::value_type, std::tuple_size<T>::value>;
161 requires std::same_as<
162 T, std::array<typename T::value_type, std::tuple_size<T>::value>>;
163};
165static_assert (!StdArray<std::vector<float>>);
166
167template <typename T> struct is_unique_ptr : std::false_type
168{
169};
170template <typename T> struct is_unique_ptr<std::unique_ptr<T>> : std::true_type
171{
172};
173template <typename T> constexpr bool is_unique_ptr_v = is_unique_ptr<T>::value;
174
175static_assert (is_unique_ptr_v<std::unique_ptr<int>>);
176static_assert (!is_unique_ptr_v<std::shared_ptr<int>>);
177
178template <typename T> struct is_shared_ptr : std::false_type
179{
180};
181template <typename T> struct is_shared_ptr<std::shared_ptr<T>> : std::true_type
182{
183};
184template <typename T> constexpr bool is_shared_ptr_v = is_shared_ptr<T>::value;
185
186template <typename T>
187concept SmartPtr = is_unique_ptr_v<T> || is_shared_ptr_v<T>;
188
189template <typename Derived, typename Base>
191 std::derived_from<Derived, Base> && !std::same_as<Derived, Base>;
192
193template <typename Derived, template <typename> class BaseTemplate>
194concept DerivedFromCRTPBase = std::is_base_of_v<BaseTemplate<Derived>, Derived>;
195
196namespace detail_test
197{
198template <typename T> class CRTPBase
199{
200 CRTPBase () = default;
201 friend T;
202};
203class Derived : public CRTPBase<Derived>
204{
205};
207{
208};
209class OtherNonDerived : public CRTPBase<NonDerived>
210{
211};
215}; // namespace detail_test
216
217// Trick to print the tparam type during compilation
218// i.e.: No type named 'something_made_up' in 'tparam'
219#define DEBUG_TEMPLATE_PARAM(tparam) \
220 [[maybe_unused]] typedef typename tparam::something_made_up X;
221
222template <typename R, typename T>
223concept RangeOf =
224 std::ranges::range<R>
225 && (std::derived_from<std::ranges::range_value_t<R>, T> ||
226 // handle pointers too
227 (IsRawPointer<std::ranges::range_value_t<R>> && IsRawPointer<T> && std::derived_from<std::remove_pointer_t<std::ranges::range_value_t<R>>, std::remove_pointer_t<T>>) );
228
229namespace detail
230{
231struct build_test_type
232{
233 template <typename... Args>
234 build_test_type (Args &&...) { } // Accept any constructor arguments
235};
236}
240template <typename T>
241concept ObjectBuilder = requires (T t) {
242 {
243 t.template build<detail::build_test_type> ()
244 } -> std::same_as<std::unique_ptr<detail::build_test_type>>;
245};
246namespace object_builder_test
247{
249{
250 template <typename T> auto build () { return std::make_unique<T> (); }
251};
253{
254 template <typename T> void build () { }
255};
256static_assert (ObjectBuilder<ValidBuilder>);
257static_assert (!ObjectBuilder<InvalidBuilder>);
258};
259
260template <typename T>
261concept EnumType = std::is_enum_v<T>;
Concept that checks if a type is a builder for objects.
Definition traits.h:241