TIMPI
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
30 # include "timpi/ignore_warnings.h"
31 # include "mpi.h"
32 # include "timpi/restore_warnings.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 
54 namespace TIMPI
55 {
56 
57 //-------------------------------------------------------------------
58 
59 // Templated helper class to be used with static_assert.
60 template<typename T>
61 struct standardtype_dependent_false : std::false_type
62 {};
63 
82 template <typename T, typename Enable>
83 class StandardType : public NotADataType
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  */
91 private:
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  */
100 template <typename T>
102 {
103  typedef T type;
104 };
105 
106 
107 template <typename T, typename A>
108 struct InnermostType<std::vector<T, A>>
109 {
110  typedef typename InnermostType<T>::type type;
111 };
112 
113 
114 template <typename T, typename A>
115 struct InnermostType<std::list<T, A>>
116 {
117  typedef typename InnermostType<T>::type type;
118 };
119 
120 
121 template <typename K, typename T, typename C, typename A>
122 struct InnermostType<std::map<K, T, C, A>>
123 {
125 };
126 
127 
128 template <typename K, typename T, typename C, typename A>
129 struct InnermostType<std::multimap<K, T, C, A>>
130 {
132 };
133 
134 
135 template <typename T, typename C, typename A>
136 struct InnermostType<std::multiset<T, C, A>>
137 {
138  typedef typename InnermostType<T>::type type;
139 };
140 
141 
142 template <typename T, typename C, typename A>
143 struct InnermostType<std::set<T, C, A>>
144 {
145  typedef typename InnermostType<T>::type type;
146 };
147 
148 
149 template <typename K, typename T, typename C, typename A>
150 struct InnermostType<std::unordered_map<K, T, C, A>>
151 {
153 };
154 
155 
156 template <typename K, typename T, typename C, typename A>
157 struct InnermostType<std::unordered_multimap<K, T, C, A>>
158 {
160 };
161 
162 
163 template <typename T, typename C, typename A>
164 struct InnermostType<std::unordered_multiset<T, C, A>>
165 {
166  typedef typename InnermostType<T>::type type;
167 };
168 
169 
170 template <typename T, typename C, typename A>
171 struct 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  */
180 template <typename T>
181 StandardType<T> build_standard_type(const T * example = nullptr)
182 {
183  StandardType<T> returnval(example);
184  return returnval;
185 }
186 
187 
188 
189 /*
190  * Returns a StandardType suitable for use with the data in the
191  * example container.
192  */
193 template <typename T, typename A>
194 StandardType<typename InnermostType<T>::type>
195 build_standard_type(const std::vector<T, A> * example = nullptr)
196 {
197  const T * inner_example = (example && !example->empty()) ? &(*example)[0] : nullptr;
198  return build_standard_type(inner_example);
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 
234 TIMPI_STANDARD_TYPE(char,MPI_CHAR);
235 TIMPI_STANDARD_TYPE(signed char,MPI_SIGNED_CHAR);
236 TIMPI_STANDARD_TYPE(unsigned char,MPI_UNSIGNED_CHAR);
237 TIMPI_STANDARD_TYPE(short int,MPI_SHORT);
238 TIMPI_STANDARD_TYPE(unsigned short int,MPI_UNSIGNED_SHORT);
239 TIMPI_STANDARD_TYPE(int,MPI_INT);
240 TIMPI_STANDARD_TYPE(unsigned int,MPI_UNSIGNED);
241 TIMPI_STANDARD_TYPE(long,MPI_LONG);
242 TIMPI_STANDARD_TYPE(long long,MPI_LONG_LONG_INT);
243 TIMPI_STANDARD_TYPE(unsigned long,MPI_UNSIGNED_LONG);
244 TIMPI_STANDARD_TYPE(unsigned long long,MPI_UNSIGNED_LONG_LONG);
245 TIMPI_STANDARD_TYPE(float,MPI_FLOAT);
246 TIMPI_STANDARD_TYPE(double,MPI_DOUBLE);
247 TIMPI_STANDARD_TYPE(long double,MPI_LONG_DOUBLE);
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
255 class ManageType : public SemiPermanent
256 {
257 public:
258  ManageType(data_type uncommitted_type) :
259  _type(uncommitted_type) {
260  MPI_Type_commit (&uncommitted_type);
261  }
262 
263  virtual ~ManageType() override {
264  MPI_Type_free(&_type);
265  }
266 private:
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<>
275  class StandardType<TIMPI_DEFAULT_SCALAR_TYPE> : public DataType
276  {
277  public:
278  explicit
279  StandardType(const TIMPI_DEFAULT_SCALAR_TYPE * = nullptr) : DataType() {
280  static data_type static_type = MPI_DATATYPE_NULL;
281  if (static_type == MPI_DATATYPE_NULL)
282  {
283  timpi_call_mpi(MPI_Type_contiguous(2, MPI_DOUBLE, &static_type));
285  (std::make_unique<ManageType>(static_type));
286  }
287  _datatype = static_type;
288  }
289 
291  _datatype = t._datatype;
292  }
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
305  TIMPI_STANDARD_TYPE(TIMPI_DEFAULT_SCALAR_TYPE,);
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
312 template<typename T1, typename T2>
313 class 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 {
318 public:
319  explicit
320  StandardType(const std::pair<T1, T2> * example = nullptr)
321  : DataType()
322  {
323 #ifdef TIMPI_HAVE_MPI
324  static data_type static_type = MPI_DATATYPE_NULL;
325  if (static_type == MPI_DATATYPE_NULL)
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 
339  MPI_Datatype types[] = { (data_type)d1, (data_type)d2 };
340  int blocklengths[] = {1,1};
341  MPI_Aint displs[2], start;
342 
343  timpi_call_mpi
344  (MPI_Get_address (const_cast<std::pair<T1,T2> *>(example),
345  &start));
346  timpi_call_mpi
347  (MPI_Get_address (const_cast<T1*>(&example->first),
348  &displs[0]));
349  timpi_call_mpi
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
356  MPI_Datatype tmptype;
357  timpi_call_mpi
358  (MPI_Type_create_struct (2, blocklengths, displs, types,
359  &tmptype));
360  timpi_call_mpi
361  (MPI_Type_commit (&tmptype));
362 
363  // resize the structure type to account for padding, if any
364  timpi_call_mpi
365  (MPI_Type_create_resized (tmptype, 0,
366  sizeof(std::pair<T1,T2>),
367  &static_type));
368  timpi_call_mpi
369  (MPI_Type_free (&tmptype));
370 
372  (std::make_unique<ManageType>(static_type));
373  }
374  _datatype = static_type;
375 #else
376  timpi_ignore(example);
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 
397 template<typename T, std::size_t N>
398 class StandardType<std::array<T, N>,
399  typename std::enable_if<
400  StandardType<T>::is_fixed_type>::type> : public DataType
401 {
402 public:
403  explicit
404  StandardType(const std::array<T, N> * example = nullptr)
405  : DataType()
406  {
407 #ifdef TIMPI_HAVE_MPI
408  static data_type static_type = MPI_DATATYPE_NULL;
409  if (static_type == MPI_DATATYPE_NULL)
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;
426  MPI_Aint displs, start;
427  MPI_Datatype tmptype, type = T_type;
428 
429  timpi_call_mpi
430  (MPI_Get_address (ex, &start));
431  timpi_call_mpi
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
438  timpi_call_mpi
439  (MPI_Type_create_struct (1, &blocklength, &displs, &type,
440  &tmptype));
441  timpi_call_mpi
442  (MPI_Type_commit (&tmptype));
443 
444  // resize the structure type to account for padding, if any
445  timpi_call_mpi
446  (MPI_Type_create_resized (tmptype, 0, sizeof(std::array<T,N>),
447  &static_type));
448 
449  timpi_call_mpi
450  (MPI_Type_free (&tmptype));
451 
453  (std::make_unique<ManageType>(static_type));
454  }
455  _datatype = static_type;
456 #else // #ifdef TIMPI_HAVE_MPI
457  timpi_ignore(example);
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
480 template<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 
488 template <>
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 
496 template<std::size_t n_minus_i>
497 template<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 
514 template<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 
522 template<>
524 {
525  template <typename OutArray, typename... Types>
526  static void fill(OutArray & /*out*/,
527  const std::tuple<Types...> & /*example*/) {}
528 };
529 
530 
531 template<std::size_t n_minus_i>
532 template<typename OutArray, typename... Types>
534  (OutArray & out_vec,
535  const std::tuple<Types...> & example)
536 {
537  timpi_call_mpi
538  (MPI_Get_address
539  (&std::get<sizeof...(Types)-n_minus_i>(example),
540  &out_vec[sizeof...(Types)-n_minus_i]));
541 
543 }
544 
545 
546 template <typename Head, typename... Tail>
548 {
551 };
552 
553 template <typename Head>
554 struct CheckAllFixedTypes<Head>
555 {
557 };
558 
559 template<typename... Types>
560 class StandardType<std::tuple<Types...>,
561  typename std::enable_if<
562  CheckAllFixedTypes<Types...>::is_fixed_type>::type> : public DataType
563 {
564 public:
565  explicit
566  StandardType(const std::tuple<Types...> * example = nullptr)
567  : DataType()
568  {
569 #ifdef TIMPI_HAVE_MPI
570  static data_type static_type = MPI_DATATYPE_NULL;
571  if (static_type == MPI_DATATYPE_NULL)
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 
578  MPI_Aint start;
579 
580  timpi_call_mpi
581  (MPI_Get_address (example, &start));
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
602  MPI_Datatype tmptype;
603  timpi_call_mpi
604  (MPI_Type_create_struct (tuplesize, blocklengths.data(), displs.data(), types.data(),
605  &tmptype));
606  timpi_call_mpi
607  (MPI_Type_commit (&tmptype));
608 
609  // resize the structure type to account for padding, if any
610  timpi_call_mpi
611  (MPI_Type_create_resized (tmptype, 0,
612  sizeof(std::tuple<Types...>),
613  &static_type));
614  timpi_call_mpi
615  (MPI_Type_free (&tmptype));
616 
618  (std::make_unique<ManageType>(static_type));
619  }
620  _datatype = static_type;
621 #else // #ifdef TIMPI_HAVE_MPI
622  timpi_ignore(example);
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 
638  static const bool is_fixed_type = CheckAllFixedTypes<Types...>::is_fixed_type;
639 };
640 
641 
642 template<typename T>
643 class StandardType<std::complex<T>> : public DataType
644 {
645 public:
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
ManageType(data_type uncommitted_type)
MPI_Datatype data_type
Data types for communication.
Definition: data_type.h:33
InnermostType< std::pair< const K, T > >::type type
StandardType(const std::complex< T > *=nullptr)
StandardType & operator=(StandardType &t)
static void build(std::vector< std::unique_ptr< DataType >> &, const std::tuple< Types... > &)
StandardType(const StandardType< TIMPI_DEFAULT_SCALAR_TYPE > &t)
Encapsulates the MPI_Datatype.
Definition: data_type.h:50
Templated class to provide the appropriate MPI datatype for use with built-in C types or simple C++ c...
Definition: standard_type.h:83
StandardType(const T *example=nullptr)
StandardType< T > build_standard_type(const T *example=nullptr)
virtual ~ManageType() override
static void build(std::vector< std::unique_ptr< DataType >> &out_vec, const std::tuple< Types... > &example)
data_type _datatype
Definition: data_type.h:105
static const bool is_fixed_type
Definition: data_type.h:130
static void fill(OutArray &out, const std::tuple< Types... > &example)
StandardType<T>&#39;s which do not define a way to MPI_Type T should inherit from this class...
Definition: data_type.h:120
InnermostType< std::pair< const K, T > >::type type
static void fill(OutArray &, const std::tuple< Types... > &)
The SemiPermanent "class" is basically just a place for a destructor vtable.
Definition: semipermanent.h:48
InnermostType< std::pair< const K, T > >::type type
static const bool is_fixed_type
static void add(std::unique_ptr< SemiPermanent > obj)
Definition: semipermanent.C:42
void timpi_ignore(const Args &...)
Definition: timpi_assert.h:286
StandardType(const TIMPI_DEFAULT_SCALAR_TYPE *=nullptr)
InnermostType< std::pair< const K, T > >::type type
TIMPI_STANDARD_TYPE(char, MPI_CHAR)