HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
cast.hpp
1// Copyright Take Vos 2020-2022.
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 "utility.hpp"
8#include "concepts.hpp"
9#include "assert.hpp"
10#include <type_traits>
11#include <concepts>
12#include <climits>
13
14hi_warning_push();
15// C26472: Don't use static_cast for arithmetic conversions, Use brace initialization, gsl::narrow_cast or gsl::narrow (type.1).
16// This file implements narrow_cast().
17hi_warning_ignore_msvc(26472);
18// C26467: Converting from floating point to unsigned integral types results in non-portable code if the double/float has
19// a negative value. Use gsl::narrow_cast or gsl::naroow instead to guard against undefined behavior and potential data loss
20// (es.46).
21// This file implements narrow_cast().
22hi_warning_ignore_msvc(26467);
23// C26496: The variable 'r' does not change after construction, mark it as const (con.4).
24// False positive
25hi_warning_ignore_msvc(26496);
26
27namespace hi::inline v1 {
28template<typename T>
29[[nodiscard]] constexpr T copy(T value) noexcept
30{
31 return value;
32}
33
36template<typename Out, std::derived_from<std::remove_pointer_t<Out>> In>
37[[nodiscard]] constexpr Out up_cast(In *rhs) noexcept
38 requires std::is_pointer_v<Out> and (std::is_const_v<std::remove_pointer_t<Out>> == std::is_const_v<In> or std::is_const_v<std::remove_pointer_t<Out>>)
39{
40 return static_cast<Out>(rhs);
41}
42
45template<typename Out>
46[[nodiscard]] constexpr Out up_cast(nullptr_t) noexcept
47 requires std::is_pointer_v<Out>
48{
49 return nullptr;
50}
51
54template<typename Out, std::derived_from<std::remove_reference_t<Out>> In>
55[[nodiscard]] constexpr Out up_cast(In& rhs) noexcept
56 requires std::is_reference_v<Out> and (std::is_const_v<std::remove_reference_t<Out>> == std::is_const_v<In> or std::is_const_v<std::remove_reference_t<Out>>)
57{
58 return static_cast<Out>(rhs);
59}
60
68template<typename Out, base_of<std::remove_pointer_t<Out>> In>
69[[nodiscard]] constexpr Out down_cast(In *rhs) noexcept
70 requires std::is_pointer_v<Out> and (std::is_const_v<std::remove_pointer_t<Out>> == std::is_const_v<In> or std::is_const_v<std::remove_pointer_t<Out>>)
71{
72 hi_axiom(rhs == nullptr or dynamic_cast<Out>(rhs) != nullptr);
73 return static_cast<Out>(rhs);
74}
75
80template<typename Out>
81[[nodiscard]] constexpr Out down_cast(nullptr_t) noexcept
82 requires std::is_pointer_v<Out>
83{
84 return nullptr;
85}
86
93template<typename Out, base_of<std::remove_reference_t<Out>> In>
94[[nodiscard]] constexpr Out down_cast(In& rhs) noexcept
95 requires std::is_reference_v<Out> and (
96 std::is_const_v<std::remove_reference_t<Out>> == std::is_const_v<In> or std::is_const_v<std::remove_reference_t<Out>>)
97{
98 hi_axiom(dynamic_cast<std::add_pointer_t<std::remove_reference_t<Out>>>(std::addressof(rhs)) != nullptr);
99 return static_cast<Out>(rhs);
100}
101
104template<arithmetic Out, arithmetic In>
105[[nodiscard]] constexpr Out wide_cast(In rhs) noexcept requires(type_in_range_v<Out, In>)
106{
107 return static_cast<Out>(rhs);
108}
109
112template<arithmetic Out>
113[[nodiscard]] constexpr Out wide_cast(bool rhs) noexcept
114{
115 return static_cast<Out>(rhs);
116}
117
118namespace detail {
119
120template<arithmetic Out, arithmetic In>
121[[nodiscard]] constexpr bool narrow_validate(Out out, In in) noexcept
122{
123 // in- and out-value compares the same, after converting out-value back to in-type.
124 auto r = (in == static_cast<In>(out));
125
126 // If the types have different signs we need to do an extra test to make sure the actual sign
127 // of the values are the same as well.
129 r &= (in < In{}) == (out < Out{});
130 }
131
132 return r;
133}
134
135} // namespace detail
136
139template<std::unsigned_integral Out, std::unsigned_integral In>
140[[nodiscard]] constexpr Out saturate_cast(In rhs) noexcept
141{
143 if (rhs < r) {
144 r = static_cast<Out>(rhs);
145 }
146 return r;
147}
148
157template<arithmetic Out, arithmetic In>
158[[nodiscard]] constexpr Out narrow_cast(In rhs) noexcept
159{
160 if constexpr (type_in_range_v<Out, In>) {
161 return static_cast<Out>(rhs);
162 } else {
163 hilet r = static_cast<Out>(rhs);
164 hi_axiom(detail::narrow_validate(r, rhs));
165 return r;
166 }
167}
168
169template<std::integral Out, arithmetic In>
170[[nodiscard]] constexpr Out truncate(In rhs) noexcept
171{
172 return static_cast<Out>(rhs);
173}
174
185template<std::integral Out, std::integral In>
186[[nodiscard]] constexpr Out char_cast(In rhs) noexcept
187{
188 using in_unsigned_type = std::make_unsigned_t<In>;
189 using out_unsigned_type = std::make_unsigned_t<Out>;
190
191 // We cast to unsigned of the same type, so that we don't accidentally sign extent 'char'.
192 auto in_unsigned = static_cast<in_unsigned_type>(rhs);
193 auto out_unsigned = narrow_cast<out_unsigned_type>(in_unsigned);
194 return static_cast<Out>(out_unsigned);
195}
196
197template<std::integral Out>
198[[nodiscard]] constexpr Out char_cast(std::byte rhs) noexcept
199{
200 return char_cast<Out>(static_cast<uint8_t>(rhs));
201}
202
205template<std::unsigned_integral OutType, std::unsigned_integral InType>
206[[nodiscard]] constexpr OutType low_bit_cast(InType value) noexcept
207{
208 static_assert(sizeof(OutType) * 2 == sizeof(InType), "Return value of low_bit_cast must be half the size of the input");
209 return static_cast<OutType>(value);
210}
211
214template<std::unsigned_integral OutType, std::unsigned_integral InType>
215[[nodiscard]] constexpr OutType high_bit_cast(InType value) noexcept
216{
217 static_assert(sizeof(OutType) * 2 == sizeof(InType), "Return value of high_bit_cast must be half the size of the input");
218 return static_cast<OutType>(value >> sizeof(OutType) * CHAR_BIT);
219}
220
223template<std::signed_integral OutType, std::signed_integral InType>
224[[nodiscard]] constexpr OutType low_bit_cast(InType value) noexcept
225{
226 using UInType = std::make_unsigned_t<InType>;
227 using UOutType = std::make_unsigned_t<OutType>;
228 return static_cast<OutType>(low_bit_cast<UOutType>(static_cast<UInType>(value)));
229}
230
233template<std::signed_integral OutType, std::signed_integral InType>
234[[nodiscard]] constexpr OutType high_bit_cast(InType value) noexcept
235{
236 using UInType = std::make_unsigned_t<InType>;
237 using UOutType = std::make_unsigned_t<OutType>;
238 return static_cast<OutType>(high_bit_cast<UOutType>(static_cast<UInType>(value)));
239}
240
243template<std::unsigned_integral OutType, std::signed_integral InType>
244[[nodiscard]] constexpr OutType low_bit_cast(InType value) noexcept
245{
246 using UInType = std::make_unsigned_t<InType>;
247 return low_bit_cast<OutType>(static_cast<UInType>(value));
248}
249
252template<std::unsigned_integral OutType, std::signed_integral InType>
253[[nodiscard]] constexpr OutType high_bit_cast(InType value) noexcept
254{
255 using UInType = std::make_unsigned_t<InType>;
256 return high_bit_cast<OutType>(static_cast<UInType>(value));
257}
258
261template<std::unsigned_integral OutType, std::unsigned_integral InType>
262[[nodiscard]] constexpr OutType merge_bit_cast(InType hi, InType lo) noexcept
263{
264 static_assert(sizeof(OutType) == sizeof(InType) * 2, "Return value of merge_bit_cast must be double the size of the input");
265
266 OutType r = static_cast<OutType>(hi);
267 r <<= sizeof(InType) * CHAR_BIT;
268 r |= static_cast<OutType>(lo);
269 return r;
270}
271
274template<std::signed_integral OutType, std::signed_integral InType>
275[[nodiscard]] constexpr OutType merge_bit_cast(InType hi, InType lo) noexcept
276{
277 using UInType = std::make_unsigned_t<InType>;
278 using UOutType = std::make_unsigned_t<OutType>;
279 return static_cast<OutType>(merge_bit_cast<UOutType>(static_cast<UInType>(hi), static_cast<UInType>(lo)));
280}
281
284template<std::signed_integral OutType, std::unsigned_integral InType>
285[[nodiscard]] constexpr OutType merge_bit_cast(InType hi, InType lo) noexcept
286{
287 using UOutType = std::make_unsigned_t<OutType>;
288 return narrow_cast<OutType>(merge_bit_cast<UOutType>(hi, lo));
289}
290
291[[nodiscard]] constexpr auto to_underlying(scoped_enum auto rhs) noexcept
292{
293 return static_cast<std::underlying_type_t<decltype(rhs)>>(rhs);
294}
295
296template<typename T>
297[[nodiscard]] constexpr bool to_bool(T&& rhs) noexcept requires(requires(T&& x) { static_cast<bool>(std::forward<T>(x)); })
298{
299 return static_cast<bool>(std::forward<T>(rhs));
300}
301
302} // namespace hi::inline v1
303
304hi_warning_pop();
Utilities to assert and bound check.
#define hi_axiom(expression)
Specify an axiom; an expression that is true.
Definition assert.hpp:133
Utilities used by the HikoGUI library itself.
#define hilet
Invariant should be the default for variables.
Definition utility.hpp:23
DOXYGEN BUG.
Definition algorithm.hpp:15
constexpr OutType merge_bit_cast(InType hi, InType lo) noexcept
Return the upper half of the input value.
Definition cast.hpp:262
constexpr Out wide_cast(In rhs) noexcept
Cast a number to a type that will be able to represent all values without loss of precision.
Definition cast.hpp:105
constexpr OutType high_bit_cast(InType value) noexcept
Return the upper half of the input value.
Definition cast.hpp:215
constexpr Out saturate_cast(In rhs) noexcept
Cast an unsigned number and saturate on overflow.
Definition cast.hpp:140
constexpr Out up_cast(In *rhs) noexcept
Cast a pointer to a class to its base class or itself.
Definition cast.hpp:37
constexpr Out char_cast(In rhs) noexcept
Cast a character.
Definition cast.hpp:186
constexpr OutType low_bit_cast(InType value) noexcept
Return the low half of the input value.
Definition cast.hpp:206
constexpr Out down_cast(In *rhs) noexcept
Cast a pointer to a class to its derived class or itself.
Definition cast.hpp:69
constexpr Out narrow_cast(In rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:158
The HikoGUI namespace.
Definition ascii.hpp:19
T addressof(T... args)
T copy(T... args)
T max(T... args)