TIMPI
Loading...
Searching...
No Matches
standard_type.h
Go to the documentation of this file.
1// The TIMPI Message-Passing Parallelism Library.
2// Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3
4// This library is free software; you can redistribute it and/or
5// modify it under the terms of the GNU Lesser General Public
6// License as published by the Free Software Foundation; either
7// version 2.1 of the License, or (at your option) any later version.
8
9// This library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12// Lesser General Public License for more details.
13
14// You should have received a copy of the GNU Lesser General Public
15// License along with this library; if not, write to the Free Software
16// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17
18
19#ifndef TIMPI_STANDARD_TYPE_H
20#define TIMPI_STANDARD_TYPE_H
21
22// TIMPI includes
23#include "timpi/data_type.h"
24#include "timpi/timpi_config.h"
26#include "timpi/semipermanent.h"
27
28// C/C++ includes
29#ifdef TIMPI_HAVE_MPI
31# include "mpi.h"
33#endif // TIMPI_HAVE_MPI
34
35// Boost include if necessary for float128
36#ifdef TIMPI_DEFAULT_QUADRUPLE_PRECISION
37# include <boost/multiprecision/float128.hpp>
38#endif
39
40#include <array>
41#include <complex>
42#include <list>
43#include <map>
44#include <memory>
45#include <numeric>
46#include <set>
47#include <tuple>
48#include <type_traits>
49#include <unordered_map>
50#include <unordered_set>
51#include <utility>
52#include <vector>
53
54namespace TIMPI
55{
56
57//-------------------------------------------------------------------
58
59// Templated helper class to be used with static_assert.
60template<typename T>
61struct standardtype_dependent_false : std::false_type
62{};
63
82template <typename T, typename Enable>
84{
85 /*
86 * The unspecialized class is useless, so we make its constructor
87 * private to catch mistakes at compile-time rather than link-time.
88 * Specializations should have a public constructor of the same
89 * form.
90 */
91private:
92 StandardType(const T * example = nullptr);
93};
94
95
96/*
97 * Template metaprogramming to make build_standard_type work nicely
98 * with nested containers
99 */
100template <typename T>
102{
103 typedef T type;
104};
105
106
107template <typename T, typename A>
108struct InnermostType<std::vector<T, A>>
109{
110 typedef typename InnermostType<T>::type type;
111};
112
113
114template <typename T, typename A>
115struct InnermostType<std::list<T, A>>
116{
117 typedef typename InnermostType<T>::type type;
118};
119
120
121template <typename K, typename T, typename C, typename A>
122struct InnermostType<std::map<K, T, C, A>>
123{
125};
126
127
128template <typename K, typename T, typename C, typename A>
129struct InnermostType<std::multimap<K, T, C, A>>
130{
132};
133
134
135template <typename T, typename C, typename A>
136struct InnermostType<std::multiset<T, C, A>>
137{
138 typedef typename InnermostType<T>::type type;
139};
140
141
142template <typename T, typename C, typename A>
143struct InnermostType<std::set<T, C, A>>
144{
145 typedef typename InnermostType<T>::type type;
146};
147
148
149template <typename K, typename T, typename C, typename A>
150struct InnermostType<std::unordered_map<K, T, C, A>>
151{
153};
154
155
156template <typename K, typename T, typename C, typename A>
157struct InnermostType<std::unordered_multimap<K, T, C, A>>
158{
160};
161
162
163template <typename T, typename C, typename A>
165{
166 typedef typename InnermostType<T>::type type;
167};
168
169
170template <typename T, typename C, typename A>
171struct InnermostType<std::unordered_set<T, C, A>>
172{
173 typedef typename InnermostType<T>::type type;
174};
175
176
177/*
178 * Returns a StandardType suitable for use with the example data.
179 */
180template <typename T>
186
187
188
189/*
190 * Returns a StandardType suitable for use with the data in the
191 * example container.
192 */
193template <typename T, typename A>
195build_standard_type(const std::vector<T, A> * example = nullptr)
196{
197 const T * inner_example = (example && !example->empty()) ? &(*example)[0] : nullptr;
199}
200
201
202
203// ------------------------------------------------------------
204// Declare StandardType specializations for C++ built-in types
205
206#ifdef TIMPI_HAVE_MPI
207
208#define TIMPI_STANDARD_TYPE(cxxtype,mpitype) \
209 template<> \
210 class StandardType<cxxtype> : public DataType \
211 { \
212 public: \
213 explicit \
214 StandardType(const cxxtype * = nullptr) : DataType(mpitype) {} \
215 \
216 static const bool is_fixed_type = true; \
217 }
218
219#else
220
221#define TIMPI_STANDARD_TYPE(cxxtype,mpitype) \
222 template<> \
223 class StandardType<cxxtype> : public DataType \
224 { \
225 public: \
226 explicit \
227 StandardType(const cxxtype * = nullptr) : DataType() {} \
228 \
229 static const bool is_fixed_type = true; \
230 }
231
232#endif
233
248
249#ifdef TIMPI_HAVE_MPI
250
251// For non-default data types, we like to be able to construct them on
252// the fly, but we don't like to repeatedly destroy and reconstruct
253// them and we don't like to leak them, so let's keep them until TIMPI
254// exits via SemiPermanent
256{
257public:
262
263 virtual ~ManageType() override {
265 }
266private:
268};
269
270
271// Quad and float128 types aren't standard C++, so only work with them
272// if configure and PETSc encapsulated the non-standard issues.
273# ifdef TIMPI_DEFAULT_QUADRUPLE_PRECISION
274 template<>
276 {
277 public:
278 explicit
282 {
285 (std::make_unique<ManageType>(static_type));
286 }
287 _datatype = static_type;
288 }
289
293
295 {
296 _datatype = t._datatype;
297 return *this;
298 }
299
300 static const bool is_fixed_type = true;
301 };
302# endif
303#else
304# ifdef TIMPI_DEFAULT_QUADRUPLE_PRECISION
306# endif
307#endif
308
309// using remove_const here so our packing code can see a
310// `StandardType<pair<const K,T>>::is_fixed_type` and infer that it
311// can do memcpy on them
312template<typename T1, typename T2>
313class StandardType<std::pair<T1, T2>,
314 typename std::enable_if<
315 StandardType<typename std::remove_const<T1>::type>::is_fixed_type &&
316 StandardType<T2>::is_fixed_type>::type> : public DataType
317{
318public:
319 explicit
320 StandardType(const std::pair<T1, T2> * example = nullptr)
321 : DataType()
322 {
323#ifdef TIMPI_HAVE_MPI
326 {
327 // We need an example for MPI_Address to use
328 static const std::pair<T1, T2> p;
329 if (!example)
330 example = &p;
331
332 // Get the sub-data-types, and make sure they live long enough
333 // to construct the derived type
335 d1(const_cast<typename std::remove_const<T1>::type *>
336 (&example->first));
337 StandardType<T2> d2(&example->second);
338
340 int blocklengths[] = {1,1};
342
344 (MPI_Get_address (const_cast<std::pair<T1,T2> *>(example),
345 &start));
347 (MPI_Get_address (const_cast<T1*>(&example->first),
348 &displs[0]));
350 (MPI_Get_address (const_cast<T2*>(&example->second),
351 &displs[1]));
352 displs[0] -= start;
353 displs[1] -= start;
354
355 // create a prototype structure
359 &tmptype));
362
363 // resize the structure type to account for padding, if any
366 sizeof(std::pair<T1,T2>),
367 &static_type));
370
372 (std::make_unique<ManageType>(static_type));
373 }
374 _datatype = static_type;
375#else
377#endif // TIMPI_HAVE_MPI
378 }
379
380 StandardType(const StandardType<std::pair<T1, T2>> & t)
381 : DataType()
382 {
383 _datatype = t._datatype;
384 }
385
387 {
388 _datatype = t._datatype;
389 return *this;
390 }
391
392 static const bool is_fixed_type = true;
393};
394
395
396
397template<typename T, std::size_t N>
398class StandardType<std::array<T, N>,
399 typename std::enable_if<
400 StandardType<T>::is_fixed_type>::type> : public DataType
401{
402public:
403 explicit
404 StandardType(const std::array<T, N> * example = nullptr)
405 : DataType()
406 {
407#ifdef TIMPI_HAVE_MPI
410 {
411 // We need an example for MPI_Address to use
412 std::array<T, N> * ex;
413 std::unique_ptr<std::array<T, N>> temp;
414 if (example)
415 ex = const_cast<std::array<T, N> *>(example);
416 else
417 {
418 temp.reset(new std::array<T, N>());
419 ex = temp.get();
420 }
421
422 static_assert(N > 0, "Zero-length std::array is not supported by TIMPI");
423 StandardType<T> T_type(&((*ex)[0]));
424
425 int blocklength = N;
428
432 (MPI_Get_address (&((*ex)[0]), &displs));
433
434 // subtract off offset to first value from the beginning of the structure
435 displs -= start;
436
437 // create a prototype structure
440 &tmptype));
443
444 // resize the structure type to account for padding, if any
446 (MPI_Type_create_resized (tmptype, 0, sizeof(std::array<T,N>),
447 &static_type));
448
451
453 (std::make_unique<ManageType>(static_type));
454 }
455 _datatype = static_type;
456#else // #ifdef TIMPI_HAVE_MPI
458#endif
459 }
460
461 StandardType(const StandardType<std::array<T, N>> & t)
462 : DataType()
463 {
464 _datatype = t._datatype;
465 }
466
468 {
469 _datatype = t._datatype;
470 return *this;
471 }
472
473 static const bool is_fixed_type = true;
474};
475
476
477// Helper functions for creating type/displacement arrays for tuples
478//
479// These are classes since we can't partially specialize functions
480template<std::size_t n_minus_i>
482{
483 template<typename... Types>
484 static void build(std::vector<std::unique_ptr<DataType>> & out_vec,
485 const std::tuple<Types...> & example);
486};
487
488template <>
490{
491 template<typename... Types>
492 static void build(std::vector<std::unique_ptr<DataType>> & /*out_vec*/,
493 const std::tuple<Types...> & /*example*/) {}
494};
495
496template<std::size_t n_minus_i>
497template<typename... Types>
499 (std::vector<std::unique_ptr<DataType>> & out_vec,
500 const std::tuple<Types...> & example)
501{
502 typedef typename
503 std::tuple_element<sizeof...(Types)-n_minus_i, std::tuple<Types...>>::type
504 ith_type;
505
506 out_vec.emplace_back
507 (std::make_unique<StandardType<ith_type>>
508 (&std::get<sizeof...(Types)-n_minus_i>(example)));
509
511}
512
513
514template<std::size_t n_minus_i>
516{
517 template <typename OutArray, class... Types>
518 static void fill(OutArray & out,
519 const std::tuple<Types...> & example);
520};
521
522template<>
524{
525 template <typename OutArray, typename... Types>
526 static void fill(OutArray & /*out*/,
527 const std::tuple<Types...> & /*example*/) {}
528};
529
530
531template<std::size_t n_minus_i>
532template<typename OutArray, typename... Types>
534 (OutArray & out_vec,
535 const std::tuple<Types...> & example)
536{
539 (&std::get<sizeof...(Types)-n_minus_i>(example),
540 &out_vec[sizeof...(Types)-n_minus_i]));
541
543}
544
545
546template <typename Head, typename... Tail>
552
553template <typename Head>
558
559template<typename... Types>
560class StandardType<std::tuple<Types...>,
561 typename std::enable_if<
562 CheckAllFixedTypes<Types...>::is_fixed_type>::type> : public DataType
563{
564public:
565 explicit
566 StandardType(const std::tuple<Types...> * example = nullptr)
567 : DataType()
568 {
569#ifdef TIMPI_HAVE_MPI
572 {
573 // We need an example for MPI_Address to use
574 static const std::tuple<Types...> t;
575 if (!example)
576 example = &t;
577
579
582
583 const std::size_t tuplesize = sizeof...(Types);
584
585 std::vector<std::unique_ptr<DataType>> subtypes;
586 BuildStandardTypeVector<sizeof...(Types)>::build(subtypes, *example);
587
588 std::array<MPI_Aint, sizeof...(Types)> displs;
589 FillDisplacementArray<sizeof...(Types)>::fill(displs, *example);
590
591 std::array<MPI_Datatype, sizeof...(Types)> types;
592 std::array<int, sizeof...(Types)> blocklengths;
593
594 for (std::size_t i = 0; i != tuplesize; ++i)
595 {
596 displs[i] -= start;
597 types[i] = (data_type)(*subtypes[i]);
598 blocklengths[i] = 1;
599 }
600
601 // create a prototype structure
604 (MPI_Type_create_struct (tuplesize, blocklengths.data(), displs.data(), types.data(),
605 &tmptype));
608
609 // resize the structure type to account for padding, if any
612 sizeof(std::tuple<Types...>),
613 &static_type));
616
618 (std::make_unique<ManageType>(static_type));
619 }
620 _datatype = static_type;
621#else // #ifdef TIMPI_HAVE_MPI
623#endif // TIMPI_HAVE_MPI
624 }
625
626 StandardType(const StandardType<std::tuple<Types...>> & t)
627 : DataType()
628 {
629 _datatype = t._datatype;
630 }
631
633 {
634 _datatype = t._datatype;
635 return *this;
636 }
637
639};
640
641
642template<typename T>
643class StandardType<std::complex<T>> : public DataType
644{
645public:
646 explicit
647 StandardType(const std::complex<T> * /*example*/ = nullptr) :
648 DataType(StandardType<T>(nullptr), 2) {}
649
650 ~StandardType() { this->free(); }
651
653};
654
655} // namespace TIMPI
656
657#endif // TIMPI_STANDARD_TYPE_H
Encapsulates the MPI_Datatype.
Definition data_type.h:51
data_type _datatype
Definition data_type.h:105
ManageType(data_type uncommitted_type)
virtual ~ManageType() override
StandardType<T>'s which do not define a way to MPI_Type T should inherit from this class.
Definition data_type.h:121
static const bool is_fixed_type
Definition data_type.h:130
The SemiPermanent "class" is basically just a place for a destructor vtable.
static void add(std::unique_ptr< SemiPermanent > obj)
StandardType(const TIMPI_DEFAULT_SCALAR_TYPE *=nullptr)
StandardType & operator=(StandardType &t)
StandardType(const StandardType< TIMPI_DEFAULT_SCALAR_TYPE > &t)
StandardType(const std::complex< T > *=nullptr)
Templated class to provide the appropriate MPI datatype for use with built-in C types or simple C++ c...
StandardType(const T *example=nullptr)
void timpi_ignore(const Args &...)
StandardType< T > build_standard_type(const T *example=nullptr)
TIMPI_STANDARD_TYPE(char, MPI_CHAR)
Tnew cast_int(Told oldvar)
static void build(std::vector< std::unique_ptr< DataType > > &, const std::tuple< Types... > &)
static void build(std::vector< std::unique_ptr< DataType > > &out_vec, const std::tuple< Types... > &example)
static const bool is_fixed_type
static void fill(OutArray &, const std::tuple< Types... > &)
static void fill(OutArray &out, const std::tuple< Types... > &example)
InnermostType< std::pair< constK, T > >::type type
InnermostType< std::pair< constK, T > >::type type
InnermostType< std::pair< constK, T > >::type type
InnermostType< std::pair< constK, T > >::type type