Zrythm v2.0.0-alpha.1
a highly automated and intuitive digital audio workstation
Loading...
Searching...
No Matches
variant_helpers.h
1// SPDX-FileCopyrightText: © 2024-2025 Alexandros Theodotou <alex@zrythm.org>
2// SPDX-License-Identifier: LicenseRef-ZrythmLicense
3
4#pragma once
5
6#include "utils/traits.h"
7
8#include <QObject>
9#include <QVariant>
10
11namespace zrythm::utils
12{
13
14// Primary template (false for non-variant types)
15template <typename T, typename Variant, typename = void>
16struct is_in_variant : std::false_type
17{
18};
19
20// Specialization for std::variant types
21template <typename T, typename... Ts>
23 T,
24 std::variant<Ts...>,
25 std::void_t<decltype (std::variant<Ts...> ())>>
26 : std::bool_constant<(std::is_same_v<T, Ts> || ...)>
27{
28};
29
30// Concept that checks that T is one of the types in Variant
31template <typename T, typename Variant>
33
34namespace detail_test
35{
36using MyVariant = std::variant<int, double, std::string>;
37static_assert (IsInVariant<int, MyVariant>); // Passes
38static_assert (IsInVariant<double, MyVariant>); // Passes
39static_assert (!IsInVariant<float, MyVariant>); // Fails (not in variant)
40static_assert (!IsInVariant<int, int>); // Fails (not a variant)
41}
42
43// Helper struct to merge multiple std::variant types
44template <typename... Variants> struct merge_variants;
45
46// Type alias for easier use of the merge_variants struct
47template <typename... Variants>
48using merge_variants_t = typename merge_variants<Variants...>::type;
49
50// Base case: single variant, no merging needed
51template <typename Variant> struct merge_variants<Variant>
52{
53 using type = Variant;
54};
55
56// Recursive case: merge two variants and continue with the rest
57template <typename... Types1, typename... Types2, typename... Rest>
58struct merge_variants<std::variant<Types1...>, std::variant<Types2...>, Rest...>
59{
60 // Merge the first two variants and recursively merge with the rest
61 using type = merge_variants_t<std::variant<Types1..., Types2...>, Rest...>;
62};
63
64// Usage example:
65// using Variant1 = std::variant<int, float>;
66// using Variant2 = std::variant<char, double>;
67// using MergedVariant = merge_variants_t<Variant1, Variant2>;
68// MergedVariant is now std::variant<int, float, char, double>
69
71template <typename Variant> struct to_reference_variant_impl;
72
74template <typename... Ts> struct to_reference_variant_impl<std::variant<Ts...>>
75{
77 using type = std::variant<std::reference_wrapper<Ts>...>;
78};
79
84template <typename Variant>
86
87// same for const ref
88template <typename Variant> struct to_const_reference_variant_impl;
89template <typename... Ts>
90struct to_const_reference_variant_impl<std::variant<Ts...>>
91{
92 using type = std::variant<std::reference_wrapper<const Ts>...>;
93};
94template <typename Variant>
95using to_const_reference_variant =
97
99template <typename Variant> struct to_unique_ptr_variant_impl;
100
102template <typename... Ts> struct to_unique_ptr_variant_impl<std::variant<Ts...>>
103{
105 using type = std::variant<std::unique_ptr<Ts>...>;
106};
107
112template <typename Variant>
114
116template <typename Variant> struct to_pointer_variant_impl;
117
119template <typename... Ts> struct to_pointer_variant_impl<std::variant<Ts...>>
120{
122 using type = std::variant<std::add_pointer_t<Ts>...>;
123};
124
129template <typename Variant>
131
132namespace detail
133{
134
135template <typename Variant, typename Base, typename CastFn>
136auto
137convert_to_variant_impl (const Base * base_ptr, CastFn &&cast_fn) -> Variant
138{
139 if (!base_ptr)
140 {
141 return Variant{};
142 }
143
144 Variant result{};
145
146 auto trycast = [&]<typename T> () {
147 if (auto derived = cast_fn.template operator()<T> (base_ptr))
148 {
149 result = const_cast<T *> (derived);
150 return true;
151 }
152 return false;
153 };
154
155 auto try_all_casts = [&]<typename... Ts> (std::variant<Ts...> *) {
156 return (trycast.template operator()<std::remove_pointer_t<Ts>> () || ...);
157 };
158
159 if (!try_all_casts (static_cast<Variant *> (nullptr)))
160 {
161 throw std::runtime_error ("convert_to_variant: no matching type");
162 }
163
164 return result;
165}
166} // namespace detail
167
179template <typename Variant, typename Base>
180 requires std::is_pointer_v<std::variant_alternative_t<0, Variant>>
181auto
182convert_to_variant (const Base * base_ptr) -> Variant
183{
184 return detail::convert_to_variant_impl<Variant> (
185 base_ptr,
186 []<typename T> (const Base * p) { return dynamic_cast<const T *> (p); });
187}
188
195template <typename Variant, typename Base>
196 requires std::is_pointer_v<std::variant_alternative_t<0, Variant>>
197 && std::derived_from<Base, QObject>
198auto
199convert_to_variant_qobj (const Base * base_ptr) -> Variant
200{
201 return detail::convert_to_variant_impl<Variant> (
202 base_ptr,
203 []<typename T> (const Base * p) { return qobject_cast<const T *> (p); });
204}
205
215template <class... Ts> struct overload : Ts...
216{
217 using Ts::operator()...;
218};
219
222template <typename Variant, template <typename> class Wrapper>
224
226template <typename... Ts, template <typename> class Wrapper>
227struct wrap_variant_impl<std::variant<Ts...>, Wrapper>
228{
230 using type = std::variant<Wrapper<Ts>...>;
231};
232
239template <typename Variant, template <typename> class Wrapper>
241} // namespace zrythm::utils
String utilities.
auto convert_to_variant(const Base *base_ptr) -> Variant
Converts a base pointer to a variant of pointers using dynamic_cast.
typename to_reference_variant_impl< Variant >::type to_reference_variant
Converts a variant to a variant of reference_wrappers.
auto convert_to_variant_qobj(const Base *base_ptr) -> Variant
Converts a QObject base pointer to a variant of pointers using qobject_cast.
typename to_unique_ptr_variant_impl< Variant >::type to_unique_ptr_variant
Converts a variant to a variant of reference_wrappers.
typename to_pointer_variant_impl< Variant >::type to_pointer_variant
Converts a variant to a variant of pointers.
typename wrap_variant_impl< Variant, Wrapper >::type wrap_variant_t
Converts a variant to a variant where each type is wrapped by Wrapper<T>.
std::variant< std::add_pointer_t< Ts >... > type
The resulting variant type with pointers.
Helper struct to convert a variant to a variant of pointers.
std::variant< std::reference_wrapper< Ts >... > type
The resulting variant type with reference_wrappers.
Helper struct to convert a variant to a variant of references.
std::variant< std::unique_ptr< Ts >... > type
The resulting variant type with reference_wrappers.
Helper struct to convert a variant to a variant of unique_ptr's.
std::variant< Wrapper< Ts >... > type
The resulting variant type with wrapped types.
Helper struct to convert a variant to a variant of ArrangerObjectOwner<T>.