13#include "../macros.hpp"
16#include "concepts.hpp"
17#include "exception.hpp"
27hi_export_module(hikogui.utility.cast);
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);
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>
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>
417 requires(not std::same_as<In, Out>)
439 return static_cast<Out
>(rhs);
451template<std::
floating_po
int Out, std::
integral In>
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);
568 return static_cast<Out
>(out_unsigned);
581template<std::
integral Out>
582[[nodiscard]]
constexpr Out
char_cast(std::byte rhs)
noexcept
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>
610 using UInType = std::make_unsigned_t<InType>;
611 using UOutType = std::make_unsigned_t<OutType>;
617template<std::
signed_
integral OutType, std::
signed_
integral InType>
620 using UInType = std::make_unsigned_t<InType>;
621 using UOutType = std::make_unsigned_t<OutType>;
627template<std::
unsigned_
integral OutType, std::
signed_
integral InType>
630 using UInType = std::make_unsigned_t<InType>;
636template<std::
unsigned_
integral OutType, std::
signed_
integral InType>
639 using UInType = std::make_unsigned_t<InType>;
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>
661 using UInType = std::make_unsigned_t<InType>;
662 using UOutType = std::make_unsigned_t<OutType>;
668template<std::
signed_
integral OutType, std::
unsigned_
integral InType>
671 using UOutType = std::make_unsigned_t<OutType>;
676[[nodiscard]]
constexpr bool to_bool(T&& rhs)
noexcept
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);
707[[nodiscard]] std::intptr_t to_int(T *ptr)
noexcept
709 return reinterpret_cast<std::intptr_t
>(ptr);
712template<
typename T,
byte_like 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()) {
721 throw std::bad_cast();
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) {
727 throw std::bad_cast();
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)
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()) {
743 throw std::bad_cast();
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) {
749 throw std::bad_cast();
753 return {
reinterpret_cast<value_type *
>(bytes.data()), n};
756template<
typename T,
byte_like 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()) {
765 throw std::bad_cast();
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) {
773 throw std::bad_cast();
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)
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()) {
790 throw std::bad_cast();
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) {
798 throw std::bad_cast();
802 offset +=
sizeof(value_type) * n;
803 return {
reinterpret_cast<value_type *
>(data), n};
Utilities to assert and bound check.
hi_warning_ignore_msvc(26472)
Definition int_carry.hpp:32
Utilities for throwing exceptions and terminating the application.
The HikoGUI namespace.
Definition array_generic.hpp:21
The HikoGUI API version 1.
Definition array_generic.hpp:22
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
typename copy_cv< To, From >::type copy_cv_t
Type-trait to copy const volatile qualifiers from one type to another.
Definition type_traits.hpp:486
constexpr Out char_cast(In rhs) noexcept
Cast a character.
Definition cast.hpp:560
constexpr Out truncate(In rhs) noexcept
Cast between integral types truncating or zero-extending the result.
Definition cast.hpp:544
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