LLVM  13.0.0git
Optional.h
Go to the documentation of this file.
1 //===- Optional.h - Simple variant for passing optional values --*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file provides Optional, a template class modeled in the spirit of
10 // OCaml's 'opt' variant. The idea is to strongly type whether or not
11 // a value can be optional.
12 //
13 //===----------------------------------------------------------------------===//
14 
15 #ifndef LLVM_ADT_OPTIONAL_H
16 #define LLVM_ADT_OPTIONAL_H
17 
18 #include "llvm/ADT/Hashing.h"
19 #include "llvm/ADT/None.h"
20 #include "llvm/Support/Compiler.h"
22 #include <cassert>
23 #include <memory>
24 #include <new>
25 #include <utility>
26 
27 namespace llvm {
28 
29 class raw_ostream;
30 
31 namespace optional_detail {
32 
33 struct in_place_t {};
34 
35 /// Storage for any type.
36 //
37 // The specialization condition intentionally uses
38 // llvm::is_trivially_copy_constructible instead of
39 // std::is_trivially_copy_constructible. GCC versions prior to 7.4 may
40 // instantiate the copy constructor of `T` when
41 // std::is_trivially_copy_constructible is instantiated. This causes
42 // compilation to fail if we query the trivially copy constructible property of
43 // a class which is not copy constructible.
44 //
45 // The current implementation of OptionalStorage insists that in order to use
46 // the trivial specialization, the value_type must be trivially copy
47 // constructible and trivially copy assignable due to =default implementations
48 // of the copy/move constructor/assignment. It does not follow that this is
49 // necessarily the case std::is_trivially_copyable is true (hence the expanded
50 // specialization condition).
51 //
52 // The move constructible / assignable conditions emulate the remaining behavior
53 // of std::is_trivially_copyable.
54 template <typename T, bool = (llvm::is_trivially_copy_constructible<T>::value &&
55  std::is_trivially_copy_assignable<T>::value &&
56  (std::is_trivially_move_constructible<T>::value ||
57  !std::is_move_constructible<T>::value) &&
58  (std::is_trivially_move_assignable<T>::value ||
59  !std::is_move_assignable<T>::value))>
61  union {
62  char empty;
64  };
65  bool hasVal;
66 
67 public:
69 
70  constexpr OptionalStorage() noexcept : empty(), hasVal(false) {}
71 
72  constexpr OptionalStorage(OptionalStorage const &other) : OptionalStorage() {
73  if (other.hasValue()) {
74  emplace(other.value);
75  }
76  }
78  if (other.hasValue()) {
79  emplace(std::move(other.value));
80  }
81  }
82 
83  template <class... Args>
84  constexpr explicit OptionalStorage(in_place_t, Args &&... args)
85  : value(std::forward<Args>(args)...), hasVal(true) {}
86 
87  void reset() noexcept {
88  if (hasVal) {
89  value.~T();
90  hasVal = false;
91  }
92  }
93 
94  constexpr bool hasValue() const noexcept { return hasVal; }
95 
97  assert(hasVal);
98  return value;
99  }
100  constexpr T const &getValue() const LLVM_LVALUE_FUNCTION noexcept {
101  assert(hasVal);
102  return value;
103  }
104 #if LLVM_HAS_RVALUE_REFERENCE_THIS
105  T &&getValue() && noexcept {
106  assert(hasVal);
107  return std::move(value);
108  }
109 #endif
110 
111  template <class... Args> void emplace(Args &&... args) {
112  reset();
113  ::new ((void *)std::addressof(value)) T(std::forward<Args>(args)...);
114  hasVal = true;
115  }
116 
118  if (hasValue()) {
119  value = y;
120  } else {
121  ::new ((void *)std::addressof(value)) T(y);
122  hasVal = true;
123  }
124  return *this;
125  }
127  if (hasValue()) {
128  value = std::move(y);
129  } else {
130  ::new ((void *)std::addressof(value)) T(std::move(y));
131  hasVal = true;
132  }
133  return *this;
134  }
135 
137  if (other.hasValue()) {
138  if (hasValue()) {
139  value = other.value;
140  } else {
141  ::new ((void *)std::addressof(value)) T(other.value);
142  hasVal = true;
143  }
144  } else {
145  reset();
146  }
147  return *this;
148  }
149 
151  if (other.hasValue()) {
152  if (hasValue()) {
153  value = std::move(other.value);
154  } else {
155  ::new ((void *)std::addressof(value)) T(std::move(other.value));
156  hasVal = true;
157  }
158  } else {
159  reset();
160  }
161  return *this;
162  }
163 };
164 
165 template <typename T> class OptionalStorage<T, true> {
166  union {
167  char empty;
169  };
170  bool hasVal = false;
171 
172 public:
173  ~OptionalStorage() = default;
174 
175  constexpr OptionalStorage() noexcept : empty{} {}
176 
177  constexpr OptionalStorage(OptionalStorage const &other) = default;
178  constexpr OptionalStorage(OptionalStorage &&other) = default;
179 
180  OptionalStorage &operator=(OptionalStorage const &other) = default;
181  OptionalStorage &operator=(OptionalStorage &&other) = default;
182 
183  template <class... Args>
184  constexpr explicit OptionalStorage(in_place_t, Args &&... args)
185  : value(std::forward<Args>(args)...), hasVal(true) {}
186 
187  void reset() noexcept {
188  if (hasVal) {
189  value.~T();
190  hasVal = false;
191  }
192  }
193 
194  constexpr bool hasValue() const noexcept { return hasVal; }
195 
197  assert(hasVal);
198  return value;
199  }
200  constexpr T const &getValue() const LLVM_LVALUE_FUNCTION noexcept {
201  assert(hasVal);
202  return value;
203  }
204 #if LLVM_HAS_RVALUE_REFERENCE_THIS
205  T &&getValue() && noexcept {
206  assert(hasVal);
207  return std::move(value);
208  }
209 #endif
210 
211  template <class... Args> void emplace(Args &&... args) {
212  reset();
213  ::new ((void *)std::addressof(value)) T(std::forward<Args>(args)...);
214  hasVal = true;
215  }
216 
218  if (hasValue()) {
219  value = y;
220  } else {
221  ::new ((void *)std::addressof(value)) T(y);
222  hasVal = true;
223  }
224  return *this;
225  }
227  if (hasValue()) {
228  value = std::move(y);
229  } else {
230  ::new ((void *)std::addressof(value)) T(std::move(y));
231  hasVal = true;
232  }
233  return *this;
234  }
235 };
236 
237 } // namespace optional_detail
238 
239 template <typename T> class Optional {
240  optional_detail::OptionalStorage<T> Storage;
241 
242 public:
243  using value_type = T;
244 
245  constexpr Optional() {}
246  constexpr Optional(NoneType) {}
247 
248  constexpr Optional(const T &y) : Storage(optional_detail::in_place_t{}, y) {}
249  constexpr Optional(const Optional &O) = default;
250 
251  constexpr Optional(T &&y)
252  : Storage(optional_detail::in_place_t{}, std::move(y)) {}
253  constexpr Optional(Optional &&O) = default;
254 
256  Storage = std::move(y);
257  return *this;
258  }
259  Optional &operator=(Optional &&O) = default;
260 
261  /// Create a new object by constructing it in place with the given arguments.
262  template <typename... ArgTypes> void emplace(ArgTypes &&... Args) {
263  Storage.emplace(std::forward<ArgTypes>(Args)...);
264  }
265 
266  static constexpr Optional create(const T *y) {
267  return y ? Optional(*y) : Optional();
268  }
269 
270  Optional &operator=(const T &y) {
271  Storage = y;
272  return *this;
273  }
274  Optional &operator=(const Optional &O) = default;
275 
276  void reset() { Storage.reset(); }
277 
278  constexpr const T *getPointer() const { return &Storage.getValue(); }
279  T *getPointer() { return &Storage.getValue(); }
280  constexpr const T &getValue() const LLVM_LVALUE_FUNCTION {
281  return Storage.getValue();
282  }
283  T &getValue() LLVM_LVALUE_FUNCTION { return Storage.getValue(); }
284 
285  constexpr explicit operator bool() const { return hasValue(); }
286  constexpr bool hasValue() const { return Storage.hasValue(); }
287  constexpr const T *operator->() const { return getPointer(); }
288  T *operator->() { return getPointer(); }
289  constexpr const T &operator*() const LLVM_LVALUE_FUNCTION {
290  return getValue();
291  }
293 
294  template <typename U>
295  constexpr T getValueOr(U &&value) const LLVM_LVALUE_FUNCTION {
296  return hasValue() ? getValue() : std::forward<U>(value);
297  }
298 
299  /// Apply a function to the value if present; otherwise return None.
300  template <class Function>
301  auto map(const Function &F) const LLVM_LVALUE_FUNCTION
302  -> Optional<decltype(F(getValue()))> {
303  if (*this) return F(getValue());
304  return None;
305  }
306 
307 #if LLVM_HAS_RVALUE_REFERENCE_THIS
308  T &&getValue() && { return std::move(Storage.getValue()); }
309  T &&operator*() && { return std::move(Storage.getValue()); }
310 
311  template <typename U>
312  T getValueOr(U &&value) && {
313  return hasValue() ? std::move(getValue()) : std::forward<U>(value);
314  }
315 
316  /// Apply a function to the value if present; otherwise return None.
317  template <class Function>
318  auto map(const Function &F) &&
319  -> Optional<decltype(F(std::move(*this).getValue()))> {
320  if (*this) return F(std::move(*this).getValue());
321  return None;
322  }
323 #endif
324 };
325 
326 template <class T> llvm::hash_code hash_value(const Optional<T> &O) {
327  return O ? hash_combine(true, *O) : hash_value(false);
328 }
329 
330 template <typename T, typename U>
331 constexpr bool operator==(const Optional<T> &X, const Optional<U> &Y) {
332  if (X && Y)
333  return *X == *Y;
334  return X.hasValue() == Y.hasValue();
335 }
336 
337 template <typename T, typename U>
338 constexpr bool operator!=(const Optional<T> &X, const Optional<U> &Y) {
339  return !(X == Y);
340 }
341 
342 template <typename T, typename U>
343 constexpr bool operator<(const Optional<T> &X, const Optional<U> &Y) {
344  if (X && Y)
345  return *X < *Y;
346  return X.hasValue() < Y.hasValue();
347 }
348 
349 template <typename T, typename U>
350 constexpr bool operator<=(const Optional<T> &X, const Optional<U> &Y) {
351  return !(Y < X);
352 }
353 
354 template <typename T, typename U>
355 constexpr bool operator>(const Optional<T> &X, const Optional<U> &Y) {
356  return Y < X;
357 }
358 
359 template <typename T, typename U>
360 constexpr bool operator>=(const Optional<T> &X, const Optional<U> &Y) {
361  return !(X < Y);
362 }
363 
364 template <typename T>
365 constexpr bool operator==(const Optional<T> &X, NoneType) {
366  return !X;
367 }
368 
369 template <typename T>
370 constexpr bool operator==(NoneType, const Optional<T> &X) {
371  return X == None;
372 }
373 
374 template <typename T>
375 constexpr bool operator!=(const Optional<T> &X, NoneType) {
376  return !(X == None);
377 }
378 
379 template <typename T>
380 constexpr bool operator!=(NoneType, const Optional<T> &X) {
381  return X != None;
382 }
383 
384 template <typename T> constexpr bool operator<(const Optional<T> &, NoneType) {
385  return false;
386 }
387 
388 template <typename T> constexpr bool operator<(NoneType, const Optional<T> &X) {
389  return X.hasValue();
390 }
391 
392 template <typename T>
393 constexpr bool operator<=(const Optional<T> &X, NoneType) {
394  return !(None < X);
395 }
396 
397 template <typename T>
398 constexpr bool operator<=(NoneType, const Optional<T> &X) {
399  return !(X < None);
400 }
401 
402 template <typename T> constexpr bool operator>(const Optional<T> &X, NoneType) {
403  return None < X;
404 }
405 
406 template <typename T> constexpr bool operator>(NoneType, const Optional<T> &X) {
407  return X < None;
408 }
409 
410 template <typename T>
411 constexpr bool operator>=(const Optional<T> &X, NoneType) {
412  return None <= X;
413 }
414 
415 template <typename T>
416 constexpr bool operator>=(NoneType, const Optional<T> &X) {
417  return X <= None;
418 }
419 
420 template <typename T>
421 constexpr bool operator==(const Optional<T> &X, const T &Y) {
422  return X && *X == Y;
423 }
424 
425 template <typename T>
426 constexpr bool operator==(const T &X, const Optional<T> &Y) {
427  return Y && X == *Y;
428 }
429 
430 template <typename T>
431 constexpr bool operator!=(const Optional<T> &X, const T &Y) {
432  return !(X == Y);
433 }
434 
435 template <typename T>
436 constexpr bool operator!=(const T &X, const Optional<T> &Y) {
437  return !(X == Y);
438 }
439 
440 template <typename T>
441 constexpr bool operator<(const Optional<T> &X, const T &Y) {
442  return !X || *X < Y;
443 }
444 
445 template <typename T>
446 constexpr bool operator<(const T &X, const Optional<T> &Y) {
447  return Y && X < *Y;
448 }
449 
450 template <typename T>
451 constexpr bool operator<=(const Optional<T> &X, const T &Y) {
452  return !(Y < X);
453 }
454 
455 template <typename T>
456 constexpr bool operator<=(const T &X, const Optional<T> &Y) {
457  return !(Y < X);
458 }
459 
460 template <typename T>
461 constexpr bool operator>(const Optional<T> &X, const T &Y) {
462  return Y < X;
463 }
464 
465 template <typename T>
466 constexpr bool operator>(const T &X, const Optional<T> &Y) {
467  return Y < X;
468 }
469 
470 template <typename T>
471 constexpr bool operator>=(const Optional<T> &X, const T &Y) {
472  return !(X < Y);
473 }
474 
475 template <typename T>
476 constexpr bool operator>=(const T &X, const Optional<T> &Y) {
477  return !(X < Y);
478 }
479 
480 raw_ostream &operator<<(raw_ostream &OS, NoneType);
481 
482 template <typename T, typename = decltype(std::declval<raw_ostream &>()
483  << std::declval<const T &>())>
484 raw_ostream &operator<<(raw_ostream &OS, const Optional<T> &O) {
485  if (O)
486  OS << *O;
487  else
488  OS << None;
489  return OS;
490 }
491 
492 } // end namespace llvm
493 
494 #endif // LLVM_ADT_OPTIONAL_H
llvm::optional_detail::OptionalStorage::getValue
constexpr const T & getValue() const LLVM_LVALUE_FUNCTION noexcept
Definition: Optional.h:100
llvm::optional_detail::OptionalStorage::OptionalStorage
constexpr OptionalStorage(OptionalStorage &&other)
Definition: Optional.h:77
llvm::Optional< uint64_t >::value_type
uint64_t value_type
Definition: Optional.h:243
llvm
Definition: AllocatorList.h:23
llvm::Optional::Optional
constexpr Optional(NoneType)
Definition: Optional.h:246
llvm::Function
Definition: Function.h:61
llvm::operator<=
bool operator<=(int64_t V1, const APSInt &V2)
Definition: APSInt.h:341
llvm::Optional::Optional
constexpr Optional(const T &y)
Definition: Optional.h:248
llvm::optional_detail::OptionalStorage::operator=
OptionalStorage & operator=(OptionalStorage &&other)
Definition: Optional.h:150
llvm::Optional::operator*
T & operator*() LLVM_LVALUE_FUNCTION
Definition: Optional.h:292
llvm::optional_detail::OptionalStorage< T, true >::getValue
T & getValue() LLVM_LVALUE_FUNCTION noexcept
Definition: Optional.h:196
llvm::optional_detail::OptionalStorage::value
T value
Definition: Optional.h:63
llvm::Optional::operator=
Optional & operator=(T &&y)
Definition: Optional.h:255
llvm::optional_detail::OptionalStorage::OptionalStorage
constexpr OptionalStorage() noexcept
Definition: Optional.h:70
llvm::operator!=
bool operator!=(uint64_t V1, const APInt &V2)
Definition: APInt.h:2039
llvm::Optional
Definition: APInt.h:33
LLVM_LVALUE_FUNCTION
#define LLVM_LVALUE_FUNCTION
Expands to '&' if ref-qualifiers for *this are supported.
Definition: Compiler.h:114
T
#define T
Definition: Mips16ISelLowering.cpp:341
Hashing.h
llvm::optional_detail::OptionalStorage::getValue
T & getValue() LLVM_LVALUE_FUNCTION noexcept
Definition: Optional.h:96
llvm::hash_value
hash_code hash_value(const APFloat &Arg)
See friend declarations above.
Definition: APFloat.cpp:4803
llvm::optional_detail::OptionalStorage< T, true >::emplace
void emplace(Args &&... args)
Definition: Optional.h:211
new
Common register allocation spilling lr str ldr sxth r3 ldr mla r4 can lr mov lr str ldr sxth r3 mla r4 and then merge mul and lr str ldr sxth r3 mla r4 It also increase the likelihood the store may become dead bb27 Successors according to LLVM ID Predecessors according to mbb< bb27, 0x8b0a7c0 > Note ADDri is not a two address instruction its result reg1037 is an operand of the PHI node in bb76 and its operand reg1039 is the result of the PHI node We should treat it as a two address code and make sure the ADDri is scheduled after any node that reads reg1039 Use info(i.e. register scavenger) to assign it a free register to allow reuse the collector could move the objects and invalidate the derived pointer This is bad enough in the first but safe points can crop up unpredictably **array_addr i32 n y store obj * new
Definition: README.txt:125
F
#define F(x, y, z)
Definition: MD5.cpp:56
llvm::Optional::getPointer
constexpr const T * getPointer() const
Definition: Optional.h:278
llvm::Optional::hasValue
constexpr bool hasValue() const
Definition: Optional.h:286
llvm::optional_detail::in_place_t
Definition: Optional.h:33
Y
static GCMetadataPrinterRegistry::Add< OcamlGCMetadataPrinter > Y("ocaml", "ocaml 3.10-compatible collector")
false
Definition: StackSlotColoring.cpp:142
llvm::optional_detail::OptionalStorage::OptionalStorage
constexpr OptionalStorage(OptionalStorage const &other)
Definition: Optional.h:72
llvm::raw_ostream
This class implements an extremely fast bulk output stream that can only output to a stream.
Definition: raw_ostream.h:50
llvm::operator<<
raw_ostream & operator<<(raw_ostream &OS, const APFixedPoint &FX)
Definition: APFixedPoint.h:230
llvm::Optional::create
static constexpr Optional create(const T *y)
Definition: Optional.h:266
llvm::None
const NoneType None
Definition: None.h:23
X
static GCMetadataPrinterRegistry::Add< ErlangGCPrinter > X("erlang", "erlang-compatible garbage collector")
llvm::Optional::Optional
constexpr Optional(T &&y)
Definition: Optional.h:251
llvm::Optional::getValueOr
constexpr T getValueOr(U &&value) const LLVM_LVALUE_FUNCTION
Definition: Optional.h:295
llvm::operator>=
bool operator>=(int64_t V1, const APSInt &V2)
Definition: APSInt.h:342
llvm::RISCVFenceField::O
@ O
Definition: RISCVBaseInfo.h:132
llvm::optional_detail::OptionalStorage< T, true >::OptionalStorage
constexpr OptionalStorage(in_place_t, Args &&... args)
Definition: Optional.h:184
llvm::Optional::reset
void reset()
Definition: Optional.h:276
llvm::NoneType
NoneType
A simple null object to allow implicit construction of Optional<T> and similar types without having t...
Definition: None.h:22
const
aarch64 promote const
Definition: AArch64PromoteConstant.cpp:232
llvm::Optional::emplace
void emplace(ArgTypes &&... Args)
Create a new object by constructing it in place with the given arguments.
Definition: Optional.h:262
move
compiles ldr LCPI1_0 ldr ldr mov lsr tst moveq r1 ldr LCPI1_1 and r0 bx lr It would be better to do something like to fold the shift into the conditional move
Definition: README.txt:546
llvm::operator<
bool operator<(int64_t V1, const APSInt &V2)
Definition: APSInt.h:343
llvm::Optional::getPointer
T * getPointer()
Definition: Optional.h:279
llvm::optional_detail::OptionalStorage< T, true >::hasValue
constexpr bool hasValue() const noexcept
Definition: Optional.h:194
llvm::optional_detail::OptionalStorage::~OptionalStorage
~OptionalStorage()
Definition: Optional.h:68
assert
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
llvm::optional_detail::OptionalStorage::OptionalStorage
constexpr OptionalStorage(in_place_t, Args &&... args)
Definition: Optional.h:84
llvm::operator==
bool operator==(uint64_t V1, const APInt &V2)
Definition: APInt.h:2037
llvm::optional_detail::OptionalStorage::operator=
OptionalStorage & operator=(T const &y)
Definition: Optional.h:117
llvm::Optional::map
auto map(const Function &F) const LLVM_LVALUE_FUNCTION -> Optional< decltype(F(getValue()))>
Apply a function to the value if present; otherwise return None.
Definition: Optional.h:301
llvm::optional_detail::OptionalStorage< T, true >::OptionalStorage
constexpr OptionalStorage() noexcept
Definition: Optional.h:175
llvm::optional_detail::OptionalStorage< T, true >::getValue
constexpr const T & getValue() const LLVM_LVALUE_FUNCTION noexcept
Definition: Optional.h:200
None.h
llvm::optional_detail::OptionalStorage::reset
void reset() noexcept
Definition: Optional.h:87
Compiler.h
llvm::optional_detail::OptionalStorage::operator=
OptionalStorage & operator=(OptionalStorage const &other)
Definition: Optional.h:136
llvm::Optional::operator->
constexpr const T * operator->() const
Definition: Optional.h:287
llvm::optional_detail::OptionalStorage::hasValue
constexpr bool hasValue() const noexcept
Definition: Optional.h:94
llvm::optional_detail::OptionalStorage< T, true >::operator=
OptionalStorage & operator=(T &&y)
Definition: Optional.h:226
llvm::cl::Optional
@ Optional
Definition: CommandLine.h:119
std
Definition: BitVector.h:838
type_traits.h
llvm::Optional::Optional
constexpr Optional()
Definition: Optional.h:245
llvm::optional_detail::OptionalStorage< T, true >::value
T value
Definition: Optional.h:168
llvm::optional_detail::OptionalStorage::operator=
OptionalStorage & operator=(T &&y)
Definition: Optional.h:126
llvm::Optional::operator->
T * operator->()
Definition: Optional.h:288
y
into llvm powi allowing the code generator to produce balanced multiplication trees the intrinsic needs to be extended to support and second the code generator needs to be enhanced to lower these to multiplication trees Interesting testcase for add shift mul int y
Definition: README.txt:61
llvm::optional_detail::OptionalStorage::empty
char empty
Definition: Optional.h:62
llvm::Optional::getValue
T & getValue() LLVM_LVALUE_FUNCTION
Definition: Optional.h:283
llvm::hash_combine
hash_code hash_combine(const Ts &...args)
Combine values into a single hash_code.
Definition: Hashing.h:604
llvm::Optional::operator=
Optional & operator=(const T &y)
Definition: Optional.h:270
llvm::operator>
bool operator>(int64_t V1, const APSInt &V2)
Definition: APSInt.h:344
llvm::optional_detail::OptionalStorage< T, true >::empty
char empty
Definition: Optional.h:167
llvm::optional_detail::OptionalStorage::emplace
void emplace(Args &&... args)
Definition: Optional.h:111
llvm::AMDGPU::HSAMD::Kernel::Key::Args
constexpr char Args[]
Key for Kernel::Metadata::mArgs.
Definition: AMDGPUMetadata.h:389
llvm::optional_detail::OptionalStorage< T, true >::reset
void reset() noexcept
Definition: Optional.h:187
llvm::optional_detail::OptionalStorage< T, true >::operator=
OptionalStorage & operator=(T const &y)
Definition: Optional.h:217
true
basic Basic Alias true
Definition: BasicAliasAnalysis.cpp:1797
llvm::optional_detail::OptionalStorage
Storage for any type.
Definition: Optional.h:60
llvm::Optional::operator*
constexpr const T & operator*() const LLVM_LVALUE_FUNCTION
Definition: Optional.h:289
llvm::Optional::getValue
constexpr const T & getValue() const LLVM_LVALUE_FUNCTION
Definition: Optional.h:280
llvm::hash_code
An opaque object representing a hash code.
Definition: Hashing.h:72