XRTraits C++ OpenXR Utilities
GetChained.h
Go to the documentation of this file.
1 // Copyright 2019, Collabora, Ltd.
2 // SPDX-License-Identifier: BSL-1.0
3 /*!
4  * @file
5  * @brief Header providing xr_get_chained_struct, to extract a struct of a
6  * given type from a next chain
7  * @author Ryan Pavlik <ryan.pavlik@collabora.com>
8  */
9 
10 #pragma once
11 
12 #include "../Common.h"
13 
14 #ifdef XRTRAITS_HAVE_CONSTEXPR_IF
15 
16 // Internal Includes
17 #include "TaggedCastCommon.h"
18 #include "TaggedRiskyCast.h"
19 
20 // Library Includes
21 // - none
22 
23 // Standard Includes
24 // - none
25 
26 namespace xrtraits {
27 
28 namespace casts {
29 #ifndef XRTRAITS_DOXYGEN
30  namespace detail {
31  using ::std::conditional_t;
32  using ::std::enable_if_t;
33  using ::std::is_const;
34  using ::std::is_pointer;
35  using ::std::is_reference;
36  using ::std::is_same;
37  using ::std::remove_pointer_t;
38 
39  /*! Either const void* or void* depending on whether the
40  * source type is a pointer to const or not.
41  */
42  template <typename SourceType>
43  using possibly_const_void_ptr =
44  conditional_t<is_const<remove_pointer_t<SourceType>>::value,
45  const void*, void*>;
46 
47  /*! Given a non-null const void * or void * pointing to
48  * an OpenXR tagged type, retrieve its nested next
49  * pointer, casted to XrBaseInStructure const* or
50  * XrBaseOutStructure* depending on the input constness.
51  */
52  template <typename SourceType>
53  inline auto risky_get_next(SourceType source)
54  {
55  static_assert(
56  is_same_v<SourceType, const void*> ||
57  is_same_v<SourceType, void*>,
58  "risky_get_next is only for void* or const void*");
59  return cast_to_base(source)->next;
60  }
61 
62  /*! Helper function template that performs all static
63  * assertions for xr_get_chained_struct
64  */
65  template <typename TargetType, typename SourceType>
66  inline void xr_get_chained_struct_static_assertions()
67  {
68  constexpr bool is_source_void =
69  is_same_v<remove_reference_pointer_cv_t<SourceType>,
70  void>;
71  constexpr bool is_source_xr_tagged =
72  traits::is_xr_tagged_type_v<SourceType>;
73  constexpr bool is_source_void_or_xr_tagged =
74  is_source_void || is_source_xr_tagged;
75 
76  // This operation requires either a `[const]
77  // void *` input (which it assumes points to an
78  // XR tagged type), or a static type that is
79  // verifiably an XR tagged type.
80  static_assert(
81  is_pointer_v<SourceType> &&
82  is_source_void_or_xr_tagged,
83  "Can only use xr_get_chained_struct to traverse "
84  "starting from a pointer to (possibly const) void, "
85  "or from a pointer to a (possibly const) XR tagged "
86  "type.");
87  static_assert(
88  is_pointer_v<TargetType> &&
89  traits::is_xr_tagged_type_v<TargetType>,
90  "Can only use xr_get_chained_struct to "
91  "find and cast to a pointer to a "
92  "(possibly const) XR tagged type.");
93  // Function determines whether to perform
94  // reinterpret or return nullptr based on
95  // comparing value of `type` to expected value
96  // for the target type, so there must actually
97  // be such an expected value. (Note that we
98  // don't require this for the source type.)
99  static_assert(
100  traits::has_xr_type_tag_v<TargetType>,
101  "Can only use xr_get_chained_struct to find and "
102  "cast to a pointer/reference to a concrete XR "
103  "tagged type with a known, defined type tag value "
104  "(XrStructureType enum value).");
105  static_assert(
106  std::is_const<
107  remove_reference_pointer_t<SourceType>>::value
108  ? std::is_const<remove_reference_pointer_t<
109  TargetType>>::value
110  : true,
111  "Cannot remove const qualification through "
112  "xr_get_chained_struct");
113  }
114 
115  /*! Core implementation of xr_get_chained_struct() that
116  * takes only void * or const void * as input, to unify
117  * cases and reduce template instantiations.
118  */
119  template <typename TargetType, typename SourceType>
120  inline TargetType xr_get_chained_struct_impl(SourceType src)
121  {
122  while (nullptr != src) {
123  auto ret =
124  xr_tagged_risky_cast<TargetType>(src);
125  if (ret) {
126  // we found a match!
127  return ret;
128  }
129  // no type match, so advance.
130  src = risky_get_next(src);
131  }
132  // If we get here, the chain has ended: we're at
133  // a nullptr
134  return nullptr;
135  }
136  } // namespace detail
137 #endif // !XRTRAITS_DOXYGEN
138  /*! Follow the chained next pointers in an OpenXR tagged struct
139  * until we reach the end or find one matching the desired type.
140  *
141  * If no desired type is given, it is assumed to be the same as
142  * the source type, as is used in API entry point
143  * implementations.
144  *
145  * Given a type that is a pointer to a (possibly const) OpenXR
146  * tagged struct with known tag, and an input pointer that is
147  * either (possibly const) void * or a pointer to a (possibly
148  * const) OpenXR tagged struct, iterate through the current
149  * struct and all chained structs found via the next pointer,
150  * until a nullptr is hit or we find one whose tag matches the
151  * tag expected for the target type.
152  *
153  * If a match is found, it is casted to the same "const-ness" as
154  * the input and returned. If no match is found, then nullptr is
155  * returned.
156  *
157  * Unlike xr_tagged_risky_cast() and xr_tagged_dynamic_cast(),
158  * this function has a high likelihood of not finding what it is
159  * looking for, so there is no support for returning a reference
160  * type.
161  *
162  * @see xr_tagged_risky_cast() to which this function is closely
163  * related
164  *
165  * Source parameter is specified as `SourceType * src` instead
166  * of `SourceType src` to avoid getting std::nullptr_t as
167  * SourceType.
168  *
169  * @ingroup Casts
170  */
171  template <typename TargetType, typename SourceType>
172  inline TargetType xr_get_chained_struct(SourceType* src)
173  {
174  // Verify static assertions.
175  detail::xr_get_chained_struct_static_assertions<TargetType,
176  SourceType*>();
177  // turn SourceType into void * or const void *,
178  // whichever is more accurate
179  using type_erased_input_ptr =
180  detail::possibly_const_void_ptr<SourceType*>;
181  // call the implementation function with the [const]
182  // void * casted input
183  return detail::xr_get_chained_struct_impl<TargetType>(
184  static_cast<type_erased_input_ptr>(src));
185  }
186 
187  /*! Overload that doesn't require explicit specification of a
188  * template parameter, for "converting" a type to the type it
189  * already is.
190  *
191  * @ingroup Casts
192  */
193  template <typename T> inline T* xr_get_chained_struct(T* src)
194  {
195  // Verify static assertions.
196  detail::xr_get_chained_struct_static_assertions<T*, T*>();
197 
198  // turn T* into void * or const void *, whichever is
199  // more accurate
200  using type_erased_input_ptr =
201  detail::possibly_const_void_ptr<T*>;
202  // call the implementation function with the [const]
203  // void * casted input
204  return detail::xr_get_chained_struct_impl<T*>(
205  static_cast<type_erased_input_ptr>(src));
206  }
207 } // namespace casts
208 
209 } // namespace xrtraits
210 
211 #endif // XRTRAITS_HAVE_CONSTEXPR_IF
Main namespace for these C++ OpenXR utilities.
Definition: GetChained.h:26
Header providing a slightly-riskier relative of xr_tagged_dynamic_cast() that works on void pointers ...
Header shared between the two type-safe cast headers.
TargetType xr_get_chained_struct(SourceType *src)
Definition: GetChained.h:172
TargetType xr_tagged_risky_cast(SourceType source)
Definition: TaggedRiskyCast.h:110