C++ Library Extensions 2022.12.09
To help learn modern C++ programming
cpg_tuple_operations.hpp
Go to the documentation of this file.
1 /*
2 Author: Thomas Kim
3 First Edit: Dec. 03, 2021
4 Second Edit: Dec. 07, 2021
5 Second Edit: Dec. 09, 2021 - safe binary operation
6*/
7
8#ifndef _CGP_TUPLE_OPERATIONS_HPP
9#define _CGP_TUPLE_OPERATIONS_HPP
10
11#ifndef NOMINMAX
12#define NOMINMAX
13#endif
14
15#include "cpg_types.hpp"
16
17namespace std::inline stl_extensions
18{
19 namespace cgt = cpg::types;
20
21 namespace hidden
22 {
23 template<bool bSigned, cgt::numerical_c Left, cgt::numerical_c Right>
24 constexpr auto tuple_common_elements(Left, Right) noexcept
25 {
26 if constexpr(bSigned)
28 else
30 }
31
32 template<bool bSigned = true, typename... Elements, cgt::non_tuple_c Scalar>
33 constexpr auto tuple_common_elements(std::tuple<Elements...>, Scalar) noexcept
34 {
35 return std::tuple{ tuple_common_elements<bSigned>( Elements{}, Scalar{}) ... };
36 }
37
38 template<bool bSigned = true, typename T, std::size_t N, cgt::numerical_c Scalar>
39 constexpr auto tuple_common_elements(std::array<T, N>, Scalar) noexcept
40 {
41 return std::array<cgt::common_signed_t<T, Scalar>, N>{};
42 }
43
44 template<bool bSigned = true, typename T, std::size_t N, cgt::numerical_c Scalar>
45 constexpr auto tuple_common_elements(Scalar, std::array<T, N>) noexcept
46 {
47 return std::array<cgt::common_signed_t<T, Scalar>, N>{};
48 }
49
50 template<bool bSigned = true, typename T, cgt::numerical_c Scalar>
51 constexpr auto tuple_common_elements(std::vector<T>, Scalar) noexcept
52 {
53 return std::vector<cgt::common_signed_t<T, Scalar>>{};
54 }
55
56 template<bool bSigned = true, typename T, cgt::numerical_c Scalar>
57 constexpr auto tuple_common_elements(Scalar, std::vector<T>) noexcept
58 {
59 return std::vector<cgt::common_signed_t<T, Scalar>>{};
60 }
61
62 template<bool bSigned = true, typename... Lefts, typename... Rights>
63 requires( sizeof...(Lefts) == sizeof...(Rights) )
64 constexpr auto tuple_common_elements(std::tuple<Lefts...>, std::tuple<Rights...>) noexcept
65 {
66 return std::tuple{ tuple_common_elements<bSigned>( Lefts{}, Rights{}) ... };
67 }
68
69 // signed common
70 template<typename... Lefts, typename... Rights>
71 requires( sizeof...(Lefts) == sizeof...(Rights) )
72 constexpr auto operator|(std::tuple<Lefts...>, std::tuple<Rights...>) noexcept
73 {
74 return std::tuple{ tuple_common_elements<true>( Lefts{}, Rights{} )... };
75 }
76
77 template<typename... Elements, cgt::non_tuple_c Scalar>
78 constexpr auto operator|(std::tuple<Elements...>, Scalar) noexcept
79 {
80 using c_t = std::tuple<Elements...>;
81 return std::tuple{ tuple_common_elements<true>( Elements{}, Scalar{} )... };
82 }
83
84 template<typename... Elements, cgt::non_tuple_c Scalar>
85 constexpr auto operator|(Scalar, std::tuple<Elements...>) noexcept
86 {
87 using c_t = std::tuple<Elements...>;
88 return std::tuple{ tuple_common_elements<true>( Scalar{}, Elements{} )... };
89 }
90
91 // unsigned common
92 template<typename... Lefts, typename... Rights>
93 requires( sizeof...(Lefts) == sizeof...(Rights) )
94 constexpr auto operator||(std::tuple<Lefts...>, std::tuple<Rights...>) noexcept
95 {
96 return std::tuple{ tuple_common_elements<false>( Lefts{}, Rights{} )... };
97 }
98
99 template<typename... Elements, cgt::non_tuple_c Scalar>
100 constexpr auto operator||(std::tuple<Elements...>, Scalar) noexcept
101 {
102 using c_t = std::tuple<Elements...>;
103 return std::tuple{ tuple_common_elements<false>( Elements{}, Scalar{} )... };
104 }
105 }
106 // end of namespace hidde
107
108 template<typename T, typename... Tails>
109 constexpr auto signed_tuple_operation(T arg, Tails... args) noexcept
110 {
111 using namespace hidden;
112 return (arg | ... | args);
113 }
114
115 template<typename T, typename... Tails>
116 constexpr auto unsigned_tuple_operation(T arg, Tails... args) noexcept
117 {
118 using namespace hidden;
119 return (arg || ... || args);
120 }
121
122 template<cgt::tuple_flat_c LeftType, cgt::tuple_flat_c RightType>
123 requires( std::tuple_size_v<std::remove_cvref_t<LeftType>>
124 == std::tuple_size_v<std::remove_cvref_t<RightType>>)
125 constexpr decltype(auto) operator+(LeftType&& A, RightType&& B) noexcept(!cpg::bDetectOverFlow)
126 {
127 using container_a_t = std::remove_cvref_t<LeftType>;
128 using container_b_t = std::remove_cvref_t<RightType>;
129
130 if constexpr(std::same_as<container_a_t, container_b_t> &&
131 std::is_rvalue_reference_v<decltype(A)>)
132 {
133 cgt::for_workhorse(A, [&](auto i, auto&& a)
134 {
135 a = cgt::sbo(a) + cgt::sbo(std::get<i.value>(B));
136 });
137
138 return std::move(A);
139 }
140 else if constexpr(std::same_as<container_a_t, container_b_t> &&
141 std::is_rvalue_reference_v<decltype(B)>)
142 {
143 cgt::for_workhorse(B, [&](auto i, auto&& b)
144 {
145 b = cgt::sbo(b) + cgt::sbo(std::get<i.value>(A));
146 });
147
148 return std::move(B);
149 }
150 else
151 {
152 constexpr std::size_t N = std::tuple_size_v<container_a_t>;
153 return cgt::for_stallion<N>([&]<auto... i>(cgt::sequence<i...>)
154 {
155 auto opr = []<typename L, typename R>(L& a, R& b)
156 {
157 return cgt::sbo(a) + cgt::sbo(b);
158 };
159
160 return std::tuple{ opr(std::get<i>(A), std::get<i>(B)) ... };
161 });
162 }
163 }
164
165 template<cgt::tuple_flat_c LeftType, cgt::tuple_flat_c RightType>
166 requires( std::tuple_size_v<std::remove_cvref_t<LeftType>>
167 == std::tuple_size_v<std::remove_cvref_t<RightType>>)
168 constexpr decltype(auto) operator-(LeftType&& A, RightType&& B) noexcept(!cpg::bDetectOverFlow)
169 {
170 using container_a_t = std::remove_cvref_t<LeftType>;
171 using container_b_t = std::remove_cvref_t<RightType>;
172
173 if constexpr(std::same_as<container_a_t, container_b_t> &&
174 std::is_rvalue_reference_v<decltype(A)>)
175 {
176 cgt::for_workhorse(A, [&](auto i, auto&& a)
177 {
178 a = cgt::sbo(a) - cgt::sbo(std::get<i.value>(B));
179 });
180
181 return std::move(A);
182 }
183 else if constexpr(std::same_as<container_a_t, container_b_t> &&
184 std::is_rvalue_reference_v<decltype(B)>)
185 {
186 cgt::for_workhorse(B, [&](auto i, auto&& b)
187 {
188 b = cgt::sbo(b) - cgt::sbo(std::get<i.value>(A));
189 });
190
191 return std::move(B);
192 }
193 else
194 {
195 constexpr std::size_t N = std::tuple_size_v<container_a_t>;
196 return cgt::for_stallion<N>([&]<auto... i>(cgt::sequence<i...>)
197 {
198 auto opr = []<typename L, typename R>(L& a, R& b)
199 {
200 return cgt::sbo(a) - cgt::sbo(b);
201 };
202
203 return std::tuple{ opr(std::get<i>(A), std::get<i>(B)) ... };
204 });
205 }
206 }
207
208 template<cgt::tuple_flat_c LeftType, cgt::tuple_flat_c RightType>
209 requires( std::tuple_size_v<std::remove_cvref_t<LeftType>>
210 == std::tuple_size_v<std::remove_cvref_t<RightType>>)
211 constexpr decltype(auto) operator*(LeftType&& A, RightType&& B) noexcept(!cpg::bDetectOverFlow)
212 {
213 using container_a_t = std::remove_cvref_t<LeftType>;
214 using container_b_t = std::remove_cvref_t<RightType>;
215
216 if constexpr(std::same_as<container_a_t, container_b_t> &&
217 std::is_rvalue_reference_v<decltype(A)>)
218 {
219 cgt::for_workhorse(A, [&](auto i, auto&& a)
220 {
221 a = cgt::sbo(a) * cgt::sbo(std::get<i.value>(B));
222 });
223
224 return std::move(A);
225 }
226 else if constexpr(std::same_as<container_a_t, container_b_t> &&
227 std::is_rvalue_reference_v<decltype(B)>)
228 {
229 cgt::for_workhorse(B, [&](auto i, auto&& b)
230 {
231 b = cgt::sbo(b) * cgt::sbo(std::get<i.value>(A));
232 });
233
234 return std::move(B);
235 }
236 else
237 {
238 constexpr std::size_t N = std::tuple_size_v<container_a_t>;
239 return cgt::for_stallion<N>([&]<auto... i>(cgt::sequence<i...>)
240 {
241 auto opr = []<typename L, typename R>(L& a, R& b)
242 {
243 return cgt::sbo(a) * cgt::sbo(b);
244 };
245
246 return std::tuple{ opr(std::get<i>(A), std::get<i>(B)) ... };
247 });
248 }
249 }
250
251 template<cgt::tuple_flat_c LeftType, cgt::tuple_flat_c RightType>
252 requires( std::tuple_size_v<std::remove_cvref_t<LeftType>>
253 == std::tuple_size_v<std::remove_cvref_t<RightType>>)
254 constexpr decltype(auto) operator/(LeftType&& A, RightType&& B) noexcept(!cpg::bDetectOverFlow)
255 {
256 using container_a_t = std::remove_cvref_t<LeftType>;
257 using container_b_t = std::remove_cvref_t<RightType>;
258
259 if constexpr(std::same_as<container_a_t, container_b_t> &&
260 std::is_rvalue_reference_v<decltype(A)>)
261 {
262 cgt::for_workhorse(A, [&](auto i, auto&& a)
263 {
264 a = cgt::sbo(a) / cgt::sbo(std::get<i.value>(B));
265 });
266
267 return std::move(A);
268 }
269 else if constexpr(std::same_as<container_a_t, container_b_t> &&
270 std::is_rvalue_reference_v<decltype(B)>)
271 {
272 cgt::for_workhorse(B, [&](auto i, auto&& b)
273 {
274 b = cgt::sbo(b) / cgt::sbo(std::get<i.value>(A));
275 });
276
277 return std::move(B);
278 }
279 else
280 {
281 constexpr std::size_t N = std::tuple_size_v<container_a_t>;
282 return cgt::for_stallion<N>([&]<auto... i>(cgt::sequence<i...>)
283 {
284 auto opr = []<typename L, typename R>(L& a, R& b)
285 {
286 return cgt::sbo(a) / cgt::sbo(b);
287 };
288
289 return std::tuple{ opr(std::get<i>(A), std::get<i>(B)) ... };
290 });
291 }
292 }
293
295
296 namespace hidden
297 {
298 template<typename T, typename S>
300 {
302 };
303
304 template<typename... Types, cgt::non_tuple_c ScalarType>
305 struct st_tuple_scalar_signed_cast<std::tuple<Types...>, ScalarType>
306 {
307 using type = ScalarType;
308 };
309
310 template<typename T, typename S>
312 {
314 };
315
316 template<typename... Types, cgt::non_tuple_c ScalarType>
317 struct st_tuple_scalar_unsigned_cast<std::tuple<Types...>, ScalarType>
318 {
319 using type = ScalarType;
320 };
321 }
322 // end of namespace hidden
323
324 template<typename T, typename S>
326
327 template<typename T, typename S>
329
330 template<cgt::tuple_flat_c TupleType, cgt::numerical_c ScalarType>
331 constexpr decltype(auto) operator+(TupleType&& A, ScalarType scalar) noexcept(!cpg::bDetectOverFlow)
332 {
333 using container_t = std::remove_cvref_t<TupleType>;
334 using c_t = decltype(signed_tuple_operation(A, scalar));
335
336 if constexpr(std::same_as<container_t, c_t>
337 && std::is_rvalue_reference_v<decltype(A)>)
338 {
339 cgt::for_workhorse(A, [&](auto i, auto&& a)
340 {
341 a = cgt::sbo(a) + cgt::sbo(scalar);
342 });
343
344 return std::move(A);
345 }
346 else
347 {
348 constexpr std::size_t N = std::tuple_size_v<container_t>;
349 return cgt::for_stallion<N>([&]<auto... i>(cgt::sequence<i...>)
350 {
351 return c_t{ cgt::sbo(std::get<i>(A)) + cgt::sbo(scalar)... };
352 });
353 }
354 }
355
356 template<cgt::tuple_flat_c TupleType, cgt::numerical_c ScalarType>
357 constexpr decltype(auto) operator+(ScalarType scalar, TupleType&& A) noexcept(!cpg::bDetectOverFlow)
358 {
359 using container_t = std::remove_cvref_t<TupleType>;
360 using c_t = decltype(signed_tuple_operation(A, scalar));
361
362 if constexpr(std::same_as<container_t, c_t>
363 && std::is_rvalue_reference_v<decltype(A)>)
364 {
365 cgt::for_workhorse(A, [&](auto i, auto&& a)
366 {
367 a = cgt::sbo(scalar) + cgt::sbo(a);
368 });
369
370 return std::move(A);
371 }
372 else
373 {
374 constexpr std::size_t N = std::tuple_size_v<container_t>;
375 return cgt::for_stallion<N>([&]<auto... i>(cgt::sequence<i...>)
376 {
377 return c_t{ cgt::sbo(scalar) + cgt::sbo(std::get<i>(A))... };
378 });
379 }
380 }
381
382 template<cgt::tuple_flat_c TupleType, cgt::numerical_c ScalarType>
383 constexpr decltype(auto) operator-(TupleType&& A, ScalarType scalar) noexcept(!cpg::bDetectOverFlow)
384 {
385 using container_t = std::remove_cvref_t<TupleType>;
386 using c_t = decltype(signed_tuple_operation(A, scalar));
387
388 if constexpr(std::same_as<container_t, c_t>
389 && std::is_rvalue_reference_v<decltype(A)>)
390 {
391 cgt::for_workhorse(A, [&](auto i, auto&& a)
392 {
393 a = cgt::sbo(a) - cgt::sbo(scalar);
394 });
395
396 return std::move(A);
397 }
398 else
399 {
400 constexpr std::size_t N = std::tuple_size_v<container_t>;
401 return cgt::for_stallion<N>([&]<auto... i>(cgt::sequence<i...>)
402 {
403 return c_t{ cgt::sbo(std::get<i>(A)) - cgt::sbo(scalar)... };
404 });
405 }
406 }
407
408 template<cgt::tuple_flat_c TupleType, cgt::numerical_c ScalarType>
409 constexpr decltype(auto) operator-(ScalarType scalar, TupleType&& A) noexcept(!cpg::bDetectOverFlow)
410 {
411 using container_t = std::remove_cvref_t<TupleType>;
412 using c_t = decltype(signed_tuple_operation(A, scalar));
413
414 if constexpr(std::same_as<container_t, c_t>
415 && std::is_rvalue_reference_v<decltype(A)>)
416 {
417 cgt::for_workhorse(A, [&](auto i, auto&& a)
418 {
419 a = cgt::sbo(scalar) - cgt::sbo(a);
420 });
421
422 return std::move(A);
423 }
424 else
425 {
426 constexpr std::size_t N = std::tuple_size_v<container_t>;
427 return cgt::for_stallion<N>([&]<auto... i>(cgt::sequence<i...>)
428 {
429 return c_t{ cgt::sbo(scalar) - cgt::sbo(std::get<i>(A))... };
430 });
431 }
432 }
433
434 template<cgt::tuple_flat_c TupleType, cgt::numerical_c ScalarType>
435 constexpr decltype(auto) operator*(TupleType&& A, ScalarType scalar) noexcept(!cpg::bDetectOverFlow)
436 {
437 using container_t = std::remove_cvref_t<TupleType>;
438 using c_t = decltype(signed_tuple_operation(A, scalar));
439
440 if constexpr(std::same_as<container_t, c_t>
441 && std::is_rvalue_reference_v<decltype(A)>)
442 {
443 cgt::for_workhorse(A, [&](auto i, auto&& a)
444 {
445 a = cgt::sbo(a) * cgt::sbo(scalar);
446 });
447
448 return std::move(A);
449 }
450 else
451 {
452 constexpr std::size_t N = std::tuple_size_v<container_t>;
453 return cgt::for_stallion<N>([&]<auto... i>(cgt::sequence<i...>)
454 {
455 return c_t{ cgt::sbo(std::get<i>(A)) * cgt::sbo(scalar)... };
456 });
457 }
458 }
459
460 template<cgt::tuple_flat_c TupleType, cgt::numerical_c ScalarType>
461 constexpr decltype(auto) operator*(ScalarType scalar, TupleType&& A) noexcept(!cpg::bDetectOverFlow)
462 {
463 using container_t = std::remove_cvref_t<TupleType>;
464 using c_t = decltype(signed_tuple_operation(A, scalar));
465
466 if constexpr(std::same_as<container_t, c_t>
467 && std::is_rvalue_reference_v<decltype(A)>)
468 {
469 cgt::for_workhorse(A, [&](auto i, auto&& a)
470 {
471 a = cgt::sbo(scalar) * cgt::sbo(a);
472 });
473
474 return std::move(A);
475 }
476 else
477 {
478 constexpr std::size_t N = std::tuple_size_v<container_t>;
479 return cgt::for_stallion<N>([&]<auto... i>(cgt::sequence<i...>)
480 {
481 return c_t{ cgt::sbo(scalar) * cgt::sbo(std::get<i>(A))... };
482 });
483 }
484 }
485
486 template<cgt::tuple_flat_c TupleType, cgt::numerical_c ScalarType>
487 constexpr decltype(auto) operator/(TupleType&& A, ScalarType scalar) noexcept(!cpg::bDetectOverFlow)
488 {
489 using container_t = std::remove_cvref_t<TupleType>;
490 using c_t = decltype(signed_tuple_operation(A, scalar));
491
492 if constexpr(std::same_as<container_t, c_t>
493 && std::is_rvalue_reference_v<decltype(A)>)
494 {
495 cgt::for_workhorse(A, [&](auto i, auto&& a)
496 {
497 a = cgt::sbo(a) / cgt::sbo(scalar);
498 });
499
500 return std::move(A);
501 }
502 else
503 {
504 constexpr std::size_t N = std::tuple_size_v<container_t>;
505 return cgt::for_stallion<N>([&]<auto... i>(cgt::sequence<i...>)
506 {
507 return c_t{ cgt::sbo(std::get<i>(A)) / cgt::sbo(scalar)... };
508 });
509 }
510 }
511
512 template<cgt::tuple_flat_c TupleType, cgt::numerical_c ScalarType>
513 constexpr decltype(auto) operator/(ScalarType scalar, TupleType&& A) noexcept(!cpg::bDetectOverFlow)
514 {
515 using container_t = std::remove_cvref_t<TupleType>;
516 using c_t = decltype(signed_tuple_operation(A, scalar));
517
518 if constexpr(std::same_as<container_t, c_t>
519 && std::is_rvalue_reference_v<decltype(A)>)
520 {
521 cgt::for_workhorse(A, [&](auto i, auto&& a)
522 {
523 a = cgt::sbo(scalar) / cgt::sbo(a);
524 });
525
526 return std::move(A);
527 }
528 else
529 {
530 constexpr std::size_t N = std::tuple_size_v<container_t>;
531 return cgt::for_stallion<N>([&]<auto... i>(cgt::sequence<i...>)
532 {
533 return c_t{ cgt::sbo(scalar) / cgt::sbo(std::get<i>(A))... };
534 });
535 }
536 }
537}
538// end of namespace std
539
540
541#endif // end of file
void for_workhorse(WorkType &&work, std::integer_sequence< T, Indices... >, ArgTypes &&... args)
Definition: cpg_types.hpp:3814
make_signed_t< std::common_type_t< std::remove_cvref_t< Types >... > > common_signed_t
Definition: cpg_types.hpp:1012
make_unsigned_t< std::common_type_t< std::remove_cvref_t< Types >... > > common_unsigned_t
Definition: cpg_types.hpp:1016
constexpr auto sbo(T &&value) noexcept(!cpg::bDetectOverFlow)
Definition: cpg_types.hpp:2549
std::integer_sequence< std::common_type_t< std::remove_cvref_t< decltype(Indices)>... >, Indices... > sequence
Definition: cpg_types.hpp:2665
constexpr const bool bDetectOverFlow
Definition: cpg_types.hpp:754
auto operator-(const std::tuple< ArgTypes1... > &tuple_a, const std::tuple< ArgTypes2... > &tuple_b)
auto operator+(const std::tuple< ArgTypes1... > &tuple_a, const std::tuple< ArgTypes2... > &tuple_b)
auto operator/(const std::tuple< ArgTypes1... > &tuple_a, const std::tuple< ArgTypes2... > &tuple_b)
auto operator*(const std::tuple< ArgTypes1... > &tuple_a, const std::tuple< ArgTypes2... > &tuple_b)
constexpr auto operator|(Scalar, std::tuple< Elements... >) noexcept
constexpr auto operator||(std::tuple< Elements... >, Scalar) noexcept
constexpr auto tuple_common_elements(std::tuple< Lefts... >, std::tuple< Rights... >) noexcept
constexpr auto signed_tuple_operation(T arg, Tails... args) noexcept
typename hidden::st_tuple_scalar_signed_cast< T, S >::type tuple_scalar_signed_cast
typename hidden::st_tuple_scalar_unsigned_cast< T, S >::type tuple_scalar_unsigned_cast
constexpr auto unsigned_tuple_operation(T arg, Tails... args) noexcept