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