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