13#include "../macros.hpp"
16#include "concepts.hpp"
17#include "exception.hpp"
27hi_export_module(hikogui.utility.cast);
32hi_warning_ignore_msvc(26472);
37hi_warning_ignore_msvc(26467);
40hi_warning_ignore_msvc(26496);
43hi_warning_ignore_msvc(26466);
46hi_warning_ignore_msvc(26474);
48hi_warning_ignore_msvc(4702);
50hi_export
namespace hi {
inline namespace v1 {
53[[nodiscard]]
constexpr T
copy(T value)
noexcept
65template<
typename Out,
typename In>
66[[nodiscard]]
constexpr Out
up_cast(In *rhs)
noexcept
68 using out_type = std::remove_pointer_t<Out>;
70 static_assert(std::is_pointer_v<Out>,
"up_cast() Out template paramater must be a pointer if the input is a pointer.");
71 static_assert(std::is_const_v<out_type> == std::is_const_v<In> or std::is_const_v<out_type>,
"up_cast() can not cast away const.");
72 static_assert(std::is_base_of_v<out_type, In>,
"up_cast() may only be used to cast to a base-type.");
74 return static_cast<Out
>(rhs);
85[[nodiscard]]
constexpr Out
up_cast(nullptr_t)
noexcept
87 static_assert(std::is_pointer_v<Out>,
"up_cast() Out template paramater must be a pointer.");
98template<
typename Out,
typename In>
99[[nodiscard]]
constexpr Out
up_cast(In& rhs)
noexcept
101 using out_type = std::remove_reference_t<Out>;
103 static_assert(std::is_reference_v<Out>,
"up_cast() Out template paramater must be a reference if the input is a reference.");
104 static_assert(std::is_const_v<out_type> == std::is_const_v<In> or std::is_const_v<out_type>,
"up_cast() can not cast away const.");
105 static_assert(std::is_base_of_v<out_type, In>,
"up_cast() may only be used to cast to a base-type.");
107 return static_cast<Out
>(rhs);
118template<
typename Out,
typename In>
121 using out_type = std::remove_pointer_t<Out>;
123 static_assert(std::is_pointer_v<Out>,
"down_cast() Out template paramater must be a pointer if the input is a pointer.");
124 static_assert(std::is_const_v<out_type> == std::is_const_v<In> or std::is_const_v<out_type>,
"down_cast() can not cast away const.");
125 static_assert(std::is_base_of_v<out_type, In> or std::is_base_of_v<In, out_type>,
"down_cast() may only be used to cast to a related type.");
127 if constexpr (not std::is_base_of_v<out_type, In>) {
128 hi_axiom(rhs ==
nullptr or
dynamic_cast<Out
>(rhs) !=
nullptr);
130 return static_cast<Out
>(rhs);
138template<
typename Out>
139[[nodiscard]]
constexpr Out
down_cast(nullptr_t)
noexcept
140 requires std::is_pointer_v<Out>
152template<
typename Out,
typename In>
155 using out_type = std::remove_reference_t<Out>;
157 static_assert(std::is_reference_v<Out>,
"down_cast() Out template paramater must be a reference if the input is a reference.");
158 static_assert(std::is_const_v<out_type> == std::is_const_v<In> or std::is_const_v<out_type>,
"down_cast() can not cast away const.");
159 static_assert(std::is_base_of_v<out_type, In> or std::is_base_of_v<In, out_type>,
"down_cast() may only be used to cast to a related type.");
161 if constexpr (not std::is_base_of_v<out_type, In>) {
162 hi_axiom(
dynamic_cast<std::add_pointer_t<out_type>
>(
std::addressof(rhs)) !=
nullptr);
164 return static_cast<Out
>(rhs);
175template<
typename Out, std::same_as<Out> In>
176[[nodiscard]]
constexpr Out
wide_cast(In
const& rhs)
noexcept
188template<std::
floating_po
int Out, std::
floating_po
int In>
189[[nodiscard]]
constexpr Out
wide_cast(In
const& rhs)
noexcept
190 requires(not std::same_as<In, Out>)
194 "wide_cast() is only allowed to a floating point of the same or larger size.");
196 return static_cast<Out
>(rhs);
206template<std::
integral Out, std::
integral In>
208 requires(not std::same_as<In, Out>)
212 "wide_cast() is only allowed if the input is unsigned or if both input and output have the same signess.");
216 "wide_cast() is only allowed to an integer of the same or larger size.");
218 return static_cast<Out
>(rhs);
235template<std::
floating_po
int Out, std::
integral In>
240 "wide_cast() is only allowed if the input can be represented with perfect accuracy by the floating point output type.");
242 return static_cast<Out
>(rhs);
251template<std::
integral Out, arithmetic In>
254 if constexpr (std::is_floating_point_v<In>) {
265 return static_cast<Out
>(rhs);
276template<
typename Out, std::same_as<Out> In>
289template<std::
floating_po
int Out, std::
floating_po
int In>
291 requires(not std::same_as<In, Out>)
309template<std::
integral Out, std::
integral In>
311 requires(not std::same_as<In, Out>)
348template<std::
floating_po
int Out, std::
integral In>
354 constexpr auto lowest = -max;
356 return rhs >= lowest and rhs <= max;
377template<
typename Out, std::same_as<Out> In>
392template<std::
floating_po
int Out, std::
floating_po
int In>
394 requires(not std::same_as<In, Out>)
403 return static_cast<Out
>(rhs);
415template<std::
integral Out, std::
integral In>
416[[nodiscard]]
constexpr Out
narrow_cast(In
const& rhs)
noexcept
417 requires(not std::same_as<In, Out>)
439 return static_cast<Out
>(rhs);
451template<std::
floating_po
int Out, std::
integral In>
452[[nodiscard]]
constexpr Out
narrow_cast(In
const& rhs)
noexcept
457 constexpr auto lowest = -max;
459 hi_axiom(rhs >= lowest and rhs <= max);
464 hi_axiom(rhs <= max);
468 return static_cast<Out
>(rhs);
471template<std::
integral Out, std::
floating_po
int In>
472[[nodiscard]]
constexpr bool can_round_cast(In rhs)
noexcept
478template<
std::integral Out,
std::floating_point In>
479[[nodiscard]] constexpr
bool can_floor_cast(In rhs) noexcept
485template<
std::integral Out,
std::floating_point In>
486[[nodiscard]] constexpr
bool can_ceil_cast(In rhs) noexcept
492template<
std::integral Out,
std::floating_point In>
493[[nodiscard]] constexpr Out round_cast(In rhs) noexcept
499 hi_axiom(rhs_ >= lowest and rhs_ <= highest);
500 return static_cast<Out
>(rhs_);
503template<std::
integral Out, std::
floating_po
int In>
504[[nodiscard]]
constexpr Out floor_cast(In rhs)
noexcept
510 hi_axiom(rhs_ >= lowest and rhs_ <= highest);
511 return static_cast<Out
>(rhs_);
514template<std::
integral Out, std::
floating_po
int In>
515[[nodiscard]]
constexpr Out ceil_cast(In rhs)
noexcept
521 hi_axiom(rhs_ >= lowest and rhs_ <= highest);
522 return static_cast<Out
>(rhs_);
527template<std::
integral In>
528[[nodiscard]]
constexpr std::make_unsigned_t<In>
to_unsigned(In rhs)
noexcept
530 return static_cast<std::make_unsigned_t<In>
>(rhs);
535template<std::
integral In>
536[[nodiscard]]
constexpr std::make_signed_t<In>
to_signed(In rhs)
noexcept
538 return static_cast<std::make_signed_t<In>
>(rhs);
543template<std::
integral Out, std::
integral In>
544[[nodiscard]]
constexpr Out
truncate(In rhs)
noexcept
559template<std::
integral Out, std::
integral In>
562 using in_unsigned_type = std::make_unsigned_t<In>;
563 using out_unsigned_type = std::make_unsigned_t<Out>;
566 auto in_unsigned =
static_cast<in_unsigned_type
>(rhs);
567 auto out_unsigned = narrow_cast<out_unsigned_type>(in_unsigned);
568 return static_cast<Out
>(out_unsigned);
581template<std::
integral Out>
582[[nodiscard]]
constexpr Out
char_cast(std::byte rhs)
noexcept
584 return char_cast<Out>(
static_cast<uint8_t
>(rhs));
589template<std::
unsigned_
integral OutType, std::
unsigned_
integral InType>
592 static_assert(
sizeof(OutType) * 2 ==
sizeof(InType),
"Return value of low_bit_cast must be half the size of the input");
593 return static_cast<OutType
>(value);
598template<std::
unsigned_
integral OutType, std::
unsigned_
integral InType>
601 static_assert(
sizeof(OutType) * 2 ==
sizeof(InType),
"Return value of high_bit_cast must be half the size of the input");
602 return static_cast<OutType
>(value >>
sizeof(OutType) * CHAR_BIT);
607template<std::
signed_
integral OutType, std::
signed_
integral InType>
608[[nodiscard]]
constexpr OutType
low_bit_cast(InType value)
noexcept
610 using UInType = std::make_unsigned_t<InType>;
611 using UOutType = std::make_unsigned_t<OutType>;
612 return static_cast<OutType
>(low_bit_cast<UOutType>(
static_cast<UInType
>(value)));
617template<std::
signed_
integral OutType, std::
signed_
integral InType>
618[[nodiscard]]
constexpr OutType
high_bit_cast(InType value)
noexcept
620 using UInType = std::make_unsigned_t<InType>;
621 using UOutType = std::make_unsigned_t<OutType>;
622 return static_cast<OutType
>(high_bit_cast<UOutType>(
static_cast<UInType
>(value)));
627template<std::
unsigned_
integral OutType, std::
signed_
integral InType>
628[[nodiscard]]
constexpr OutType
low_bit_cast(InType value)
noexcept
630 using UInType = std::make_unsigned_t<InType>;
631 return low_bit_cast<OutType>(
static_cast<UInType
>(value));
636template<std::
unsigned_
integral OutType, std::
signed_
integral InType>
637[[nodiscard]]
constexpr OutType
high_bit_cast(InType value)
noexcept
639 using UInType = std::make_unsigned_t<InType>;
640 return high_bit_cast<OutType>(
static_cast<UInType
>(value));
645template<std::
unsigned_
integral OutType, std::
unsigned_
integral InType>
648 static_assert(
sizeof(OutType) ==
sizeof(InType) * 2,
"Return value of merge_bit_cast must be double the size of the input");
650 OutType r =
static_cast<OutType
>(
hi);
651 r <<=
sizeof(InType) * CHAR_BIT;
652 r |=
static_cast<OutType
>(lo);
658template<std::
signed_
integral OutType, std::
signed_
integral InType>
659[[nodiscard]]
constexpr OutType
merge_bit_cast(InType
hi, InType lo)
noexcept
661 using UInType = std::make_unsigned_t<InType>;
662 using UOutType = std::make_unsigned_t<OutType>;
663 return static_cast<OutType
>(merge_bit_cast<UOutType>(
static_cast<UInType
>(
hi),
static_cast<UInType
>(lo)));
668template<std::
signed_
integral OutType, std::
unsigned_
integral InType>
669[[nodiscard]]
constexpr OutType
merge_bit_cast(InType
hi, InType lo)
noexcept
671 using UOutType = std::make_unsigned_t<OutType>;
672 return narrow_cast<OutType>(merge_bit_cast<UOutType>(
hi, lo));
676[[nodiscard]]
constexpr bool to_bool(T&& rhs)
noexcept
677 requires(
requires(T&& x) {
static_cast<bool>(std::forward<T>(x)); })
679 return static_cast<bool>(std::forward<T>(rhs));
689[[nodiscard]]
constexpr T
to_mask(
bool v)
noexcept
691 using large_bool = make_intxx_t<
sizeof(T) * CHAR_BIT>;
693 auto r =
static_cast<large_bool
>(v);
694 r <<=
sizeof(T) * CHAR_BIT - 1;
695 r >>=
sizeof(T) * CHAR_BIT - 1;
696 return std::bit_cast<T>(r);
701 requires std::is_pointer_v<T>
703 return reinterpret_cast<T
>(value);
712template<
typename T,
byte_like Byte>
713[[nodiscard]] copy_cv_t<T, Byte>& implicit_cast(std::span<Byte> bytes)
715 using value_type = copy_cv_t<T, Byte>;
717 static_assert(std::is_trivially_default_constructible_v<value_type>);
718 static_assert(std::is_trivially_destructible_v<value_type>);
720 if (
sizeof(value_type) > bytes.size()) {
723 hi_axiom_not_null(bytes.data());
725 if constexpr (
alignof(value_type) != 1) {
726 if (std::bit_cast<std::uintptr_t>(bytes.data()) %
alignof(value_type) != 0) {
731 return *
reinterpret_cast<value_type *
>(bytes.data());
734template<
typename T,
byte_like Byte>
735[[nodiscard]] std::span<copy_cv_t<T, Byte>> implicit_cast(std::span<Byte> bytes,
size_t n)
737 using value_type = copy_cv_t<T, Byte>;
739 static_assert(std::is_trivially_default_constructible_v<value_type>);
740 static_assert(std::is_trivially_destructible_v<value_type>);
742 if (
sizeof(value_type) * n > bytes.size()) {
745 hi_axiom_not_null(bytes.data());
747 if constexpr (
alignof(value_type) != 1) {
748 if (std::bit_cast<std::uintptr_t>(bytes.data()) %
alignof(value_type) != 0) {
753 return {
reinterpret_cast<value_type *
>(bytes.data()), n};
756template<
typename T,
byte_like Byte>
757[[nodiscard]] copy_cv_t<T, Byte>& implicit_cast(
size_t& offset, std::span<Byte> bytes)
759 using value_type = copy_cv_t<T, Byte>;
761 static_assert(std::is_trivially_default_constructible_v<value_type>);
762 static_assert(std::is_trivially_destructible_v<value_type>);
764 if (
sizeof(value_type) + offset > bytes.size()) {
767 hi_axiom_not_null(bytes.data());
769 auto const data = bytes.data() + offset;
771 if constexpr (
alignof(value_type) != 1) {
772 if (std::bit_cast<std::uintptr_t>(data) %
alignof(value_type) != 0) {
777 offset +=
sizeof(value_type);
778 return *
reinterpret_cast<value_type *
>(data);
781template<
typename T,
byte_like Byte>
782[[nodiscard]] std::span<copy_cv_t<T, Byte>> implicit_cast(
size_t& offset, std::span<Byte> bytes,
size_t n)
784 using value_type = copy_cv_t<T, Byte>;
786 static_assert(std::is_trivially_default_constructible_v<value_type>);
787 static_assert(std::is_trivially_destructible_v<value_type>);
789 if (
sizeof(value_type) * n + offset > bytes.size()) {
792 hi_axiom_not_null(bytes.data());
794 auto const data = bytes.data() + offset;
796 if constexpr (
alignof(value_type) != 1) {
797 if (std::bit_cast<std::uintptr_t>(data) %
alignof(value_type) != 0) {
802 offset +=
sizeof(value_type) * n;
803 return {
reinterpret_cast<value_type *
>(data), n};
Utilities to assert and bound check.
Utilities for throwing exceptions and terminating the application.
@ truncate
After the file has been opened, truncate it.
The HikoGUI namespace.
Definition array_generic.hpp:20
constexpr std::make_signed_t< In > to_signed(In rhs) noexcept
Cast an integral to an signed integral of the same size.
Definition cast.hpp:536
constexpr Out char_cast(In rhs) noexcept
Cast a character.
Definition cast.hpp:560
constexpr OutType low_bit_cast(InType value) noexcept
Return the low half of the input value.
Definition cast.hpp:590
constexpr OutType high_bit_cast(InType value) noexcept
Return the upper half of the input value.
Definition cast.hpp:599
constexpr Out saturate_cast(In rhs) noexcept
Cast a numeric value to an integer saturating on overflow.
Definition cast.hpp:252
constexpr OutType merge_bit_cast(InType hi, InType lo) noexcept
Return the upper half of the input value.
Definition cast.hpp:646
constexpr bool can_narrow_cast(In const &rhs) noexcept
Check if a value can be casted to a narrow type.
Definition cast.hpp:277
constexpr auto three_way_compare(Lhs const &lhs, Rhs const &rhs) noexcept
Safely compare two arithmetic values to each other.
Definition compare.hpp:126
constexpr Out down_cast(In *rhs) noexcept
Cast a pointer to a class to its derived class or itself.
Definition cast.hpp:119
constexpr Out up_cast(In *rhs) noexcept
Cast a pointer to a class to its base class or itself.
Definition cast.hpp:66
constexpr std::make_unsigned_t< In > to_unsigned(In rhs) noexcept
Cast an integral to an unsigned integral of the same size.
Definition cast.hpp:528
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:378
constexpr Out wide_cast(In const &rhs) noexcept
Cast to a type which can hold all values from the input type.
Definition cast.hpp:176
constexpr T to_mask(bool v) noexcept
Create a mask from a boolean value.
Definition cast.hpp:689
DOXYGEN BUG.
Definition algorithm_misc.hpp:20