Line data Source code
1 : #pragma once
2 :
3 : #include <type_traits>
4 : #include <typeinfo>
5 : #include <limits>
6 : #include <cassert>
7 : #include <memory>
8 :
9 : #include "elsaDefines.h"
10 : #include "Error.h"
11 :
12 : namespace elsa
13 : {
14 : namespace detail
15 : {
16 : /// Type of CopyConst_t is: 'const Dst' if Src is 'const', else it's 'Dst'
17 : template <typename Src, typename Dst>
18 : using CopyConst_t =
19 : typename std::conditional_t<std::is_const_v<Src>, std::add_const_t<Dst>, Dst>;
20 : } // namespace detail
21 :
22 : /// Check if a type can be dynamically casted to another
23 : template <typename Derived, typename Base>
24 : bool is(Base& input)
25 153724 : {
26 153724 : try {
27 : // we can't convert to pointer and check for nullptr,
28 : // because the reference can never be nullptr and the compiler knows this :)
29 153724 : [[maybe_unused]] const auto& r =
30 153724 : dynamic_cast<detail::CopyConst_t<Base, Derived>&>(input);
31 153724 : return true;
32 153724 : } catch (const std::bad_cast&) {
33 46 : return false;
34 46 : }
35 153724 : }
36 :
37 : /// Overload to check for pointer types directly
38 : template <typename Derived, typename Base>
39 : bool is(Base* input)
40 72872 : {
41 72892 : return input && is<Derived>(*input);
42 72872 : }
43 :
44 : /// Overload to check for unique_ptr directly.
45 : /// Usually passing a const reference to a unique_ptr is really dumb, but for this case it's
46 : /// what is needed.
47 : template <typename Derived, typename Base, typename Deleter>
48 : bool is(std::unique_ptr<Base, Deleter>& input)
49 29 : {
50 29 : return input && is<Derived>(*input);
51 29 : }
52 :
53 : /// Downcast pointer, assumes that type is known (i.e. checked by is(...))
54 : template <typename Derived, typename Base>
55 : auto downcast(Base* input) -> detail::CopyConst_t<Base, Derived>*
56 74188 : {
57 74188 : static_assert(std::is_base_of_v<Base, Derived>, "To downcast, types needs to be derived");
58 :
59 74188 : assert(!input || is<Derived>(*input));
60 :
61 74188 : return static_cast<detail::CopyConst_t<Base, Derived>*>(input);
62 74188 : }
63 :
64 : /// Downcast reference, assumes that type is known (i.e. checked by is(...))
65 : template <typename Derived, typename Base>
66 : auto downcast(Base& input) -> detail::CopyConst_t<Base, Derived>&
67 6309 : {
68 6309 : static_assert(std::is_base_of_v<Base, Derived>, "To downcast, types needs to be derived");
69 :
70 6309 : assert(is<Derived>(input));
71 :
72 6309 : return static_cast<detail::CopyConst_t<Base, Derived>&>(input);
73 6309 : }
74 :
75 : /// Downcast reference, assumes that type is known (i.e. checked by is(...))
76 : /// Note: This version only works with the default deleter, if you need a fancy deleter,
77 : /// this function might need's some overloading and SFINAE to get it working
78 : template <typename Derived, typename Base>
79 : std::unique_ptr<Derived> downcast(std::unique_ptr<Base>&& input)
80 22 : {
81 22 : static_assert(std::is_base_of_v<Base, Derived>, "To downcast, types needs to be derived");
82 :
83 22 : assert(is<Derived>(input));
84 :
85 22 : auto d = static_cast<Derived*>(input.release());
86 22 : return std::unique_ptr<Derived>(d);
87 22 : }
88 :
89 : /// Try to downcast pointer to Base to Derived, return a nullptr if it fails
90 : template <typename Derived, typename Base>
91 : auto downcast_safe(Base* input) -> detail::CopyConst_t<Base, Derived>*
92 72832 : {
93 72832 : static_assert(std::is_base_of_v<Base, Derived>, "To downcast, types needs to be derived");
94 :
95 74268 : if (is<Derived>(input)) {
96 74268 : return downcast<Derived>(input);
97 74268 : }
98 :
99 >1844*10^16 : return nullptr;
100 >1844*10^16 : }
101 :
102 : /// Try to downcast reference to Base to Derived, Will throw std::bad_cast if it can't
103 : /// dynamically cast to Derived
104 : template <typename Derived, typename Base>
105 : auto downcast_safe(Base& input) -> detail::CopyConst_t<Base, Derived>&
106 622 : {
107 622 : static_assert(std::is_base_of_v<Base, Derived>, "To downcast, types needs to be derived");
108 :
109 622 : if (is<Derived>(input)) {
110 619 : return downcast<Derived>(input);
111 619 : }
112 :
113 3 : throw BadCastError("Could not cast given reference to wanted type");
114 3 : }
115 :
116 : /// Try to downcast a unique_ptr to Base to Derived, return a nullptr if it fails
117 : /// Note: that if the downcast can't be performed the callees unique_ptr is not touched
118 : /// But once the cast is done, the callees unique_ptr is not safe to use anymore
119 : /// Also This version only works with the default deleter, if you need a fancy deleter,
120 : /// this function might need's some overloading and SFINAE to get it working
121 : template <typename Derived, typename Base>
122 : std::unique_ptr<Derived> downcast_safe(std::unique_ptr<Base>&& input)
123 17 : {
124 17 : static_assert(std::is_base_of_v<Base, Derived>, "To downcast, types needs to be derived");
125 :
126 17 : if (Derived* result = downcast_safe<Derived>(input.get())) {
127 16 : input.release();
128 16 : return std::unique_ptr<Derived>(result);
129 16 : }
130 :
131 1 : return std::unique_ptr<Derived>(nullptr);
132 1 : }
133 :
134 : namespace detail
135 : {
136 : /// Alias to remove const, volatile and reference qualifiers of type
137 : template <typename T>
138 : using remove_crv_t = std::remove_reference_t<std::remove_cv_t<T>>;
139 : } // namespace detail
140 :
141 : /// Cast from one type to another without any checks, use with care
142 : template <typename To, typename From>
143 : auto as(From&& from) noexcept
144 108142097 : {
145 108142097 : return static_cast<To>(std::forward<From>(from));
146 108142097 : }
147 :
148 : /// Convert a signed value to an unsigned. Check for underflow only
149 : template <typename From>
150 : auto asUnsigned(From&& v) noexcept
151 49148953 : {
152 49148953 : static_assert(std::is_arithmetic_v<detail::remove_crv_t<From>>,
153 49148953 : "Expect arithmetic type (use as() instead)");
154 :
155 49148953 : if constexpr (std::is_unsigned_v<From>) {
156 49148953 : return std::forward<From>(v);
157 49148953 : }
158 :
159 49148953 : using To = std::make_unsigned_t<detail::remove_crv_t<From>>;
160 :
161 49148953 : assert(v >= 0 && "Only convert positive numbers to an unsigned");
162 49148953 : return as<To>(std::forward<From>(v));
163 49148953 : }
164 :
165 : /// Convert an unsigned value to an signed. Check for overflow only
166 : template <typename From>
167 : auto asSigned(From&& v) noexcept
168 58912351 : {
169 58912351 : static_assert(std::is_arithmetic_v<detail::remove_crv_t<From>>,
170 58912351 : "Expect arithmetic type (use as() instead)");
171 :
172 58912351 : if constexpr (std::is_signed_v<From>) {
173 58912351 : return std::forward<From>(v);
174 58912351 : }
175 :
176 58912351 : using To = std::make_signed_t<detail::remove_crv_t<From>>;
177 :
178 58912351 : assert(v <= std::numeric_limits<To>::max() && "Converted value is overflown");
179 58912351 : return as<To>(std::forward<From>(v));
180 58912351 : }
181 : } // namespace elsa
|