HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
cast.hpp
1// Copyright Take Vos 2020.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "required.hpp"
8#include "concepts.hpp"
9#include "assert.hpp"
10#include <type_traits>
11#include <concepts>
12#include <climits>
13
14namespace hi::inline v1 {
15
16template<typename T>
17[[nodiscard]] constexpr T copy(T value) noexcept
18{
19 return value;
20}
21
24template<typename Out, std::derived_from<std::remove_pointer_t<Out>> In>
25[[nodiscard]] constexpr Out up_cast(In *rhs) noexcept
26 requires(std::is_const_v<std::remove_pointer_t<Out>> == std::is_const_v<In> or std::is_const_v<std::remove_pointer_t<Out>>)
27{
28 return static_cast<Out>(rhs);
29}
30
33template<typename Out>
34[[nodiscard]] constexpr Out up_cast(nullptr_t) noexcept
35{
36 return nullptr;
37}
38
41template<typename Out, std::derived_from<std::remove_reference_t<Out>> In>
42[[nodiscard]] constexpr Out up_cast(In &rhs) noexcept requires(
43 std::is_const_v<std::remove_reference_t<Out>> == std::is_const_v<In> or std::is_const_v<std::remove_reference_t<Out>>)
44{
45 return static_cast<Out>(rhs);
46}
47
55template<typename Out, base_of<std::remove_pointer_t<Out>> In>
56[[nodiscard]] constexpr Out down_cast(In *rhs) noexcept
57 requires(std::is_const_v<std::remove_pointer_t<Out>> == std::is_const_v<In> or std::is_const_v<std::remove_pointer_t<Out>>)
58{
59 hi_axiom(rhs == nullptr or dynamic_cast<Out>(rhs) != nullptr);
60 return static_cast<Out>(rhs);
61}
62
70template<typename Out>
71[[nodiscard]] constexpr Out down_cast(nullptr_t) noexcept
72{
73 return nullptr;
74}
75
82template<typename Out, base_of<std::remove_reference_t<Out>> In>
83[[nodiscard]] constexpr Out down_cast(In &rhs) noexcept requires(
84 std::is_const_v<std::remove_reference_t<Out>> == std::is_const_v<In> or std::is_const_v<std::remove_reference_t<Out>>)
85{
86 return static_cast<Out>(rhs);
87}
88
91template<numeric Out, numeric In>
92[[nodiscard]] constexpr Out wide_cast(In rhs) noexcept requires(type_in_range_v<Out, In>)
93{
94 return static_cast<Out>(rhs);
95}
96
97namespace detail {
98
99template<numeric Out, numeric In>
100[[nodiscard]] constexpr bool narrow_validate(Out out, In in) noexcept
101{
102 // in- and out-value compares the same, after converting out-value back to in-type.
103 auto r = (in == static_cast<In>(out));
104
105 // If the types have different signs we need to do an extra test to make sure the actual sign
106 // of the values are the same as well.
108 r &= (in < In{}) == (out < Out{});
109 }
110
111 return r;
112}
113
114}
115
124template<numeric Out, numeric In>
125[[nodiscard]] constexpr Out narrow(In rhs) noexcept(type_in_range_v<Out, In>)
126{
127 if constexpr (type_in_range_v<Out, In>) {
128 return static_cast<Out>(rhs);
129 } else {
130 hilet r = static_cast<Out>(rhs);
131
132 if (not detail::narrow_validate(r, rhs)) {
133 throw std::bad_cast();
134 }
135
136 return r;
137 }
138}
139
148template<numeric Out, numeric In>
149[[nodiscard]] constexpr Out narrow_cast(In rhs) noexcept
150{
151 if constexpr (type_in_range_v<Out, In>) {
152 return static_cast<Out>(rhs);
153 } else {
154 hilet r = static_cast<Out>(rhs);
155 hi_axiom(detail::narrow_validate(r, rhs));
156 return r;
157 }
158}
159
164decltype(auto) awaitable_cast(awaitable_direct auto &&rhs) noexcept
165{
166 return hi_forward(rhs);
167}
168
173decltype(auto) awaitable_cast(awaitable_member auto &&rhs) noexcept
174{
175 return hi_forward(rhs).operator co_await();
176}
177
182decltype(auto) awaitable_cast(awaitable_non_member auto &&rhs) noexcept
183{
184 return operator co_await(hi_forward(rhs));
185}
186
191template<awaitable T>
193 using type = std::remove_cvref_t<decltype(awaitable_cast(std::declval<T>()))>;
194};
195
200template<awaitable T>
201using awaitable_cast_type_t = awaitable_cast_type<T>::type;
202
203
204
207template<std::unsigned_integral OutType, std::unsigned_integral InType>
208[[nodiscard]] constexpr OutType low_bit_cast(InType value) noexcept
209{
210 static_assert(sizeof(OutType) * 2 == sizeof(InType), "Return value of low_bit_cast must be half the size of the input");
211 return static_cast<OutType>(value);
212}
213
216template<std::unsigned_integral OutType, std::unsigned_integral InType>
217[[nodiscard]] constexpr OutType high_bit_cast(InType value) noexcept
218{
219 static_assert(sizeof(OutType) * 2 == sizeof(InType), "Return value of high_bit_cast must be half the size of the input");
220 return static_cast<OutType>(value >> sizeof(OutType) * CHAR_BIT);
221}
222
225template<std::signed_integral OutType, std::signed_integral InType>
226[[nodiscard]] constexpr OutType low_bit_cast(InType value) noexcept
227{
228 using UInType = std::make_unsigned_t<InType>;
229 using UOutType = std::make_unsigned_t<OutType>;
230 return static_cast<OutType>(low_bit_cast<UOutType>(static_cast<UInType>(value)));
231}
232
235template<std::signed_integral OutType, std::signed_integral InType>
236[[nodiscard]] constexpr OutType high_bit_cast(InType value) noexcept
237{
238 using UInType = std::make_unsigned_t<InType>;
239 using UOutType = std::make_unsigned_t<OutType>;
240 return static_cast<OutType>(high_bit_cast<UOutType>(static_cast<UInType>(value)));
241}
242
245template<std::unsigned_integral OutType, std::signed_integral InType>
246[[nodiscard]] constexpr OutType low_bit_cast(InType value) noexcept
247{
248 using UInType = std::make_unsigned_t<InType>;
249 return low_bit_cast<OutType>(static_cast<UInType>(value));
250}
251
254template<std::unsigned_integral OutType, std::signed_integral InType>
255[[nodiscard]] constexpr OutType high_bit_cast(InType value) noexcept
256{
257 using UInType = std::make_unsigned_t<InType>;
258 return high_bit_cast<OutType>(static_cast<UInType>(value));
259}
260
263template<std::unsigned_integral OutType, std::unsigned_integral InType>
264[[nodiscard]] constexpr OutType merge_bit_cast(InType hi, InType lo) noexcept
265{
266 static_assert(sizeof(OutType) == sizeof(InType) * 2, "Return value of merge_bit_cast must be double the size of the input");
267
268 OutType r = static_cast<OutType>(hi);
269 r <<= sizeof(InType) * CHAR_BIT;
270 r |= static_cast<OutType>(lo);
271 return r;
272}
273
276template<std::signed_integral OutType, std::signed_integral InType>
277[[nodiscard]] constexpr OutType merge_bit_cast(InType hi, InType lo) noexcept
278{
279 using UInType = std::make_unsigned_t<InType>;
280 using UOutType = std::make_unsigned_t<OutType>;
281 return static_cast<OutType>(merge_bit_cast<UOutType>(static_cast<UInType>(hi), static_cast<UInType>(lo)));
282}
283
286template<std::signed_integral OutType, std::unsigned_integral InType>
287[[nodiscard]] constexpr OutType merge_bit_cast(InType hi, InType lo) noexcept
288{
289 using UOutType = std::make_unsigned_t<OutType>;
290 return narrow_cast<OutType>(merge_bit_cast<UOutType>(hi, lo));
291}
292
293[[nodiscard]] constexpr auto to_underlying(scoped_enum auto rhs) noexcept
294{
295 return static_cast<std::underlying_type_t<decltype(rhs)>>(rhs);
296}
297
298} // namespace hi::inline v1
This file includes required definitions.
#define hilet
Invariant should be the default for variables.
Definition required.hpp:23
#define hi_forward(x)
Forward a value, based on the decltype of the value.
Definition required.hpp:29
Resolve the type that is directly-awaitable.
Definition cast.hpp:192
T copy(T... args)