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
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_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{
48 return nullptr;
49}
50
53template<typename Out, std::derived_from<std::remove_reference_t<Out>> In>
54[[nodiscard]] constexpr Out up_cast(In& rhs) noexcept requires(
55 std::is_const_v<std::remove_reference_t<Out>> == std::is_const_v<In> or std::is_const_v<std::remove_reference_t<Out>>)
56{
57 return static_cast<Out>(rhs);
58}
59
67template<typename Out, base_of<std::remove_pointer_t<Out>> In>
68[[nodiscard]] constexpr Out down_cast(In *rhs) noexcept
69 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>>)
70{
71 hi_axiom(rhs == nullptr or dynamic_cast<Out>(rhs) != nullptr);
72 return static_cast<Out>(rhs);
73}
74
82template<typename Out>
83[[nodiscard]] constexpr Out down_cast(nullptr_t) noexcept
84{
85 return nullptr;
86}
87
94template<typename Out, base_of<std::remove_reference_t<Out>> In>
95[[nodiscard]] constexpr Out down_cast(In& rhs) noexcept requires(
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 return static_cast<Out>(rhs);
99}
100
103template<arithmetic Out, arithmetic In>
104[[nodiscard]] constexpr Out wide_cast(In rhs) noexcept requires(type_in_range_v<Out, In>)
105{
106 return static_cast<Out>(rhs);
107}
108
111template<arithmetic Out>
112[[nodiscard]] constexpr Out wide_cast(bool rhs) noexcept
113{
114 return static_cast<Out>(rhs);
115}
116
117namespace detail {
118
119template<arithmetic Out, arithmetic In>
120[[nodiscard]] constexpr bool narrow_validate(Out out, In in) noexcept
121{
122 // in- and out-value compares the same, after converting out-value back to in-type.
123 auto r = (in == static_cast<In>(out));
124
125 // If the types have different signs we need to do an extra test to make sure the actual sign
126 // of the values are the same as well.
128 r &= (in < In{}) == (out < Out{});
129 }
130
131 return r;
132}
133
134} // namespace detail
135
144template<arithmetic Out, arithmetic In>
145[[nodiscard]] constexpr Out narrow(In rhs) noexcept(type_in_range_v<Out, In>)
146{
147 if constexpr (type_in_range_v<Out, In>) {
148 return static_cast<Out>(rhs);
149 } else {
150 hilet r = static_cast<Out>(rhs);
151
152 if (not detail::narrow_validate(r, rhs)) {
153 throw std::bad_cast();
154 }
155
156 return r;
157 }
158}
159
168template<arithmetic Out, arithmetic In>
169[[nodiscard]] constexpr Out narrow_cast(In rhs) noexcept
170{
171 if constexpr (type_in_range_v<Out, In>) {
172 return static_cast<Out>(rhs);
173 } else {
174 hilet r = static_cast<Out>(rhs);
175 hi_axiom(detail::narrow_validate(r, rhs));
176 return r;
177 }
178}
179
180template<std::integral Out, arithmetic In>
181[[nodiscard]] constexpr Out truncate(In rhs) noexcept
182{
183 return static_cast<Out>(rhs);
184}
185
196template<std::integral Out, std::integral In>
197[[nodiscard]] constexpr Out char_cast(In rhs) noexcept
198{
199 using in_unsigned_type = std::make_unsigned_t<In>;
200 using out_unsigned_type = std::make_unsigned_t<Out>;
201
202 // We cast to unsigned of the same type, so that we don't accidentally sign extent 'char'.
203 auto in_unsigned = static_cast<in_unsigned_type>(rhs);
204 auto out_unsigned = narrow_cast<out_unsigned_type>(in_unsigned);
205 return static_cast<Out>(out_unsigned);
206}
207
210template<std::unsigned_integral OutType, std::unsigned_integral InType>
211[[nodiscard]] constexpr OutType low_bit_cast(InType value) noexcept
212{
213 static_assert(sizeof(OutType) * 2 == sizeof(InType), "Return value of low_bit_cast must be half the size of the input");
214 return static_cast<OutType>(value);
215}
216
219template<std::unsigned_integral OutType, std::unsigned_integral InType>
220[[nodiscard]] constexpr OutType high_bit_cast(InType value) noexcept
221{
222 static_assert(sizeof(OutType) * 2 == sizeof(InType), "Return value of high_bit_cast must be half the size of the input");
223 return static_cast<OutType>(value >> sizeof(OutType) * CHAR_BIT);
224}
225
228template<std::signed_integral OutType, std::signed_integral InType>
229[[nodiscard]] constexpr OutType low_bit_cast(InType value) noexcept
230{
231 using UInType = std::make_unsigned_t<InType>;
232 using UOutType = std::make_unsigned_t<OutType>;
233 return static_cast<OutType>(low_bit_cast<UOutType>(static_cast<UInType>(value)));
234}
235
238template<std::signed_integral OutType, std::signed_integral InType>
239[[nodiscard]] constexpr OutType high_bit_cast(InType value) noexcept
240{
241 using UInType = std::make_unsigned_t<InType>;
242 using UOutType = std::make_unsigned_t<OutType>;
243 return static_cast<OutType>(high_bit_cast<UOutType>(static_cast<UInType>(value)));
244}
245
248template<std::unsigned_integral OutType, std::signed_integral InType>
249[[nodiscard]] constexpr OutType low_bit_cast(InType value) noexcept
250{
251 using UInType = std::make_unsigned_t<InType>;
252 return low_bit_cast<OutType>(static_cast<UInType>(value));
253}
254
257template<std::unsigned_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 return high_bit_cast<OutType>(static_cast<UInType>(value));
262}
263
266template<std::unsigned_integral OutType, std::unsigned_integral InType>
267[[nodiscard]] constexpr OutType merge_bit_cast(InType hi, InType lo) noexcept
268{
269 static_assert(sizeof(OutType) == sizeof(InType) * 2, "Return value of merge_bit_cast must be double the size of the input");
270
271 OutType r = static_cast<OutType>(hi);
272 r <<= sizeof(InType) * CHAR_BIT;
273 r |= static_cast<OutType>(lo);
274 return r;
275}
276
279template<std::signed_integral OutType, std::signed_integral InType>
280[[nodiscard]] constexpr OutType merge_bit_cast(InType hi, InType lo) noexcept
281{
282 using UInType = std::make_unsigned_t<InType>;
283 using UOutType = std::make_unsigned_t<OutType>;
284 return static_cast<OutType>(merge_bit_cast<UOutType>(static_cast<UInType>(hi), static_cast<UInType>(lo)));
285}
286
289template<std::signed_integral OutType, std::unsigned_integral InType>
290[[nodiscard]] constexpr OutType merge_bit_cast(InType hi, InType lo) noexcept
291{
292 using UOutType = std::make_unsigned_t<OutType>;
293 return narrow_cast<OutType>(merge_bit_cast<UOutType>(hi, lo));
294}
295
296[[nodiscard]] constexpr auto to_underlying(scoped_enum auto rhs) noexcept
297{
298 return static_cast<std::underlying_type_t<decltype(rhs)>>(rhs);
299}
300
301template<typename T>
302[[nodiscard]] constexpr bool to_bool(T&& rhs) noexcept requires(requires(T&& x) { static_cast<bool>(std::forward<T>(x)); })
303{
304 return static_cast<bool>(std::forward<T>(rhs));
305}
306
307} // namespace hi::inline v1
308
309hi_warning_pop();
This file includes required definitions.
#define hilet
Invariant should be the default for variables.
Definition required.hpp:23
T copy(T... args)