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
169template<arithmetic Out, arithmetic In>
170[[nodiscard]] constexpr Out narrow_cast(In rhs) noexcept
171{
172 if constexpr (type_in_range_v<Out, In>) {
173 return static_cast<Out>(rhs);
174 } else {
175 hilet r = static_cast<Out>(rhs);
176 hi_axiom(detail::narrow_validate(r, rhs));
177 return r;
178 }
179}
180
181template<std::integral Out, arithmetic In>
182[[nodiscard]] constexpr Out truncate(In rhs) noexcept
183{
184 return static_cast<Out>(rhs);
185}
186
197template<std::integral Out, std::integral In>
198[[nodiscard]] constexpr Out char_cast(In rhs) noexcept
199{
200 using in_unsigned_type = std::make_unsigned_t<In>;
201 using out_unsigned_type = std::make_unsigned_t<Out>;
202
203 // We cast to unsigned of the same type, so that we don't accidentally sign extent 'char'.
204 auto in_unsigned = static_cast<in_unsigned_type>(rhs);
205 auto out_unsigned = narrow_cast<out_unsigned_type>(in_unsigned);
206 return static_cast<Out>(out_unsigned);
207}
208
209template<std::integral Out>
210[[nodiscard]] constexpr Out char_cast(std::byte rhs) noexcept
211{
212 return char_cast<Out>(static_cast<uint8_t>(rhs));
213}
214
217template<std::unsigned_integral OutType, std::unsigned_integral InType>
218[[nodiscard]] constexpr OutType low_bit_cast(InType value) noexcept
219{
220 static_assert(sizeof(OutType) * 2 == sizeof(InType), "Return value of low_bit_cast must be half the size of the input");
221 return static_cast<OutType>(value);
222}
223
226template<std::unsigned_integral OutType, std::unsigned_integral InType>
227[[nodiscard]] constexpr OutType high_bit_cast(InType value) noexcept
228{
229 static_assert(sizeof(OutType) * 2 == sizeof(InType), "Return value of high_bit_cast must be half the size of the input");
230 return static_cast<OutType>(value >> sizeof(OutType) * CHAR_BIT);
231}
232
235template<std::signed_integral OutType, std::signed_integral InType>
236[[nodiscard]] constexpr OutType low_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>(low_bit_cast<UOutType>(static_cast<UInType>(value)));
241}
242
245template<std::signed_integral OutType, std::signed_integral InType>
246[[nodiscard]] constexpr OutType high_bit_cast(InType value) noexcept
247{
248 using UInType = std::make_unsigned_t<InType>;
249 using UOutType = std::make_unsigned_t<OutType>;
250 return static_cast<OutType>(high_bit_cast<UOutType>(static_cast<UInType>(value)));
251}
252
255template<std::unsigned_integral OutType, std::signed_integral InType>
256[[nodiscard]] constexpr OutType low_bit_cast(InType value) noexcept
257{
258 using UInType = std::make_unsigned_t<InType>;
259 return low_bit_cast<OutType>(static_cast<UInType>(value));
260}
261
264template<std::unsigned_integral OutType, std::signed_integral InType>
265[[nodiscard]] constexpr OutType high_bit_cast(InType value) noexcept
266{
267 using UInType = std::make_unsigned_t<InType>;
268 return high_bit_cast<OutType>(static_cast<UInType>(value));
269}
270
273template<std::unsigned_integral OutType, std::unsigned_integral InType>
274[[nodiscard]] constexpr OutType merge_bit_cast(InType hi, InType lo) noexcept
275{
276 static_assert(sizeof(OutType) == sizeof(InType) * 2, "Return value of merge_bit_cast must be double the size of the input");
277
278 OutType r = static_cast<OutType>(hi);
279 r <<= sizeof(InType) * CHAR_BIT;
280 r |= static_cast<OutType>(lo);
281 return r;
282}
283
286template<std::signed_integral OutType, std::signed_integral InType>
287[[nodiscard]] constexpr OutType merge_bit_cast(InType hi, InType lo) noexcept
288{
289 using UInType = std::make_unsigned_t<InType>;
290 using UOutType = std::make_unsigned_t<OutType>;
291 return static_cast<OutType>(merge_bit_cast<UOutType>(static_cast<UInType>(hi), static_cast<UInType>(lo)));
292}
293
296template<std::signed_integral OutType, std::unsigned_integral InType>
297[[nodiscard]] constexpr OutType merge_bit_cast(InType hi, InType lo) noexcept
298{
299 using UOutType = std::make_unsigned_t<OutType>;
300 return narrow_cast<OutType>(merge_bit_cast<UOutType>(hi, lo));
301}
302
303[[nodiscard]] constexpr auto to_underlying(scoped_enum auto rhs) noexcept
304{
305 return static_cast<std::underlying_type_t<decltype(rhs)>>(rhs);
306}
307
308template<typename T>
309[[nodiscard]] constexpr bool to_bool(T&& rhs) noexcept requires(requires(T&& x) { static_cast<bool>(std::forward<T>(x)); })
310{
311 return static_cast<bool>(std::forward<T>(rhs));
312}
313
314} // namespace hi::inline v1
315
316hi_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:274
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:227
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:198
constexpr OutType low_bit_cast(InType value) noexcept
Return the low half of the input value.
Definition cast.hpp:218
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:170
The HikoGUI namespace.
Definition ascii.hpp:19
T addressof(T... args)
T copy(T... args)