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
145template<arithmetic Out, arithmetic In>
146[[nodiscard]] constexpr Out narrow(In rhs) noexcept(type_in_range_v<Out, In>)
147{
148 if constexpr (type_in_range_v<Out, In>) {
149 return static_cast<Out>(rhs);
150 } else {
151 hilet r = static_cast<Out>(rhs);
152
153 if (not detail::narrow_validate(r, rhs)) {
154 throw std::bad_cast();
155 }
156
157 return r;
158 }
159}
160
163template<std::unsigned_integral Out, std::unsigned_integral In>
164[[nodiscard]] constexpr Out saturate_cast(In rhs) noexcept
165{
167 if (rhs < r) {
168 r = static_cast<Out>(rhs);
169 }
170 return r;
171}
172
181template<arithmetic Out, arithmetic In>
182[[nodiscard]] constexpr Out narrow_cast(In rhs) noexcept
183{
184 if constexpr (type_in_range_v<Out, In>) {
185 return static_cast<Out>(rhs);
186 } else {
187 hilet r = static_cast<Out>(rhs);
188 hi_axiom(detail::narrow_validate(r, rhs));
189 return r;
190 }
191}
192
193template<std::integral Out, arithmetic In>
194[[nodiscard]] constexpr Out truncate(In rhs) noexcept
195{
196 return static_cast<Out>(rhs);
197}
198
209template<std::integral Out, std::integral In>
210[[nodiscard]] constexpr Out char_cast(In rhs) noexcept
211{
212 using in_unsigned_type = std::make_unsigned_t<In>;
213 using out_unsigned_type = std::make_unsigned_t<Out>;
214
215 // We cast to unsigned of the same type, so that we don't accidentally sign extent 'char'.
216 auto in_unsigned = static_cast<in_unsigned_type>(rhs);
217 auto out_unsigned = narrow_cast<out_unsigned_type>(in_unsigned);
218 return static_cast<Out>(out_unsigned);
219}
220
221template<std::integral Out>
222[[nodiscard]] constexpr Out char_cast(std::byte rhs) noexcept
223{
224 return char_cast<Out>(static_cast<uint8_t>(rhs));
225}
226
229template<std::unsigned_integral OutType, std::unsigned_integral InType>
230[[nodiscard]] constexpr OutType low_bit_cast(InType value) noexcept
231{
232 static_assert(sizeof(OutType) * 2 == sizeof(InType), "Return value of low_bit_cast must be half the size of the input");
233 return static_cast<OutType>(value);
234}
235
238template<std::unsigned_integral OutType, std::unsigned_integral InType>
239[[nodiscard]] constexpr OutType high_bit_cast(InType value) noexcept
240{
241 static_assert(sizeof(OutType) * 2 == sizeof(InType), "Return value of high_bit_cast must be half the size of the input");
242 return static_cast<OutType>(value >> sizeof(OutType) * CHAR_BIT);
243}
244
247template<std::signed_integral OutType, std::signed_integral InType>
248[[nodiscard]] constexpr OutType low_bit_cast(InType value) noexcept
249{
250 using UInType = std::make_unsigned_t<InType>;
251 using UOutType = std::make_unsigned_t<OutType>;
252 return static_cast<OutType>(low_bit_cast<UOutType>(static_cast<UInType>(value)));
253}
254
257template<std::signed_integral OutType, std::signed_integral InType>
258[[nodiscard]] constexpr OutType high_bit_cast(InType value) noexcept
259{
260 using UInType = std::make_unsigned_t<InType>;
261 using UOutType = std::make_unsigned_t<OutType>;
262 return static_cast<OutType>(high_bit_cast<UOutType>(static_cast<UInType>(value)));
263}
264
267template<std::unsigned_integral OutType, std::signed_integral InType>
268[[nodiscard]] constexpr OutType low_bit_cast(InType value) noexcept
269{
270 using UInType = std::make_unsigned_t<InType>;
271 return low_bit_cast<OutType>(static_cast<UInType>(value));
272}
273
276template<std::unsigned_integral OutType, std::signed_integral InType>
277[[nodiscard]] constexpr OutType high_bit_cast(InType value) noexcept
278{
279 using UInType = std::make_unsigned_t<InType>;
280 return high_bit_cast<OutType>(static_cast<UInType>(value));
281}
282
285template<std::unsigned_integral OutType, std::unsigned_integral InType>
286[[nodiscard]] constexpr OutType merge_bit_cast(InType hi, InType lo) noexcept
287{
288 static_assert(sizeof(OutType) == sizeof(InType) * 2, "Return value of merge_bit_cast must be double the size of the input");
289
290 OutType r = static_cast<OutType>(hi);
291 r <<= sizeof(InType) * CHAR_BIT;
292 r |= static_cast<OutType>(lo);
293 return r;
294}
295
298template<std::signed_integral OutType, std::signed_integral InType>
299[[nodiscard]] constexpr OutType merge_bit_cast(InType hi, InType lo) noexcept
300{
301 using UInType = std::make_unsigned_t<InType>;
302 using UOutType = std::make_unsigned_t<OutType>;
303 return static_cast<OutType>(merge_bit_cast<UOutType>(static_cast<UInType>(hi), static_cast<UInType>(lo)));
304}
305
308template<std::signed_integral OutType, std::unsigned_integral InType>
309[[nodiscard]] constexpr OutType merge_bit_cast(InType hi, InType lo) noexcept
310{
311 using UOutType = std::make_unsigned_t<OutType>;
312 return narrow_cast<OutType>(merge_bit_cast<UOutType>(hi, lo));
313}
314
315[[nodiscard]] constexpr auto to_underlying(scoped_enum auto rhs) noexcept
316{
317 return static_cast<std::underlying_type_t<decltype(rhs)>>(rhs);
318}
319
320template<typename T>
321[[nodiscard]] constexpr bool to_bool(T&& rhs) noexcept requires(requires(T&& x) { static_cast<bool>(std::forward<T>(x)); })
322{
323 return static_cast<bool>(std::forward<T>(rhs));
324}
325
326} // namespace hi::inline v1
327
328hi_warning_pop();
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:286
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:239
constexpr Out saturate_cast(In rhs) noexcept
Cast an unsigned number and saturate on overflow.
Definition cast.hpp:164
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 narrow(In rhs) noexcept(type_in_range_v< Out, In >)
Cast numeric values without loss of precision.
Definition cast.hpp:146
constexpr Out char_cast(In rhs) noexcept
Cast a character.
Definition cast.hpp:210
constexpr OutType low_bit_cast(InType value) noexcept
Return the low half of the input value.
Definition cast.hpp:230
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:182
The HikoGUI namespace.
Definition ascii.hpp:19
T addressof(T... args)
T copy(T... args)
T max(T... args)