• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #ifdef __cplusplus
20 
21 #include <optional>
22 #include <string>
23 #include <tuple>
24 #include <type_traits>
25 #include <utility>
26 
27 namespace android::audio_utils {
28 
29 // Determine if a type is a specialization of a templated type
30 // Example: is_specialization_v<T, std::vector>
31 template <typename Test, template <typename...> class Ref>
32 struct is_specialization : std::false_type {};
33 
34 template <template <typename...> class Ref, typename... Args>
35 struct is_specialization<Ref<Args...>, Ref> : std::true_type {};
36 
37 template <typename Test, template <typename...> class Ref>
38 inline constexpr bool is_specialization_v = is_specialization<Test, Ref>::value;
39 
40 // For static assert(false) we need a template version to avoid early failure.
41 template <typename T>
42 inline constexpr bool dependent_false_v = false;
43 
44 // Determine the number of arguments required for structured binding.
45 // See the discussion here and follow the links:
46 // https://isocpp.org/blog/2016/08/cpp17-structured-bindings-convert-struct-to-a-tuple-simple-reflection
47 // Example: is_braces_constructible<T, any_type>()
48 struct any_type {
49   template <class T>
50   constexpr operator T();  // non explicit
51 };
52 
53 template <typename T, typename... TArgs>
54 decltype(void(T{std::declval<TArgs>()...}), std::true_type{})
55 test_is_braces_constructible(int);
56 
57 template <typename, typename...>
58 std::false_type test_is_braces_constructible(...);
59 
60 template <typename T, typename... TArgs>
61 using is_braces_constructible =
62     decltype(test_is_braces_constructible<T, TArgs...>(0));
63 
64 template <typename T, typename... Args>
65 constexpr bool is_braces_constructible_v =
66     is_braces_constructible<T, Args...>::value;
67 
68 // Define a concept to check if a class has a member type `Tag` and `getTag()` method
69 template <typename T>
70 concept has_tag_and_get_tag = requires(T t) {
71     { t.getTag() } -> std::same_as<typename T::Tag>;
72 };
73 
74 template <typename T>
75 inline constexpr bool has_tag_and_get_tag_v = has_tag_and_get_tag<T>;
76 
77 /**
78  * Concept to identify primitive types, includes fundamental types, enums, and
79  * std::string.
80  */
81 template <typename T>
82 concept PrimitiveType = std::is_arithmetic_v<T> || std::is_enum_v<T> ||
83                         std::is_same_v<T, std::string>;
84 
85 // Helper to access elements by runtime index
86 template <typename Tuple, typename Func, size_t... Is>
87 void op_tuple_elements_by_index(Tuple&& tuple, size_t index, Func&& func,
88                                 std::index_sequence<Is...>) {
89   ((index == Is ? func(std::get<Is>(tuple)) : void()), ...);
90 }
91 
92 template <typename Tuple, typename Func>
93 void op_tuple_elements(Tuple&& tuple, size_t index, Func&& func) {
94   constexpr size_t tuple_size = std::tuple_size_v<std::decay_t<Tuple>>;
95   op_tuple_elements_by_index(std::forward<Tuple>(tuple), index,
96                              std::forward<Func>(func),
97                              std::make_index_sequence<tuple_size>{});
98 }
99 
100 /**
101  * The maximum structure members supported in the structure.
102  * If this utility is used for a structure with more than `N` members, the
103  * compiler will fail. In that case, `structure_to_tuple` must be extended.
104  *
105  */
106 constexpr size_t kMaxStructMember = 20;
107 
108 /**
109  * @brief Converts a structure to a tuple.
110  *
111  * This function uses structured bindings to decompose the input structure `t`
112  * into individual elements, and then returns a `std::tuple` containing those
113  * elements.
114  *
115  * Example:
116  * ```cpp
117  * struct Point3D {
118  *     int x;
119  *     int y;
120  *     int z;
121  * };
122  * Point3D point{1, 2, 3};
123  * auto tuple = structure_to_tuple(point);  // tuple will be std::make_tuple(1,
124  * 2, 3)
125  * ```
126  *
127  * @tparam T The type of the structure to be converted.
128  * @param t The structure to be converted to a tuple.
129  * @return A `std::tuple` containing all members of the input structure.
130  *
131  * @note The maximum number of members supported in a structure is
132  * `kMaxStructMember`. If the input structure has more than `kMaxStructMember`
133  * members, a compile-time error will occur.
134  */
135 template <typename T>
136 auto structure_to_tuple(const T& t) {
137   if constexpr (is_braces_constructible<
138                     T, any_type, any_type, any_type, any_type, any_type,
139                     any_type, any_type, any_type, any_type, any_type, any_type,
140                     any_type, any_type, any_type, any_type, any_type, any_type,
141                     any_type, any_type, any_type>()) {
142     auto&& [t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15,
143             t16, t17, t18, t19, t20] = t;
144     return std::make_tuple(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12,
145                            t13, t14, t15, t16, t17, t18, t19, t20);
146   } else if constexpr (is_braces_constructible<
147                            T, any_type, any_type, any_type, any_type, any_type,
148                            any_type, any_type, any_type, any_type, any_type,
149                            any_type, any_type, any_type, any_type, any_type,
150                            any_type, any_type, any_type, any_type>()) {
151     auto&& [t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15,
152             t16, t17, t18, t19] = t;
153     return std::make_tuple(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12,
154                            t13, t14, t15, t16, t17, t18, t19);
155   } else if constexpr (is_braces_constructible<
156                            T, any_type, any_type, any_type, any_type, any_type,
157                            any_type, any_type, any_type, any_type, any_type,
158                            any_type, any_type, any_type, any_type, any_type,
159                            any_type, any_type, any_type>()) {
160     auto&& [t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15,
161             t16, t17, t18] = t;
162     return std::make_tuple(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12,
163                            t13, t14, t15, t16, t17, t18);
164   } else if constexpr (is_braces_constructible<
165                            T, any_type, any_type, any_type, any_type, any_type,
166                            any_type, any_type, any_type, any_type, any_type,
167                            any_type, any_type, any_type, any_type, any_type,
168                            any_type, any_type>()) {
169     auto&& [t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15,
170             t16, t17] = t;
171     return std::make_tuple(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12,
172                            t13, t14, t15, t16, t17);
173   } else if constexpr (is_braces_constructible<
174                            T, any_type, any_type, any_type, any_type, any_type,
175                            any_type, any_type, any_type, any_type, any_type,
176                            any_type, any_type, any_type, any_type, any_type,
177                            any_type>()) {
178     auto&& [t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15,
179             t16] = t;
180     return std::make_tuple(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12,
181                            t13, t14, t15, t16);
182   } else if constexpr (is_braces_constructible<
183                            T, any_type, any_type, any_type, any_type, any_type,
184                            any_type, any_type, any_type, any_type, any_type,
185                            any_type, any_type, any_type, any_type,
186                            any_type>()) {
187     auto&& [t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14, t15] =
188         t;
189     return std::make_tuple(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12,
190                            t13, t14, t15);
191   } else if constexpr (is_braces_constructible<
192                            T, any_type, any_type, any_type, any_type, any_type,
193                            any_type, any_type, any_type, any_type, any_type,
194                            any_type, any_type, any_type, any_type>()) {
195     auto&& [t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13, t14] = t;
196     return std::make_tuple(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12,
197                            t13, t14);
198   } else if constexpr (is_braces_constructible<
199                            T, any_type, any_type, any_type, any_type, any_type,
200                            any_type, any_type, any_type, any_type, any_type,
201                            any_type, any_type, any_type>()) {
202     auto&& [t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12, t13] = t;
203     return std::make_tuple(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12,
204                            t13);
205   } else if constexpr (is_braces_constructible<
206                            T, any_type, any_type, any_type, any_type, any_type,
207                            any_type, any_type, any_type, any_type, any_type,
208                            any_type, any_type>()) {
209     auto&& [t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12] = t;
210     return std::make_tuple(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11, t12);
211   } else if constexpr (is_braces_constructible<T, any_type, any_type, any_type,
212                                                any_type, any_type, any_type,
213                                                any_type, any_type, any_type,
214                                                any_type, any_type>()) {
215     auto&& [t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11] = t;
216     return std::make_tuple(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10, t11);
217   } else if constexpr (is_braces_constructible<T, any_type, any_type, any_type,
218                                                any_type, any_type, any_type,
219                                                any_type, any_type, any_type,
220                                                any_type>()) {
221     auto&& [t1, t2, t3, t4, t5, t6, t7, t8, t9, t10] = t;
222     return std::make_tuple(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10);
223   } else if constexpr (is_braces_constructible<
224                            T, any_type, any_type, any_type, any_type, any_type,
225                            any_type, any_type, any_type, any_type>()) {
226     auto&& [t1, t2, t3, t4, t5, t6, t7, t8, t9] = t;
227     return std::make_tuple(t1, t2, t3, t4, t5, t6, t7, t8, t9);
228   } else if constexpr (is_braces_constructible<T, any_type, any_type, any_type,
229                                                any_type, any_type, any_type,
230                                                any_type, any_type>()) {
231     auto&& [t1, t2, t3, t4, t5, t6, t7, t8] = t;
232     return std::make_tuple(t1, t2, t3, t4, t5, t6, t7, t8);
233   } else if constexpr (is_braces_constructible<T, any_type, any_type, any_type,
234                                                any_type, any_type, any_type,
235                                                any_type>()) {
236     auto&& [t1, t2, t3, t4, t5, t6, t7] = t;
237     return std::make_tuple(t1, t2, t3, t4, t5, t6, t7);
238   } else if constexpr (is_braces_constructible<T, any_type, any_type, any_type,
239                                                any_type, any_type,
240                                                any_type>()) {
241     auto&& [t1, t2, t3, t4, t5, t6] = t;
242     return std::make_tuple(t1, t2, t3, t4, t5, t6);
243   } else if constexpr (is_braces_constructible<T, any_type, any_type, any_type,
244                                                any_type, any_type>()) {
245     auto&& [t1, t2, t3, t4, t5] = t;
246     return std::make_tuple(t1, t2, t3, t4, t5);
247   } else if constexpr (is_braces_constructible<T, any_type, any_type, any_type,
248                                                any_type>()) {
249     auto&& [t1, t2, t3, t4] = t;
250     return std::make_tuple(t1, t2, t3, t4);
251   } else if constexpr (is_braces_constructible<T, any_type, any_type,
252                                                any_type>()) {
253     auto&& [t1, t2, t3] = t;
254     return std::make_tuple(t1, t2, t3);
255   } else if constexpr (is_braces_constructible<T, any_type, any_type>()) {
256     auto&& [t1, t2] = t;
257     return std::make_tuple(t1, t2);
258   } else if constexpr (is_braces_constructible<T, any_type>()) {
259     auto&& [t1] = t;
260     return std::make_tuple(t1);
261   } else {
262     static_assert(false, "Currently supports up to 20 members only.");
263   }
264 }
265 
266 /**
267  * @brief Applies a ternary operation element-wise to structs of type T and
268  * transform the results into a new struct of the same type.
269  *
270  * This function template takes three structs of the same type `T` and an
271  * ternary operation `op`, then applies `op` element-wise to the corresponding
272  * members of the input structs. If all the results of `op` are valid (i.e., not
273  * `std::nullopt`), it constructs a new struct `T` with these results and
274  * returns it as `std::optional<T>`. Otherwise, it returns `std::nullopt`.
275  *
276  * @example
277  * struct Point3D {
278  *   int x;
279  *   int y;
280  *   int z;
281  * };
282  *
283  * std::vector<int> v1{10, 2, 1};
284  * std::vector<int> v2{4, 5, 8};
285  * std::vector<int> v3{1, 8, 6};
286  *
287  * const auto addOp3 = [](const auto& a, const auto& b, const auto& c) {
288  *   return {a + b + c};
289  * };
290  * auto result = op_aggregate(addOp3, v1, v2, v3);
291  * The value of `result` will be std::vector<int>({15, 15, 15})
292  *
293  * @tparam TernaryOp The ternary operation apply to parameters `a`, `b`, and
294  * `c`.
295  * @tparam T The type of `a`, `b`, and `c`.
296  * @param op The ternary operation to apply.
297  * @param a The first input struct.
298  * @param b The second input struct.
299  * @param c The third input struct.
300  * @return A new struct with the aggregated results if all results are valid,
301  *         `std::nullopt` otherwise.
302  */
303 template <typename TernaryOp, typename T, size_t... Is>
304 std::optional<T> op_aggregate_helper(TernaryOp op, const T& a, const T& b,
305                                      const T& c, std::index_sequence<Is...>) {
306   const auto aTuple = structure_to_tuple<T>(a);
307   const auto bTuple = structure_to_tuple<T>(b);
308   const auto cTuple = structure_to_tuple<T>(c);
309 
310   T result;
311   auto resultTuple = structure_to_tuple<T>(result);
312 
313   bool success = true;
314   (
315       [&]() {
316         if (!success) return;  // stop op with any previous error
317         auto val = op(std::get<Is>(aTuple), std::get<Is>(bTuple),
318                       std::get<Is>(cTuple));
319         if (!val) {
320           success = false;
321           return;
322         }
323         std::get<Is>(resultTuple) = *val;
324       }(),
325       ...);
326 
327   if (!success) {
328     return std::nullopt;
329   }
330 
331   return std::apply([](auto&&... args) { return T{args...}; }, resultTuple);
332 }
333 
334 template <typename TernaryOp, typename T>
335 std::optional<T> op_aggregate(TernaryOp op, const T& a, const T& b,
336                               const T& c) {
337   constexpr size_t tuple_size =
338       std::tuple_size_v<std::decay_t<decltype(structure_to_tuple(a))>>;
339   return op_aggregate_helper<TernaryOp, T>(
340       op, a, b, c, std::make_index_sequence<tuple_size>{});
341 }
342 
343 /**
344  * @brief Applies a binary operation element-wise to structs of type T and
345  * transform the results into a new struct of the same type.
346  *
347  * This function template takes three structs of the same type `T` and an
348  * operation `op`, and applies `op` element-wise to the corresponding members of
349  * the input structs. If all the results of `op` are valid (i.e., not
350  * `std::nullopt`), it constructs a new struct `T` with these results and
351  * returns it as `std::optional<T>`. Otherwise, it returns `std::nullopt`.
352  *
353  * @example
354  * struct Point3D {
355  *   int x;
356  *   int y;
357  *   int z;
358  * };
359  *
360  * std::vector<int> v1{10, 2, 1};
361  * std::vector<int> v2{4, 5, 8};
362  * const auto addOp2 = [](const auto& a, const auto& b) {
363  *   return {a + b};
364  * };
365  * auto result = op_aggregate(addOp2, v1, v2);
366  * The value of `result` will be std::vector<int>({14, 7, 9})
367  *
368  * @tparam BinaryOp The binary operation to apply to parameters `a` and `b`.
369  * @tparam T The type of `a` and `b`.
370  * @param op The ternary operation to apply.
371  * @param a The first input struct.
372  * @param b The second input struct.
373  * @return A new struct with the aggregated results if all results are valid,
374  *         `std::nullopt` otherwise.
375  */
376 template <typename BinaryOp, typename T, size_t... Is>
377 std::optional<T> op_aggregate_helper(BinaryOp op, const T& a, const T& b,
378                                      std::index_sequence<Is...>) {
379   const auto aTuple = structure_to_tuple<T>(a);
380   const auto bTuple = structure_to_tuple<T>(b);
381 
382   T result;
383   auto resultTuple = structure_to_tuple<T>(result);
384 
385   bool success = true;
386   (
387       [&]() {
388         if (!success) return;  // stop op with any previous error
389         auto val = op(std::get<Is>(aTuple), std::get<Is>(bTuple));
390         if (!val) {
391           success = false;
392           return;
393         }
394         std::get<Is>(resultTuple) = *val;
395       }(),
396       ...);
397 
398   if (!success) {
399     return std::nullopt;
400   }
401 
402   return std::apply([](auto&&... args) { return T{args...}; }, resultTuple);
403 }
404 
405 template <typename BinaryOp, typename T>
406 std::optional<T> op_aggregate(BinaryOp op, const T& a, const T& b) {
407   constexpr size_t tuple_size =
408       std::tuple_size_v<std::decay_t<decltype(structure_to_tuple(a))>>;
409   return op_aggregate_helper<BinaryOp, T>(
410       op, a, b, std::make_index_sequence<tuple_size>{});
411 }
412 
413 }  // namespace android::audio_utils
414 
415 #endif  // __cplusplus
416