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