Zrythm v2.0.0-DEV
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 <QVariant>
9
10// Primary template (false for non-variant types)
11template <typename T, typename Variant, typename = void>
12struct is_in_variant : std::false_type
13{
14};
15
16// Specialization for std::variant types
17template <typename T, typename... Ts>
19 T,
20 std::variant<Ts...>,
21 std::void_t<decltype (std::variant<Ts...> ())>>
22 : std::bool_constant<(std::is_same_v<T, Ts> || ...)>
23{
24};
25
26// Concept that checks that T is one of the types in Variant
27template <typename T, typename Variant>
29
30namespace detail_test
31{
32using MyVariant = std::variant<int, double, std::string>;
33static_assert (IsInVariant<int, MyVariant>); // Passes
34static_assert (IsInVariant<double, MyVariant>); // Passes
35static_assert (!IsInVariant<float, MyVariant>); // Fails (not in variant)
36static_assert (!IsInVariant<int, int>); // Fails (not a variant)
37}
38
39// Helper struct to merge multiple std::variant types
40template <typename... Variants> struct merge_variants;
41
42// Type alias for easier use of the merge_variants struct
43template <typename... Variants>
44using merge_variants_t = typename merge_variants<Variants...>::type;
45
46// Base case: single variant, no merging needed
47template <typename Variant> struct merge_variants<Variant>
48{
49 using type = Variant;
50};
51
52// Recursive case: merge two variants and continue with the rest
53template <typename... Types1, typename... Types2, typename... Rest>
54struct merge_variants<std::variant<Types1...>, std::variant<Types2...>, Rest...>
55{
56 // Merge the first two variants and recursively merge with the rest
57 using type = merge_variants_t<std::variant<Types1..., Types2...>, Rest...>;
58};
59
60// Usage example:
61// using Variant1 = std::variant<int, float>;
62// using Variant2 = std::variant<char, double>;
63// using MergedVariant = merge_variants_t<Variant1, Variant2>;
64// MergedVariant is now std::variant<int, float, char, double>
65
67template <typename Variant> struct to_reference_variant_impl;
68
70template <typename... Ts> struct to_reference_variant_impl<std::variant<Ts...>>
71{
73 using type = std::variant<std::reference_wrapper<Ts>...>;
74};
75
80template <typename Variant>
81using to_reference_variant = typename to_reference_variant_impl<Variant>::type;
82
83// same for const ref
84template <typename Variant> struct to_const_reference_variant_impl;
85template <typename... Ts>
86struct to_const_reference_variant_impl<std::variant<Ts...>>
87{
88 using type = std::variant<std::reference_wrapper<const Ts>...>;
89};
90template <typename Variant>
91using to_const_reference_variant =
93
95template <typename Variant> struct to_unique_ptr_variant_impl;
96
98template <typename... Ts> struct to_unique_ptr_variant_impl<std::variant<Ts...>>
99{
101 using type = std::variant<std::unique_ptr<Ts>...>;
102};
103
108template <typename Variant>
109using to_unique_ptr_variant = typename to_unique_ptr_variant_impl<Variant>::type;
110
112template <typename Variant> struct to_pointer_variant_impl;
113
115template <typename... Ts> struct to_pointer_variant_impl<std::variant<Ts...>>
116{
118 using type = std::variant<std::add_pointer_t<Ts>...>;
119};
120
125template <typename Variant>
126using to_pointer_variant = typename to_pointer_variant_impl<Variant>::type;
127
142template <typename Variant, typename Base>
143 requires std::is_pointer_v<std::variant_alternative_t<0, Variant>>
144auto
145convert_to_variant (const Base * base_ptr) -> Variant
146{
147 if (!base_ptr)
148 {
149 return Variant{};
150 }
151
152 Variant result{};
153
154 auto trycast = [&]<typename T> () {
155 if (auto derived = dynamic_cast<const T *> (base_ptr))
156 {
157 result = const_cast<T *> (derived);
158 return true;
159 }
160 return false;
161 };
162
163 auto try_all_casts = [&]<typename... Ts> (std::variant<Ts...> *) {
164 return (trycast.template operator()<std::remove_pointer_t<Ts>> () || ...);
165 };
166
167 try_all_casts (static_cast<Variant *> (nullptr));
168
169 return result;
170}
171
181template <class... Ts> struct overload : Ts...
182{
183 using Ts::operator()...;
184};
185
188template <typename Variant, template <typename> class Wrapper>
190
192template <typename... Ts, template <typename> class Wrapper>
193struct wrap_variant_impl<std::variant<Ts...>, Wrapper>
194{
196 using type = std::variant<Wrapper<Ts>...>;
197};
198
205template <typename Variant, template <typename> class Wrapper>
206using wrap_variant_t = typename wrap_variant_impl<Variant, Wrapper>::type;
Overload pattern.
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>.