XRTraits C++ OpenXR Utilities
DynamicVerified.h
Go to the documentation of this file.
1 // Copyright 2018-2019, Collabora, Ltd.
2 // SPDX-License-Identifier: BSL-1.0
3 /*!
4  * @file
5  * @brief Header providing type-enforced verification of OpenXR "tagged types".
6  * @author Ryan Pavlik <ryan.pavlik@collabora.com>
7  */
8 
9 #pragma once
10 
11 #include "Common.h"
12 
13 // Needs exceptions and constexpr-if
14 #if defined(XRTRAITS_HAVE_EXCEPTIONS) && defined(XRTRAITS_HAVE_CONSTEXPR_IF)
15 
16 /*!
17  * Defined after including DynamicVerified.h if the compiler and config
18  * requirements are satisfied to provide at least some DynamicVerified functionality.
19  */
20 #define XRTRAITS_HAVE_DYNAMIC_VERIFIED
21 
22 // Internal Includes
23 #include "Attributes.h"
24 #include "Common.h"
25 #include "casts/GetChained.h"
27 #include "casts/TaggedRiskyCast.h"
28 #include "exceptions/TypeError.h"
29 #include "traits/APITraits.h"
30 
31 // Library Includes
32 #include "GSLWrap.h"
33 #include <openxr/openxr.h>
34 
35 // Standard Includes
36 #include <functional>
37 
38 #ifdef XRTRAITS_DOXYGEN
39 /*! If defined, DynamicVerified re-checks the type member of the struct it wraps
40  * on every access, throwing in case of a mismatch.
41  *
42  * @relates xrtraits::DynamicVerified
43  */
44 #define XRTRAITS_DYNAMIC_VERIFIED_ALWAYS_RECHECK
45 #endif // XRTRAITS_DOXYGEN
46 
47 #ifdef XRTRAITS_DYNAMIC_VERIFIED_ALWAYS_RECHECK
48 /*! noexcept if XRTRAITS_DYNAMIC_VERIFIED_ALWAYS_RECHECK is not defined, empty
49  * otherwise
50  *
51  * @relates xrtraits::DynamicVerified
52  */
53 #define XRTRAITS_NOEXCEPT_IF_NOT_RECHECKING
54 #else
55 #define XRTRAITS_NOEXCEPT_IF_NOT_RECHECKING noexcept
56 #endif
57 
58 namespace xrtraits {
59 
60 /*! A wrapper for OpenXR types that have had their `type` member's value (and
61  * thus their "dynamic type") checked, or are a null pointer.
62  *
63  * Acts like a pointer to T - can compare to nullptr, use -> to access members
64  * of T, *thing to dereference... Is also sized the exact same as a raw
65  * pointer, so no space overhead.
66  *
67  * Pass by value, not by pointer or reference.
68  *
69  * Typically created with one of the following free functions:
70  *
71  * - verifyDynamicTypeOrNull() when you care about head of the chain and
72  * the dynamic type matching the pre-existing static type.
73  * - XRTRAITS_VERIFY_DYNAMIC_TYPE_OR_NULL() macro wrapping the above,
74  * stringizing its argument for better error messages when used in entry points.
75  * - tryGetFromChain<T>() when you are looking anywhere in the chain if it
76  * happens to be non-null and if it happens to include the desired type.
77  * - required_verified_or_null_cast<T>() when you are looking at a
78  * (possibly null) pointer with a static type that you don't want and that
79  * you know doesn't match its dynamic type.
80  *
81  * @see DynamicVerified The "stronger" version of this type, for passing
82  * required parameters.
83  *
84  * @ingroup TypeVerification
85  */
86 template <typename T> class DynamicVerifiedOrNull;
87 
88 /*! A wrapper for OpenXR types that have had their `type` member's value (and
89  * thus their "dynamic type") checked.
90  *
91  * Acts like a pointer to T - can use -> to access members of T, *thing to
92  * dereference, etc. (Can compare to nullptr too, but this is never equal to
93  * nullptr.)
94  *
95  * Is also sized the exact same as a raw pointer, so no space overhead.
96  *
97  * Pass by value, not by pointer or reference.
98  *
99  * Inherits from DynamicVerifiedOrNull, because it provides a strict superset
100  * of the guarantees of DynamicVerifiedOrNull.
101  *
102  * Note this is just an observer and does **not** convey ownership.
103  *
104  * Typically created with one of the following free functions:
105  *
106  * - verifyDynamicType() when you care about head of the chain and
107  * the dynamic type matching the pre-existing static type.
108  * - XRTRAITS_VERIFY_DYNAMIC_TYPE() macro wrapping the above, stringizing its
109  * argument for better error messages when used in entry points.
110  * - getFromChain<T>() when you are looking anywhere in the chain.
111  * - required_verified_cast<T>() when you are looking at a pointer with a
112  * static type that you don't want and that you know doesn't match its
113  * dynamic type.
114  * - ensureNotNull() to upgrade a DynamicVerifiedOrNull.
115  *
116  * @see DynamicVerifiedOrNull The "weaker" version of this type, for passing
117  * optional parameters.
118  *
119  * @ingroup TypeVerification
120  */
121 template <typename T> class DynamicVerified;
122 
123 /*! Check that either nullptr was passed, or that the type member's value
124  * (dynamic type) of a struct matches its static type as expected.
125  *
126  * This is the weaker relative of verifyDynamicType(), for optional parameters.
127  *
128  * @throw exceptions::type_error if type member has mis-matched value for
129  * non-nullptr argument.
130  *
131  * @param param Possibly-null pointer to an XR tagged type with known tag
132  * @param paramName Parameter name as defined in API, if known (optional).
133  * @param errorCode Error result code to include in exception if this throws
134  * (optional, defaults to validation failure).
135  *
136  * @return A wrapper for a reference to the supplied struct, to indicate
137  * statically that the dynamic type has been verified or that it is null.
138  *
139  * @ingroup TypeVerification
140  * @relatesalso DynamicVerifiedOrNull
141  */
142 template <typename T>
143 XRTRAITS_NODISCARD constexpr DynamicVerifiedOrNull<T> verifyDynamicTypeOrNull(
144  T* const& param, const char* paramName = nullptr,
145  XrResult errorCode = exceptions::DEFAULT_TYPE_ERROR_CODE);
146 
147 /*! Check that the type member's value (dynamic type) of a struct matches its
148  * static type as expected (pointer overload).
149  *
150  * This is the stronger relative of verifyDynamicTypeOrNull(), for non-optional
151  * parameters.
152  *
153  * @throw exceptions::type_error if nullptr is passed or type member has
154  * mis-matched value.
155  *
156  * @param param Pointer to an XR tagged type with known tag
157  * @param paramName Parameter name as defined in API, if known (optional).
158  * @param errorCode Error result code to include in exception if this throws
159  * (optional, defaults to validation failure).
160  *
161  * @return A wrapper for a reference to the supplied struct, to indicate
162  * statically that the dynamic type has been verified.
163  *
164  * @ingroup TypeVerification
165  * @relatesalso DynamicVerified
166  */
167 template <typename T>
168 XRTRAITS_NODISCARD constexpr DynamicVerified<T>
169 verifyDynamicType(T* param, const char* paramName = nullptr,
170  XrResult errorCode = exceptions::DEFAULT_TYPE_ERROR_CODE);
171 
172 /*! Check that the type member's value (dynamic type) of a struct matches its
173  * static type as expected (reference overload).
174  *
175  * This is the stronger relative of verifyDynamicTypeOrNull(), for non-optional
176  * parameters.
177  *
178  * @throw exceptions::type_error if type member has mis-matched value.
179  *
180  * @param param Reference to an XR tagged type with known tag
181  * @param paramName Parameter name as defined in API, if known (optional).
182  * @param errorCode Error result code to include in exception if this throws
183  * (optional, defaults to validation failure).
184  *
185  * @return A wrapper for a reference to the supplied struct, to indicate
186  * statically that the dynamic type has been verified.
187  *
188  * @ingroup TypeVerification
189  * @relatesalso DynamicVerified
190  */
191 template <typename T>
192 XRTRAITS_NODISCARD constexpr DynamicVerified<T>
193 verifyDynamicType(T& param, const char* paramName = nullptr,
194  XrResult errorCode = exceptions::DEFAULT_TYPE_ERROR_CODE);
195 
196 /*! Re-interpret the given pointer as a DynamicVerified of the supplied type, as
197  * long as the dynamic type matches.
198  *
199  * Related to verifyDynamicType(), but this one requires a type parameter to be
200  * explicitly specified.
201  *
202  * @throw exceptions::type_error if nullptr is passed or type member has
203  * mis-matched value.
204  *
205  * @tparam T OpenXR type with known tag to try casting to.
206  * @param param pointer to some XR tagged type, or nullptr
207  * @param paramName Parameter name as defined in API, if known (optional).
208  * @param errorCode Error result code to include in exception if this throws
209  * (optional, defaults to validation failure).
210  *
211  * @return A wrapper for a reference to the supplied struct, to indicate
212  * statically that the dynamic type has been verified.
213  *
214  * @ingroup TypeVerification
215  * @relatesalso DynamicVerified
216  */
217 template <typename T, typename U>
218 XRTRAITS_NODISCARD constexpr DynamicVerified<T> required_verified_cast(
219  U* const& param, const char* paramName = nullptr,
220  XrResult errorCode = exceptions::DEFAULT_TYPE_ERROR_CODE);
221 
222 /*! Re-interpret the given reference as a DynamicVerified of the supplied type,
223  * as long as the dynamic type matches.
224  *
225  * Related to verifyDynamicType(), but this one requires a type parameter to be
226  * explicitly specified.
227  *
228  * @throw exceptions::type_error if type member has
229  * mis-matched value.
230  *
231  * @tparam T OpenXR type with known tag to try casting to.
232  * @param param reference to some XR tagged type
233  * @param paramName Parameter name as defined in API, if known (optional).
234  * @param errorCode Error result code to include in exception if this throws
235  * (optional, defaults to validation failure).
236  *
237  * @return A wrapper for a reference to the supplied struct, to indicate
238  * statically that the dynamic type has been verified.
239  *
240  * @ingroup TypeVerification
241  * @relatesalso DynamicVerified
242  */
243 template <typename T, typename U>
244 XRTRAITS_NODISCARD constexpr DynamicVerified<T> required_verified_cast(
245  U& param, const char* paramName = nullptr,
246  XrResult errorCode = exceptions::DEFAULT_TYPE_ERROR_CODE);
247 
248 /*! Re-interpret the given pointer as a DynamicVerifiedOrNull of the supplied
249  * type, as long as the dynamic type matches or the input is nullptr.
250  *
251  * Related to verifyDynamicType(), but this one requires a type parameter to be
252  * explicitly specified.
253  *
254  * @throw exceptions::type_error if non-null and type member has mis-matched
255  * value.
256  *
257  * @tparam T OpenXR type with known tag to try casting to.
258  * @param param pointer to some XR tagged type, or nullptr
259  * @param paramName Parameter name as defined in API, if known (optional).
260  * @param errorCode Error result code to include in exception if this throws
261  * (optional, defaults to validation failure).
262  *
263  * @ingroup TypeVerification
264  * @relatesalso DynamicVerified
265  */
266 template <typename T, typename U>
267 XRTRAITS_NODISCARD constexpr DynamicVerifiedOrNull<T>
268 required_verified_or_null_cast(
269  U* param, const char* paramName = nullptr,
270  XrResult errorCode = exceptions::DEFAULT_TYPE_ERROR_CODE);
271 
272 /*! Upgrade DynamicVerifiedOrNull to DynamicVerified.
273  *
274  * @throw exceptions::type_error if input is nullptr.
275  *
276  * @param param DynamicVerifiedOrNull<T> object referencing something non-null.
277  * @param paramName Parameter name as defined in API, if known (optional).
278  * @param errorCode Error result code to include in exception if this throws
279  * (optional, defaults to validation failure).
280  *
281  * @return A DynamicVerified wrapper for a reference to the supplied struct, to
282  * indicate statically that the dynamic type has been verified.
283  *
284  * @ingroup TypeVerification
285  * @relates DynamicVerifiedOrNull
286  */
287 template <typename T>
288 XRTRAITS_NODISCARD constexpr DynamicVerified<T>
289 ensureNotNull(DynamicVerifiedOrNull<T> const& param,
290  const char* paramName = nullptr,
291  XrResult errorCode = exceptions::DEFAULT_TYPE_ERROR_CODE);
292 
293 /*! Given an OpenXR structure type, get another type from its next chain -
294  * pointer head overload.
295  *
296  * @throw exceptions::get_from_chain_error (derived from
297  * exceptions::type_error) if no struct of the right type exists on the chain.
298  *
299  * @param param Head of the chain
300  * @param paramName Parameter name as defined in API, if known (optional).
301  * @param errorCode Error result code to include in exception if this throws
302  * (optional, defaults to validation failure).
303  *
304  * @return The desired struct, wrapped in DynamicVerified to indicate
305  * statically that the dynamic type has been verified.
306  *
307  * @ingroup TypeVerification
308  * @relatesalso DynamicVerified
309  */
310 template <typename TargetType, typename SourceType>
311 XRTRAITS_NODISCARD constexpr DynamicVerified<TargetType>
312 getFromChain(SourceType* param, const char* paramName = nullptr,
313  XrResult errorCode = exceptions::DEFAULT_TYPE_ERROR_CODE);
314 
315 /*! Given an OpenXR structure type, get another type from its next chain -
316  * reference head overload.
317  *
318  * @throw exceptions::get_from_chain_error (derived from
319  * exceptions::type_error) if no struct of the right type exists on the chain.
320  *
321  * @param param Head of the chain
322  * @param paramName Parameter name as defined in API, if known (optional).
323  * @param errorCode Error result code to include in exception if this throws
324  * (optional, defaults to validation failure).
325  *
326  * @return The desired struct, wrapped in DynamicVerified to indicate
327  * statically that the dynamic type has been verified.
328  *
329  * @ingroup TypeVerification
330  * @relatesalso DynamicVerified
331  */
332 template <typename TargetType, typename SourceType>
333 XRTRAITS_NODISCARD constexpr std::enable_if_t<
334  traits::is_xr_tagged_type_v<SourceType>, DynamicVerified<TargetType>>
335 getFromChain(SourceType& param, const char* paramName = nullptr,
336  XrResult errorCode = exceptions::DEFAULT_TYPE_ERROR_CODE);
337 
338 /*! Given an OpenXR structure type, get another type from its next chain -
339  * DynamicVerified head overload.
340  *
341  * @throw exceptions::get_from_chain_error (derived from
342  * exceptions::type_error) if no struct of the right type exists on the chain.
343  *
344  * @param param Head of the chain
345  * @param paramName Parameter name as defined in API, if known (optional).
346  * @param errorCode Error result code to include in exception if this throws
347  * (optional, defaults to validation failure).
348  *
349  * @return The desired struct, wrapped in DynamicVerified to indicate
350  * statically that the dynamic type has been verified.
351  *
352  * @ingroup TypeVerification
353  * @relatesalso DynamicVerified
354  */
355 template <typename TargetType, typename SourceType>
356 XRTRAITS_NODISCARD constexpr DynamicVerified<TargetType>
357 getFromChain(DynamicVerified<SourceType> const& param,
358  const char* paramName = nullptr,
359  XrResult errorCode = exceptions::DEFAULT_TYPE_ERROR_CODE);
360 
361 /*! Given an OpenXR structure type, try to get another type from its next chain
362  * - pointer head overload.
363  *
364  * @param param Head of the chain (may be nullptr)
365  *
366  * @return The desired struct if found, wrapped in DynamicVerifiedOrNull to
367  * indicate statically that the dynamic type has been verified, or a
368  * DynamicVerifiedOrNull initialized with nullptr if not found.
369  *
370  * @return The desired struct if found, wrapped in DynamicVerifiedOrNull to
371  * indicate statically that the dynamic type has been verified, or a
372  * DynamicVerifiedOrNull initialized with nullptr if not found.
373  *
374  * @ingroup TypeVerification
375  * @relatesalso DynamicVerifiedOrNull
376  */
377 template <typename TargetType, typename SourceType>
378 XRTRAITS_NODISCARD constexpr DynamicVerifiedOrNull<TargetType>
379 tryGetFromChain(SourceType* param) noexcept;
380 
381 /*! Given an OpenXR structure type, try to get another type from its next chain
382  * - reference head overload.
383  *
384  * @param param Head of the chain
385  *
386  * @return The desired struct if found, wrapped in DynamicVerifiedOrNull to
387  * indicate statically that the dynamic type has been verified, or a
388  * DynamicVerifiedOrNull initialized with nullptr if not found.
389  *
390  * @ingroup TypeVerification
391  * @relatesalso DynamicVerifiedOrNull
392  */
393 template <typename TargetType, typename SourceType>
394 XRTRAITS_NODISCARD constexpr std::enable_if_t<
395  traits::is_xr_tagged_type_v<SourceType>, DynamicVerifiedOrNull<TargetType>>
396 tryGetFromChain(SourceType& param) noexcept;
397 
398 /*! Given an OpenXR structure type, try to get another type from its next chain
399  * - DynamicVerified head overload.
400  *
401  * @param param Head of the chain (may be nullptr)
402  *
403  * @return The desired struct if found, wrapped in DynamicVerifiedOrNull to
404  * indicate statically that the dynamic type has been verified, or a
405  * DynamicVerifiedOrNull initialized with nullptr if not found.
406  *
407  * @ingroup TypeVerification
408  * @relatesalso DynamicVerifiedOrNull
409  */
410 template <typename TargetType, typename SourceType>
411 XRTRAITS_NODISCARD inline constexpr DynamicVerifiedOrNull<TargetType>
412 tryGetFromChain(DynamicVerifiedOrNull<SourceType> const& param) noexcept;
413 
414 } // namespace xrtraits
415 
416 /*! Wrapper for xrtraits::verifyDynamicType() that stringizes its argument.
417  *
418  * @ingroup TypeVerification
419  * @relatesalso xrtraits::DynamicVerified
420  */
421 #define XRTRAITS_VERIFY_DYNAMIC_TYPE(PARAM) \
422  ::xrtraits::verifyDynamicType(PARAM, #PARAM)
423 
424 /*! Wrapper for xrtraits::verifyDynamicTypeOrNull() that stringizes its
425  * argument.
426  *
427  * @ingroup TypeVerification
428  * @relatesalso xrtraits::DynamicVerifiedOrNull
429  */
430 #define XRTRAITS_VERIFY_DYNAMIC_TYPE_OR_NULL(PARAM) \
431  ::xrtraits::verifyDynamicTypeOrNull(PARAM, #PARAM)
432 
433 namespace xrtraits {
434 
435 /*! Behaves like a gsl::span<T> except that it enforces that the dynamic type of
436  * each array member matches the static type.
437  */
438 template <typename T> class DynamicVerifiedSpan;
439 
440 /*! Similar to gsl::make_span, but the resulting span has its dynamic type
441  * verified.
442  */
443 template <typename T>
444 XRTRAITS_NODISCARD inline constexpr DynamicVerifiedSpan<T>
445 make_dynamic_verified_span(
446  T* pointer, std::ptrdiff_t size, const char* paramName = nullptr,
447  XrResult errorCode = exceptions::DEFAULT_TYPE_ERROR_CODE);
448 
449 } // namespace xrtraits
450 
451 /*! Wrapper for xrtraits::make_polymorphic_span() that stringizes its pointer
452  * argument.
453  *
454  * @ingroup TypeVerification
455  * @relatesalso xrtraits::PolymorphicSpan
456  */
457 #define XRTRAITS_MAKE_POLYMORPHIC_SPAN(PARAM, SIZE) \
458  ::xrtraits::make_polymorphic_span(PARAM, SIZE, #PARAM)
459 
460 /*! Wrapper for xrtraits::make_dynamic_verified_span() that stringizes its
461  * pointer argument.
462  *
463  * @ingroup TypeVerification
464  * @relatesalso xrtraits::DynamicVerifiedSpan
465  */
466 #define XRTRAITS_MAKE_DYNAMIC_VERIFIED_SPAN(PARAM, SIZE) \
467  ::xrtraits::make_dynamic_verified_span(PARAM, SIZE, #PARAM)
468 
469 namespace xrtraits {
470 #ifndef XRTRAITS_DOXYGEN
471 namespace detail {
472  /*! Tag type indicating that we want to look for a given type anywhere
473  * in the chain
474  */
475  struct GetFromChain_t
476  {
477  constexpr explicit GetFromChain_t() = default;
478  };
479 
480  /*! Tag argument indicating that we want to look for a given type
481  * anywhere in the chain.
482  */
483  constexpr GetFromChain_t get_from_chain{};
484 
485  /*! Function used internally in the get-from-chain constructor of
486  * DynamicVerified.
487  */
488  template <typename T, typename U>
489  inline constexpr T& getFromChainImpl(U* param, const char* paramName,
490  XrResult errorCode)
491  {
492  static_assert(
493  traits::is_xr_tagged_type_v<U>,
494  "Can only use getFromChain() on types known to have type "
495  "and next members.");
496 
497  if (nullptr == param) {
498  throw exceptions::get_from_chain_error(
499  paramName, nullptr, traits::xr_type_tag_v<T>,
500  errorCode);
501  }
502  auto* typed = casts::xr_get_chained_struct<T*>(param);
503  if (nullptr == typed) {
504  throw exceptions::get_from_chain_error(
505  paramName, param->type, traits::xr_type_tag_v<T>,
506  errorCode);
507  }
508  return *typed;
509  }
510  /*! Function used internally in the try-get-from-chain constructor of
511  * DynamicVerifiedOrNull.
512  */
513  template <typename T, typename U>
514  inline constexpr T* tryGetFromChainImpl(U* param) noexcept
515  {
516  static_assert(traits::is_xr_tagged_type_v<U>,
517  "Can only use tryGetFromChain() on types known "
518  "to have type and next members.");
519  static_assert(
520  !is_pointer_v<T>,
521  "Type parameter to tryGetFromChain() should just be the "
522  "structure type, not a pointer type.");
523  static_assert(
524  !is_reference_v<T>,
525  "Type parameter to tryGetFromChain() should just be the "
526  "structure type, not a reference type.");
527 
528  if (nullptr == param) {
529  return nullptr;
530  }
531 
532  auto* typed = casts::xr_get_chained_struct<T*>(param);
533 
534  return typed;
535  }
536 } // namespace detail
537 
538 #endif // !XRTRAITS_DOXYGEN
539 
540 template <typename T> class DynamicVerifiedOrNull
541 {
542 public:
543  static_assert(
544  traits::has_xr_type_tag_v<T>,
545  "Can only use verifyDynamicTypeOrNull and DynamicVerifiedOrNull on "
546  "types with an associated XrStructureType enum value.");
547 
548  /*! Constructor from pointer to wrapped type.
549  *
550  * If this directly corresponds to a consumer-supplied parameter, pass
551  * the parameter name for nicer error messages.
552  *
553  * @note Prefer calling xrtraits::verifyDynamicTypeOrNull() to create
554  * objects of this type.
555  */
556  constexpr DynamicVerifiedOrNull(T* param, const char* paramName,
557  XrResult errorCode)
558  : wrapped_(param)
559  {
560  if ((wrapped_ != nullptr) &&
561  (wrapped_->type != traits::xr_type_tag_v<T>)) {
562  throw exceptions::type_error(paramName, wrapped_->type,
563  traits::xr_type_tag_v<T>,
564  errorCode);
565  }
566  }
567 
568  /*! Constructor from reference to wrapped type.
569  *
570  * If this directly corresponds to a consumer-supplied parameter, pass
571  * the parameter name for nicer error messages.
572  *
573  * @note Prefer calling xrtraits::verifyDynamicTypeOrNull() to create
574  * objects of this type.
575  */
576  constexpr DynamicVerifiedOrNull(T& param, const char* paramName,
577  XrResult errorCode)
578  : DynamicVerifiedOrNull(std::addressof(param), paramName, errorCode)
579  {}
580 
581  /*! Constructor from nullptr
582  *
583  * @note Prefer calling xrtraits::verifyDynamicTypeOrNull() to create
584  * objects of this type.
585  */
586  constexpr DynamicVerifiedOrNull(std::nullptr_t /*unused*/) noexcept
587  : wrapped_(nullptr)
588  {}
589 
590  /*! "Private" constructor from the head of a chain, when we just want
591  * one member of the chain if it exists.
592  *
593  * @note Prefer calling xrtraits::tryGetFromChain() instead of this
594  * constructor directly.
595  */
596  template <typename U>
597  constexpr DynamicVerifiedOrNull(
598  U* param, detail::GetFromChain_t const& /*tag*/) noexcept
599  : DynamicVerifiedOrNull(detail::tryGetFromChainImpl<T>(param))
600  {}
601 
602  //! Copy-constructible.
603  constexpr DynamicVerifiedOrNull(DynamicVerifiedOrNull const&) noexcept =
604  default;
605 
606  //! Copy-assignable.
607  constexpr DynamicVerifiedOrNull&
608  operator=(DynamicVerifiedOrNull const&) noexcept = default;
609 
610  //! Move-constructible.
611  constexpr DynamicVerifiedOrNull(DynamicVerifiedOrNull&& other) noexcept
612  : DynamicVerifiedOrNull(nullptr)
613  {
614  std::swap(wrapped_, other.wrapped_);
615  }
616 
617  //! Move-assignable.
618  constexpr DynamicVerifiedOrNull&
619  operator=(DynamicVerifiedOrNull&& other) noexcept
620  {
621  wrapped_ = nullptr;
622  std::swap(wrapped_, other.wrapped_);
623  return *this;
624  }
625 
626  // Destructor is default.
627  ~DynamicVerifiedOrNull() = default;
628 
629  /*! Conversion constructor between unrelated types deleted.
630  *
631  * This improves the clarity of error messages if you have a type
632  * mismatch - says you call an explicitly-deleted constructor, instead
633  * of listing all the possible constructors and why they aren't right.
634  *
635  * If you get an error on the line below, you're trying to pass
636  * DynamicVerifiedOrNull<U> as DynamicVerifiedOrNull<T> where T and U
637  * are different types. That's not allowed because it doesn't make
638  * sense in the OpenXR object model.
639  */
640  template <typename U>
641  DynamicVerifiedOrNull(DynamicVerifiedOrNull<U> const&) = delete;
642 
643  //! True if we contain a typed pointer, not nullptr.
644  constexpr bool valid() const { return wrapped_ != nullptr; }
645 
646  //! Retrieve the pointer.
647  constexpr T* get() const
648  {
649  if (!valid()) {
650  throw exceptions::xr_logic_error(
651  "Used a DynamicVerifiedOrNull without checking to "
652  "see if it was null");
653  }
654 #ifdef XRTRAITS_DYNAMIC_VERIFIED_ALWAYS_RECHECK
655  if (wrapped_->type != traits::xr_type_tag_v<T>) {
656  throw exceptions::xr_logic_error(
657  "Something changed the .type member of an OpenXR "
658  "struct after a DynamicVerifiedOrNull wrapper was "
659  "created for it.");
660  }
661 #endif // XRTRAITS_DYNAMIC_VERIFIED_ALWAYS_RECHECK
662  return wrapped_;
663  }
664 
665  /*! "Smart Pointer" operator: for `p->memberName` as if this was just a
666  * raw pointer to the wrapped struct.
667  */
668  constexpr T* operator->() const { return get(); }
669 
670  /*! Dereference operator: for `*p` as if this was just a raw pointer
671  * to the wrapped struct.
672  */
673  constexpr T& operator*() const { return *get(); }
674 
675  /*! Accessor for wrapped pointer that does not check or throw - for
676  * internal use.
677  */
678  T* uncheckedGet() const noexcept { return wrapped_; }
679 
680 protected:
681  /*! Private constructor that skips verification of dynamic type, for
682  * use within this file's implementation only when we've already done
683  * this verification.
684  */
685  explicit constexpr DynamicVerifiedOrNull(T* wrapped) noexcept
686  : wrapped_(wrapped)
687  {
688  GSL_ASSUME((wrapped == nullptr) ||
689  (wrapped->type == traits::xr_type_tag_v<T>));
690  }
691 
692 private:
693  //! Wrapped pointer.
694  T* wrapped_;
695 };
696 
697 /*! Equality comparison operator between DynamicVerifiedOrNull and nullptr.
698  *
699  * @relates DynamicVerifiedOrNull
700  */
701 template <typename T>
702 inline constexpr bool operator==(DynamicVerifiedOrNull<T> const& wrapper,
703  std::nullptr_t /*unused*/)
704 {
705  return !wrapper.valid();
706 }
707 
708 /*! Equality comparison operator between DynamicVerifiedOrNull and nullptr.
709  *
710  * @relates DynamicVerifiedOrNull
711  */
712 template <typename T>
713 inline constexpr bool operator==(std::nullptr_t /*unused*/,
714  DynamicVerifiedOrNull<T> const& wrapper)
715 {
716  return !wrapper.valid();
717 }
718 
719 /*! Inequality comparison operator between DynamicVerifiedOrNull and nullptr.
720  *
721  * @relates DynamicVerifiedOrNull
722  */
723 template <typename T>
724 inline constexpr bool operator!=(DynamicVerifiedOrNull<T> const& wrapper,
725  std::nullptr_t /*unused*/)
726 {
727  return wrapper.valid();
728 }
729 
730 /*! Inequality comparison operator between DynamicVerifiedOrNull and nullptr.
731  *
732  * @relates DynamicVerifiedOrNull
733  */
734 template <typename T>
735 inline constexpr bool operator!=(std::nullptr_t /*unused*/,
736  DynamicVerifiedOrNull<T> const& wrapper)
737 {
738  return wrapper.valid();
739 }
740 
741 template <typename T> class DynamicVerified : public DynamicVerifiedOrNull<T>
742 {
743  using Base = DynamicVerifiedOrNull<T>;
744 
745 public:
746  static_assert(traits::has_xr_type_tag_v<T>,
747  "Can only use verifyDynamicType and DynamicVerified on "
748  "types with an associated XrStructureType enum value.");
749  static_assert(!is_pointer_v<T>,
750  "Can't specify a pointer type as the type parameter to "
751  "DynamicVerified.");
752  static_assert(!is_reference_v<T>,
753  "Can't specify a pointer type as the type parameter to "
754  "DynamicVerified.");
755 
756  /*! Constructor from reference to wrapped type.
757  *
758  * If this directly corresponds to a consumer-supplied parameter, pass
759  * the parameter name for nicer error messages.
760  *
761  * @note Prefer calling xrtraits::verifyDynamicType() to create objects
762  * of this type.
763  */
764  constexpr DynamicVerified(T& wrapped, const char* paramName,
765  XrResult errorCode)
766  : DynamicVerifiedOrNull<T>(std::addressof(wrapped), paramName,
767  errorCode)
768  {}
769 
770  /*! "Private" constructor from the head of a chain, when we just want
771  * one member of the chain.
772  *
773  * @note Prefer calling xrtraits::getFromChain() instead of this
774  * constructor directly.
775  */
776  template <typename U>
777  constexpr DynamicVerified(U* param, const char* paramName,
778  XrResult errorCode,
779  detail::GetFromChain_t const& /*tag*/)
780  : DynamicVerified(
781  detail::getFromChainImpl<T>(param, paramName, errorCode))
782  {
783  Expects(param != nullptr);
784  }
785 
786  /*! Conversion constructor between unrelated types deleted.
787  *
788  * This improves the clarity of error messages if you have a type
789  * mismatch - says you call an explicitly-deleted constructor, instead
790  * of listing all the possible constructors and why they aren't right.
791  *
792  * If you get an error on the line below, you're trying to pass
793  * DynamicVerified<U> as DynamicVerified<T> where T and U
794  * are different types. That's not allowed because it doesn't make
795  * sense in the OpenXR object model.
796  */
797  template <typename U>
798  DynamicVerified(DynamicVerified<U> const&) = delete;
799 
800  //! Copy-constructible.
801  constexpr DynamicVerified(DynamicVerified const&) noexcept = default;
802 
803  //! Copy-assignable.
804  constexpr DynamicVerified&
805  operator=(DynamicVerified const&) noexcept = default;
806 
807  //! Move-constructible.
808  constexpr DynamicVerified(DynamicVerified&&) noexcept = default;
809 
810  //! Move-assignable.
811  constexpr DynamicVerified&
812  operator=(DynamicVerified&&) noexcept = default;
813 
814  // Default destructor
815  ~DynamicVerified() = default;
816 
817  //! Not permitted to construct from nullptr
818  DynamicVerified(std::nullptr_t) = delete;
819 
820  //! Retrieve the pointer.
821  constexpr T* get() const XRTRAITS_NOEXCEPT_IF_NOT_RECHECKING
822  {
823 #ifdef XRTRAITS_DYNAMIC_VERIFIED_ALWAYS_RECHECK
824  if (Base::uncheckedGet()->type != traits::xr_type_tag_v<T>) {
825  throw exceptions::xr_logic_error(
826  "Something changed the .type member of an OpenXR "
827  "struct after a DynamicVerified wrapper was "
828  "created for it.");
829  }
830 #else
831  GSL_ASSUME(Base::uncheckedGet()->type ==
832  traits::xr_type_tag_v<T>);
833 #endif // XRTRAITS_DYNAMIC_VERIFIED_ALWAYS_RECHECK
834  return Base::uncheckedGet();
835  }
836 
837  /*! "Smart Pointer" operator: for `p->memberName` as if this was just a
838  * raw pointer to the wrapped struct.
839  */
840  constexpr T* operator->() const XRTRAITS_NOEXCEPT_IF_NOT_RECHECKING
841  {
842  return get();
843  }
844 
845  /*! Dereference operator: for `*p` as if this was just a raw pointer
846  * to the wrapped struct.
847  */
848  constexpr T& operator*() const XRTRAITS_NOEXCEPT_IF_NOT_RECHECKING
849  {
850  return *get();
851  }
852 
853  /*! Conversion directly from Initialized<T> to DynamicVerified<T> uses
854  * the private constructor.
855  */
856  template <typename U> friend struct Initialized;
857 
858 private:
859  /*! Private constructor that skips verification of dynamic type, for
860  * use within this file's implementation only when we've already done
861  * this verification.
862  */
863  explicit constexpr DynamicVerified(T& wrapped) noexcept
864  : DynamicVerifiedOrNull<T>(std::addressof(wrapped))
865  {
866  GSL_ASSUME(wrapped.type == traits::xr_type_tag_v<T>);
867  }
868 };
869 
870 /*! Equality comparison operator between DynamicVerified and nullptr - always
871  * false.
872  *
873  * @relates DynamicVerified
874  */
875 template <typename T>
876 inline constexpr bool operator==(DynamicVerified<T> const& /*unused*/,
877  std::nullptr_t /*unused*/)
878 {
879  return false;
880 }
881 
882 /*! Equality comparison operator between DynamicVerified and nullptr - always
883  * false.
884  *
885  * @relates DynamicVerified
886  */
887 template <typename T>
888 inline constexpr bool operator==(std::nullptr_t /*unused*/,
889  DynamicVerified<T> const& /*unused*/)
890 {
891  return false;
892 }
893 
894 /*! Inequality comparison operator between DynamicVerified and nullptr - always
895  * true.
896  *
897  * @relates DynamicVerified
898  */
899 template <typename T>
900 inline constexpr bool operator!=(DynamicVerified<T> const& /*unused*/,
901  std::nullptr_t /*unused*/)
902 {
903  return true;
904 }
905 
906 /*! Inequality comparison operator between DynamicVerified and nullptr - always
907  * true.
908  *
909  * @relates DynamicVerified
910  */
911 template <typename T>
912 inline constexpr bool operator!=(std::nullptr_t /*unused*/,
913  DynamicVerified<T> const& /*unused*/)
914 {
915  return true;
916 }
917 
918 // No more doc comments below - most of them have been moved to the top of the
919 // file along with their forward declarations.
920 
921 template <typename T>
922 XRTRAITS_NODISCARD inline constexpr DynamicVerifiedOrNull<T>
923 verifyDynamicTypeOrNull(T* const& param, const char* paramName,
924  XrResult errorCode)
925 {
926  static_assert(traits::has_xr_type_tag_v<T>,
927  "Can only use verifyDynamicTypeOrNull on types with an "
928  "associated XrStructureType enum value.");
929  if (nullptr == param) {
930  return {nullptr};
931  }
932  return {param, paramName, errorCode};
933 }
934 
935 template <typename T>
936 XRTRAITS_NODISCARD inline constexpr DynamicVerified<T>
937 verifyDynamicType(T* param, const char* paramName, XrResult errorCode)
938 {
939  static_assert(traits::has_xr_type_tag_v<T>,
940  "Can only use verifyDynamicType() on types with an "
941  "associated XrStructureType enum value.");
942  if (nullptr == param) {
943  throw exceptions::type_error(
944  paramName, nullptr, traits::xr_type_tag_v<T>, errorCode);
945  }
946  return {*param, paramName, errorCode};
947 }
948 
949 template <typename T>
950 XRTRAITS_NODISCARD inline constexpr DynamicVerified<T>
951 verifyDynamicType(T& param, const char* paramName, XrResult errorCode)
952 {
953  static_assert(traits::has_xr_type_tag_v<T>,
954  "Can only use verifyDynamicType() on types with an "
955  "associated XrStructureType enum value.");
956  return {param, paramName, errorCode};
957 }
958 
959 template <typename T, typename U>
960 XRTRAITS_NODISCARD inline constexpr DynamicVerified<T>
961 required_verified_cast(U* const& param, const char* paramName,
962  XrResult errorCode)
963 {
964  static_assert(traits::has_xr_type_tag_v<T>,
965  "Can only use required_verified_cast() with type params "
966  "that have an associated XrStructureType enum value.");
967  static_assert(!is_const_v<U> || is_const_v<T>,
968  "Can't use required_verify_cast() to cast away const.");
969  static_assert(!is_pointer_v<T>,
970  "Pass just the target type, not a pointer, "
971  "as the type param of required_verified_cast()");
972  static_assert(!is_reference_v<T>,
973  "Pass just the target type, not a reference, "
974  "as the type param of required_verified_cast()");
975 
976  if (nullptr == param) {
977  throw exceptions::type_error(
978  paramName, nullptr, traits::xr_type_tag_v<T>, errorCode);
979  }
980  auto* castResult = casts::xr_tagged_dynamic_cast<T*>(param);
981  if (castResult == nullptr) {
982  throw exceptions::type_error(paramName, param->type,
983  traits::xr_type_tag_v<T>,
984  errorCode);
985  }
986  return {*castResult, paramName, errorCode};
987 }
988 
989 template <typename T, typename U>
990 XRTRAITS_NODISCARD inline constexpr DynamicVerified<T>
991 required_verified_cast(U& param, const char* paramName, XrResult errorCode)
992 {
993  static_assert(traits::has_xr_type_tag_v<T>,
994  "Can only use required_verified_cast() with type params "
995  "that have an associated XrStructureType enum value.");
996  static_assert(!is_const_v<U> || is_const_v<T>,
997  "Can't use required_verify_cast() to cast away const.");
998  static_assert(!is_pointer_v<T>,
999  "Pass just the target type, not a pointer, "
1000  "as the type param of required_verified_cast()");
1001  static_assert(!is_reference_v<T>,
1002  "Pass just the target type, not a reference, "
1003  "as the type param of required_verified_cast()");
1004 
1005  auto* castResult =
1006  casts::xr_tagged_dynamic_cast<T*>(std::addressof(param));
1007  if (castResult == nullptr) {
1008  throw exceptions::type_error(
1009  paramName, param.type, traits::xr_type_tag_v<T>, errorCode);
1010  }
1011  return {*castResult, paramName, errorCode};
1012 }
1013 
1014 template <typename T, typename U>
1015 XRTRAITS_NODISCARD inline constexpr DynamicVerifiedOrNull<T>
1016 required_verified_or_null_cast(U* param, const char* paramName,
1017  XrResult errorCode)
1018 {
1019  static_assert(traits::has_xr_type_tag_v<T>,
1020  "Can only use required_verified_cast() with type params "
1021  "that have an associated XrStructureType enum value.");
1022  static_assert(
1023  !is_const_v<U> || is_const_v<T>,
1024  "Can't use required_verified_or_null_cast() to cast away const.");
1025  if (nullptr == param) {
1026  return {nullptr};
1027  }
1028  auto* castResult = casts::xr_tagged_dynamic_cast<T*>(param);
1029  if (castResult == nullptr) {
1030  throw exceptions::type_error(paramName, param->type,
1031  traits::xr_type_tag_v<T>,
1032  errorCode);
1033  }
1034  return {*castResult, paramName, errorCode};
1035 }
1036 
1037 // Doxygen gets confused about this function, warning unnecessarily, even though
1038 // it does accurately pick up the doc comment on the formward declaration above.
1039 #ifndef XRTRAITS_DOXYGEN
1040 
1041 template <typename T>
1042 XRTRAITS_NODISCARD inline constexpr DynamicVerified<T>
1043 ensureNotNull(DynamicVerifiedOrNull<T> const& param, const char* paramName,
1044  XrResult errorCode)
1045 {
1046  static_assert(traits::has_xr_type_tag_v<T>,
1047  "Can only use ensureNotNull() on types with an "
1048  "associated XrStructureType enum value.");
1049  if (nullptr == param) {
1050  throw exceptions::type_error(
1051  paramName, nullptr, traits::xr_type_tag_v<T>, errorCode);
1052  }
1053  return {*param, paramName, errorCode};
1054 }
1055 
1056 #endif // !XRTRAITS_DOXYGEN
1057 
1058 template <typename TargetType, typename SourceType>
1059 XRTRAITS_NODISCARD inline constexpr DynamicVerified<TargetType>
1060 getFromChain(SourceType* param, const char* paramName, XrResult errorCode)
1061 {
1062  static_assert(traits::is_xr_tagged_type_v<SourceType>,
1063  "Can only use getFromChain() on types known to have type "
1064  "and next members.");
1065  static_assert(!is_pointer_v<TargetType>,
1066  "Type parameter to getFromChain() should just be the "
1067  "structure type, not a pointer type.");
1068  static_assert(!is_reference_v<TargetType>,
1069  "Type parameter to getFromChain() should just be the "
1070  "structure type, not a reference type.");
1071 
1072  return {param, paramName, errorCode, detail::get_from_chain};
1073 }
1074 
1075 template <typename TargetType, typename SourceType>
1076 XRTRAITS_NODISCARD inline constexpr std::enable_if_t<
1077  traits::is_xr_tagged_type_v<SourceType>, DynamicVerified<TargetType>>
1078 getFromChain(SourceType& param, const char* paramName, XrResult errorCode)
1079 {
1080  static_assert(traits::is_xr_tagged_type_v<SourceType>,
1081  "Can only use getFromChain() on types known to have type "
1082  "and next members.");
1083  static_assert(!is_pointer_v<TargetType>,
1084  "Type parameter to getFromChain() should just be the "
1085  "structure type, not a pointer type.");
1086  static_assert(!is_reference_v<TargetType>,
1087  "Type parameter to getFromChain() should just be the "
1088  "structure type, not a reference type.");
1089 
1090  return {std::addressof(param), paramName, errorCode,
1091  detail::get_from_chain};
1092 }
1093 
1094 template <typename TargetType, typename SourceType>
1095 XRTRAITS_NODISCARD inline constexpr DynamicVerified<TargetType>
1096 getFromChain(DynamicVerified<SourceType> const& param, const char* paramName,
1097  XrResult errorCode)
1098 {
1099  static_assert(!is_pointer_v<TargetType>,
1100  "Type parameter to getFromChain() should just be the "
1101  "structure type, not a pointer type.");
1102  static_assert(!is_reference_v<TargetType>,
1103  "Type parameter to getFromChain() should just be the "
1104  "structure type, not a reference type.");
1105  return {param.get(), paramName, errorCode, detail::get_from_chain};
1106 }
1107 
1108 template <typename TargetType, typename SourceType>
1109 XRTRAITS_NODISCARD inline constexpr DynamicVerifiedOrNull<TargetType>
1110 tryGetFromChain(SourceType* param) noexcept
1111 {
1112  static_assert(traits::is_xr_tagged_type_v<SourceType>,
1113  "Can only use tryGetFromChain() on types known to have "
1114  "type and next members.");
1115  static_assert(!is_pointer_v<TargetType>,
1116  "Type parameter to tryGetFromChain() should just be the "
1117  "structure type, not a pointer type.");
1118  static_assert(!is_reference_v<TargetType>,
1119  "Type parameter to tryGetFromChain() should just be the "
1120  "structure type, not a reference type.");
1121 
1122  return {param, detail::get_from_chain};
1123 }
1124 
1125 template <typename TargetType, typename SourceType>
1126 XRTRAITS_NODISCARD inline constexpr std::enable_if_t<
1127  traits::is_xr_tagged_type_v<SourceType>, DynamicVerifiedOrNull<TargetType>>
1128 tryGetFromChain(SourceType& param) noexcept
1129 {
1130  static_assert(traits::is_xr_tagged_type_v<SourceType>,
1131  "Can only use tryGetFromChain() on types known to have "
1132  "type and next members.");
1133  static_assert(!is_pointer_v<TargetType>,
1134  "Type parameter to tryGetFromChain() should just be the "
1135  "structure type, not a pointer type.");
1136  static_assert(!is_reference_v<TargetType>,
1137  "Type parameter to tryGetFromChain() should just be the "
1138  "structure type, not a reference type.");
1139 
1140  return {std::addressof(param), detail::get_from_chain};
1141 }
1142 
1143 template <typename TargetType, typename SourceType>
1144 XRTRAITS_NODISCARD inline constexpr DynamicVerifiedOrNull<TargetType>
1145 tryGetFromChain(DynamicVerifiedOrNull<SourceType> const& param) noexcept
1146 {
1147  static_assert(!is_pointer_v<TargetType>,
1148  "Type parameter to tryGetFromChain() should just be the "
1149  "structure type, not a pointer type.");
1150  static_assert(!is_reference_v<TargetType>,
1151  "Type parameter to tryGetFromChain() should just be the "
1152  "structure type, not a reference type.");
1153  return {param.get(), detail::get_from_chain};
1154 }
1155 #if 0
1156 template <typename T>
1157 class DynamicVerifiedSpan : public span<T, gsl::dynamic_extent>
1158 {
1159 public:
1160  //! Underlying span type.
1161  using base = span<T, gsl::dynamic_extent>;
1162 
1163  //! Type used for size
1164  using size_type = typename base::size_type;
1165 
1166  static_assert(traits::has_xr_type_tag_v<T>,
1167  "Can only use DynamicVerifiedSpan on types with an "
1168  "associated XrStructureType enum value.");
1169 
1170  /*! Constructor - performs type checking of all elements in the array.
1171  *
1172  * @note Prefer make_dynamic_verified_span() to calling this directly.
1173  */
1174  constexpr DynamicVerifiedSpan(T* pointer, size_type sz,
1175  const char* paramName, XrResult errorCode)
1176  : span<T, gsl::dynamic_extent>(pointer, sz)
1177  {
1178  if (pointer == nullptr) {
1179  throw exceptions::type_error(paramName, nullptr,
1180  traits::xr_type_tag_v<T>,
1181  sz, errorCode);
1182  }
1183  for (size_type i = 0; i < sz; ++i) {
1184  const auto t = (*this)[i].type;
1185  if (t != traits::xr_type_tag_v<T>) {
1186  throw exceptions::type_error(
1187  paramName, t, traits::xr_type_tag_v<T>, i,
1188  errorCode);
1189  }
1190  }
1191  }
1192 };
1193 
1194 template <typename T>
1195 XRTRAITS_NODISCARD inline constexpr DynamicVerifiedSpan<T>
1196 make_dynamic_verified_span(T* pointer, std::ptrdiff_t size,
1197  const char* paramName, XrResult errorCode)
1198 {
1199  static_assert(
1200  traits::has_xr_type_tag_v<T>,
1201  "Can only use make_dynamic_verified_span on types with an "
1202  "associated XrStructureType enum value.");
1203  return {pointer, size, paramName, errorCode};
1204 }
1205 } // namespace xrtraits
1206 
1207 #endif
1208 
1209 } // namespace xrtraits
1210 
1211 #undef XRTRAITS_NOEXCEPT_IF_NOT_RECHECKING
1212 
1213 #endif // defined(XRTRAITS_HAVE_EXCEPTIONS) &&
1214  // defined(XRTRAITS_HAVE_CONSTEXPR_IF)
Main namespace for these C++ OpenXR utilities.
Definition: GetChained.h:26
#define XRTRAITS_NODISCARD
Compatibility wrapper for [[nodiscard]]
Definition: Attributes.h:24
TargetType xr_tagged_dynamic_cast(SourceType &&source)
Definition: TaggedDynamicCast.h:53
C++ type traits related to OpenXR, and some other compile-time functionality.
Header providing a slightly-riskier relative of xr_tagged_dynamic_cast() that works on void pointers ...
#define GSL_ASSUME(X)
Definition: GSLWrap.h:23
Header providing xr_get_chained_struct, to extract a struct of a given type from a next chain...
Header defining macros corresponding to various C++ attributes.
Header providing a dynamic_cast equivalent for OpenXR tagged types, xr_tagged_dynamic_cast().
#define Expects(X)
When GSL is available, normally asserts a precondition.
Definition: GSLWrap.h:29
Header with common utilities used by multiple headers.
Header.
Header wrapping or stubbing GSL.