XRTraits C++ OpenXR Utilities
TaggedRiskyCast.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 a slightly-riskier relative of
6  * xr_tagged_dynamic_cast() that works on void pointers (as long as they
7  * actually do point to a tagged type), called xr_tagged_risky_cast().
8  *
9  * @author Ryan Pavlik <ryan.pavlik@collabora.com>
10  */
11 
12 #pragma once
13 
14 #include "../Common.h"
15 
16 #ifdef XRTRAITS_HAVE_CONSTEXPR_IF
17 
18 // Internal Includes
19 #include "../Attributes.h"
20 #include "../exceptions/CastExceptions.h"
21 #include "TaggedCastCommon.h"
22 
23 // Library Includes
24 // - none
25 
26 // Standard Includes
27 // - none
28 
29 namespace xrtraits {
30 
31 namespace casts {
32 #ifndef XRTRAITS_DOXYGEN
33  namespace detail {
34  using ::std::conditional_t;
35  using ::std::enable_if_t;
36  using ::std::is_const;
37  using ::std::is_pointer;
38  using ::std::is_reference;
39  using ::std::is_same;
40  using ::std::remove_pointer_t;
41 
42  /*! Cast to XrBaseInStructure const* or XrBaserOutStructure*
43  * depending on the constness of the input.
44  */
45  template <typename SourceType>
46  inline auto cast_to_base(SourceType source)
47  {
48  static_assert(
49  is_same_v<SourceType, const void*> ||
50  is_same_v<SourceType, void*>,
51  "cast_to_base is only for void* or const void*");
52 
53  if constexpr (points_to_const_v<SourceType>) {
54  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
55  return reinterpret_cast<
56  XrBaseInStructure const*>(source);
57  } else { // NOLINT(readability-else-after-return)
58  // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
59  return reinterpret_cast<XrBaseOutStructure*>(
60  source);
61  }
62  }
63  /*! Given a non-null const void * or void * pointing to an
64  * OpenXR tagged type, retrieve the value of its type
65  * member.
66  */
67  template <typename SourceType>
68  inline XrStructureType risky_get_type(SourceType source)
69  {
70  static_assert(
71  is_same_v<SourceType, const void*> ||
72  is_same_v<SourceType, void*>,
73  "risky_get_type is only for void* or const void*");
74  return cast_to_base(source)->type;
75  }
76  } // namespace detail
77 
78 #endif // !XRTRAITS_DOXYGEN
79 
80  /*! OpenXR structure cast, somewhat analogous to a slightly-riskier
81  * version of the standard dynamic_cast.
82  *
83  * Cast a `void *` (or `void const *`) that is assumed to be a pointer
84  * to some XR tagged type, to a given XR tagged type pointer, if the tag
85  * matches. Provide a single type parameter (target type) as well as the
86  * input pointer.
87  *
88  * Undefined behavior if the `void *` is not a pointer to some XR tagged
89  * type, because we behave as if it were! If you have an actual
90  * specific type, not just a tag, then see xr_tagged_dynamic_cast
91  * pointer/reference parameter.
92  *
93  * Input should be a pointer or reference to a (possibly const)
94  * structure, with static type starting with `XrStructureType type;
95  * const void * next;`. The `type` member of the input is examined to
96  * ensure it matches the value defined by the spec (as indicated by
97  * generated type traits) for the TargetType provided.
98  *
99  * - If TargetType is a pointer type, then a mismatch of `type` (or
100  * null input) results in a nullptr return.
101  * - If TargetType is a reference type, then a mismatch of `type` (or
102  * null input) results in a bad_tagged_cast exception.
103  *
104  * Unlike xr_tagged_dynamic_cast, this function cannot take references
105  * as input.
106  *
107  * @ingroup Casts
108  */
109  template <typename TargetType, typename SourceType>
110  inline TargetType xr_tagged_risky_cast(SourceType source)
111  {
112  using Tgt = TargetType;
113  // This cast operation assumes that if you're calling
114  // it, you know that the source parameter type is an XR
115  // tagged type
116  static_assert(
117  is_same_v<detail::remove_reference_pointer_cv_t<SourceType>,
118  void>,
119  "Can only use xr_tagged_risky_cast to cast from a "
120  "pointer to (possibly const) void. If you have something "
121  "with real type data, you are looking for "
122  "xr_tagged_dynamic_cast.");
123 
124  static_assert(
125  traits::is_xr_tagged_type_v<Tgt>,
126  "Can only use xr_tagged_risky_cast to cast to a "
127  "pointer/reference to a (possibly const) XR tagged type.");
128  // Cast determines whether to perform reinterpret or
129  // return nullptr based on comparing value of `type` to
130  // expected value for the target type, so there must
131  // actually be such an expected value. (Note that we
132  // don't require this for the source type: there we only
133  // care that the member exists, so we can compare it to
134  // the value we're verifying the existence of here.)
135  static_assert(traits::has_xr_type_tag_v<Tgt>,
136  "Can only use xr_tagged_risky_cast to cast to a "
137  "pointer/reference to a concrete XR tagged type "
138  "with a known, defined type tag value "
139  "(XrStructureType enum value).");
140 
141  static_assert(detail::points_or_refers_to_const_v<SourceType>
142  ? detail::points_or_refers_to_const_v<Tgt>
143  : true,
144  "Cannot remove const qualification through "
145  "xr_tagged_risky_cast");
146 
147 #ifndef XRTRAITS_USE_EXCEPTIONS
148  static_assert(is_pointer_v<Tgt>,
149  "Casts to references require the availability of "
150  "xrtraits exceptions.");
151 #endif // !XRTRAITS_USE_EXCEPTIONS
152 
153  XRTRAITS_MAYBE_UNUSED static const char castType[] =
154  "xr_tagged_dynamic_cast";
155  constexpr bool is_target_pointer = is_pointer_v<Tgt>;
156 
157  // Always pointer input: must check for nullptr.
158  if (source == nullptr) {
159  if constexpr (is_target_pointer) {
160  // nullptr in, return nullptr
161  return nullptr;
162  } else { // NOLINT(readability-else-after-return)
163 
164  // nullptr in, exception
165 #ifdef XRTRAITS_USE_EXCEPTIONS
167  traits::xr_type_tag_v<Tgt>, nullptr,
168  castType);
169 #endif // XRTRAITS_USE_EXCEPTIONS
170  }
171  }
172 
173  if (traits::xr_type_tag_v<Tgt> ==
174  detail::risky_get_type(source)) {
175  // matching type tag in: do the casting and
176  // address-taking/dereferencing
177  return detail::perform_dereferencing_reinterpret_cast<
178  Tgt>(source);
179  }
180 
181  // tag on type didn't match what was expected
182  if constexpr (is_target_pointer) {
183  // tag mismatch, return nullptr
184  return nullptr;
185  } else { // NOLINT(readability-else-after-return)
186 
187  // tag mismatch, throw exception
188 #ifdef XRTRAITS_USE_EXCEPTIONS
190  traits::xr_type_tag_v<Tgt>,
191  detail::risky_get_type(source), castType);
192 #endif // XRTRAITS_USE_EXCEPTIONS
193  }
194  }
195 
196 } // namespace casts
197 
198 } // namespace xrtraits
199 
200 #endif // XRTRAITS_HAVE_CONSTEXPR_IF
Main namespace for these C++ OpenXR utilities.
Definition: GetChained.h:26
#define XRTRAITS_MAYBE_UNUSED
Compatibility wrapper for [[maybe_unused]]
Definition: Attributes.h:26
Definition: CastExceptions.h:38
constexpr bool is_same_v
Variable template wrapping std::is_same<T, U>::value.
Definition: Common.h:34
Header shared between the two type-safe cast headers.
TargetType xr_tagged_risky_cast(SourceType source)
Definition: TaggedRiskyCast.h:110