12#include "../macros.hpp"
15#include "concepts.hpp"
16#include "debugger.hpp"
17#include "exception.hpp"
26hi_export_module(hikogui.utility.cast);
49hi_export
namespace hi {
inline namespace v1 {
52[[nodiscard]]
constexpr T copy(T value)
noexcept
64template<
typename Out,
typename In>
65[[nodiscard]]
constexpr Out
up_cast(In *rhs)
noexcept
67 using out_type = std::remove_pointer_t<Out>;
69 static_assert(std::is_pointer_v<Out>,
"up_cast() Out template paramater must be a pointer if the input is a pointer.");
70 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.");
71 static_assert(std::is_base_of_v<out_type, In>,
"up_cast() may only be used to cast to a base-type.");
73 return static_cast<Out
>(rhs);
84[[nodiscard]]
constexpr Out
up_cast(nullptr_t)
noexcept
86 static_assert(std::is_pointer_v<Out>,
"up_cast() Out template paramater must be a pointer.");
97template<
typename Out,
typename In>
98[[nodiscard]]
constexpr Out
up_cast(In& rhs)
noexcept
100 using out_type = std::remove_reference_t<Out>;
102 static_assert(std::is_reference_v<Out>,
"up_cast() Out template paramater must be a reference if the input is a reference.");
103 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.");
104 static_assert(std::is_base_of_v<out_type, In>,
"up_cast() may only be used to cast to a base-type.");
106 return static_cast<Out
>(rhs);
117template<
typename Out,
typename In>
120 using out_type = std::remove_pointer_t<Out>;
122 static_assert(std::is_pointer_v<Out>,
"down_cast() Out template paramater must be a pointer if the input is a pointer.");
123 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.");
124 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.");
126 if constexpr (not std::is_base_of_v<out_type, In>) {
127 hi_axiom(rhs ==
nullptr or
dynamic_cast<Out
>(rhs) !=
nullptr);
129 return static_cast<Out
>(rhs);
137template<
typename Out>
138[[nodiscard]]
constexpr Out
down_cast(nullptr_t)
noexcept
139 requires std::is_pointer_v<Out>
151template<
typename Out,
typename In>
154 using out_type = std::remove_reference_t<Out>;
156 static_assert(std::is_reference_v<Out>,
"down_cast() Out template paramater must be a reference if the input is a reference.");
157 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.");
158 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.");
160 if constexpr (not std::is_base_of_v<out_type, In>) {
161 hi_axiom(
dynamic_cast<std::add_pointer_t<out_type>
>(
std::addressof(rhs)) !=
nullptr);
163 return static_cast<Out
>(rhs);
174template<
typename Out, std::same_as<Out> In>
175[[nodiscard]]
constexpr Out
wide_cast(In
const& rhs)
noexcept
187template<std::
floating_po
int Out, std::
floating_po
int In>
188[[nodiscard]]
constexpr Out
wide_cast(In
const& rhs)
noexcept
189 requires(not std::same_as<In, Out>)
193 "wide_cast() is only allowed to a floating point of the same or larger size.");
195 return static_cast<Out
>(rhs);
205template<std::
integral Out, std::
integral In>
207 requires(not std::same_as<In, Out>)
211 "wide_cast() is only allowed if the input is unsigned or if both input and output have the same signess.");
215 "wide_cast() is only allowed to an integer of the same or larger size.");
217 return static_cast<Out
>(rhs);
234template<std::
floating_po
int Out, std::
integral In>
239 "wide_cast() is only allowed if the input can be represented with perfect accuracy by the floating point output type.");
241 return static_cast<Out
>(rhs);
250template<std::
integral Out, arithmetic In>
253 if constexpr (std::is_floating_point_v<In>) {
264 return static_cast<Out
>(rhs);
275template<
typename Out, std::same_as<Out> In>
288template<std::
floating_po
int Out, std::
floating_po
int In>
290 requires(not std::same_as<In, Out>)
308template<std::
integral Out, std::
integral In>
310 requires(not std::same_as<In, Out>)
347template<std::
floating_po
int Out, std::
integral In>
353 constexpr auto lowest = -max;
355 return rhs >= lowest and rhs <= max;
376template<
typename Out, std::same_as<Out> In>
391template<std::
floating_po
int Out, std::
floating_po
int In>
393 requires(not std::same_as<In, Out>)
402 return static_cast<Out
>(rhs);
414template<std::
integral Out, std::
integral In>
416 requires(not std::same_as<In, Out>)
438 return static_cast<Out
>(rhs);
450template<std::
floating_po
int Out, std::
integral In>
456 constexpr auto lowest = -max;
458 hi_axiom(rhs >= lowest and rhs <= max);
463 hi_axiom(rhs <= max);
467 return static_cast<Out
>(rhs);
470template<std::
integral Out, std::
floating_po
int In>
471[[nodiscard]]
constexpr bool can_round_cast(In rhs)
noexcept
477template<
std::integral Out,
std::floating_point In>
478[[nodiscard]] constexpr
bool can_floor_cast(In rhs) noexcept
484template<
std::integral Out,
std::floating_point In>
485[[nodiscard]] constexpr
bool can_ceil_cast(In rhs) noexcept
491template<
std::integral Out,
std::floating_point In>
492[[nodiscard]] constexpr Out round_cast(In rhs) noexcept
496 return static_cast<Out
>(rhs_);
499template<std::
integral Out, std::
floating_po
int In>
500[[nodiscard]]
constexpr Out floor_cast(In rhs)
noexcept
504 return static_cast<Out
>(rhs_);
507template<std::
integral Out, std::
floating_po
int In>
508[[nodiscard]]
constexpr Out ceil_cast(In rhs)
noexcept
512 return static_cast<Out
>(rhs_);
517template<std::
integral In>
518[[nodiscard]]
constexpr std::make_unsigned_t<In>
to_unsigned(In rhs)
noexcept
520 return static_cast<std::make_unsigned_t<In>
>(rhs);
525template<std::
integral In>
526[[nodiscard]]
constexpr std::make_signed_t<In>
to_signed(In rhs)
noexcept
528 return static_cast<std::make_signed_t<In>
>(rhs);
533template<std::
integral Out, std::
integral In>
534[[nodiscard]]
constexpr Out
truncate(In rhs)
noexcept
549template<std::
integral Out, std::
integral In>
552 using in_unsigned_type = std::make_unsigned_t<In>;
553 using out_unsigned_type = std::make_unsigned_t<Out>;
556 auto in_unsigned =
static_cast<in_unsigned_type
>(rhs);
558 return static_cast<Out
>(out_unsigned);
571template<std::
integral Out>
572[[nodiscard]]
constexpr Out
char_cast(std::byte rhs)
noexcept
579template<std::
unsigned_
integral OutType, std::
unsigned_
integral InType>
582 static_assert(
sizeof(OutType) * 2 ==
sizeof(InType),
"Return value of low_bit_cast must be half the size of the input");
583 return static_cast<OutType
>(value);
588template<std::
unsigned_
integral OutType, std::
unsigned_
integral InType>
591 static_assert(
sizeof(OutType) * 2 ==
sizeof(InType),
"Return value of high_bit_cast must be half the size of the input");
592 return static_cast<OutType
>(value >>
sizeof(OutType) * CHAR_BIT);
597template<std::
signed_
integral OutType, std::
signed_
integral InType>
600 using UInType = std::make_unsigned_t<InType>;
601 using UOutType = std::make_unsigned_t<OutType>;
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::
unsigned_
integral OutType, std::
signed_
integral InType>
620 using UInType = std::make_unsigned_t<InType>;
626template<std::
unsigned_
integral OutType, std::
signed_
integral InType>
629 using UInType = std::make_unsigned_t<InType>;
635template<std::
unsigned_
integral OutType, std::
unsigned_
integral InType>
638 static_assert(
sizeof(OutType) ==
sizeof(InType) * 2,
"Return value of merge_bit_cast must be double the size of the input");
640 OutType r =
static_cast<OutType
>(
hi);
641 r <<=
sizeof(InType) * CHAR_BIT;
642 r |=
static_cast<OutType
>(lo);
648template<std::
signed_
integral OutType, std::
signed_
integral InType>
651 using UInType = std::make_unsigned_t<InType>;
652 using UOutType = std::make_unsigned_t<OutType>;
658template<std::
signed_
integral OutType, std::
unsigned_
integral InType>
661 using UOutType = std::make_unsigned_t<OutType>;
666[[nodiscard]]
constexpr bool to_bool(T&& rhs)
noexcept
673[[nodiscard]]
inline T to_ptr(std::intptr_t value)
noexcept
674 requires std::is_pointer_v<T>
676 return reinterpret_cast<T
>(value);
680[[nodiscard]] std::intptr_t to_int(T *ptr)
noexcept
682 return reinterpret_cast<std::intptr_t
>(ptr);
685template<
typename T,
byte_like Byte>
686[[nodiscard]] copy_cv_t<T, Byte>& implicit_cast(std::span<Byte> bytes)
688 using value_type = copy_cv_t<T, Byte>;
690 static_assert(std::is_trivially_default_constructible_v<value_type>);
691 static_assert(std::is_trivially_destructible_v<value_type>);
693 if (
sizeof(value_type) > bytes.size()) {
694 throw std::bad_cast();
696 hi_axiom_not_null(bytes.data());
698 if constexpr (
alignof(value_type) != 1) {
699 if (std::bit_cast<std::uintptr_t>(bytes.data()) %
alignof(value_type) != 0) {
700 throw std::bad_cast();
704 return *
reinterpret_cast<value_type *
>(bytes.data());
707template<
typename T,
byte_like Byte>
708[[nodiscard]] std::span<copy_cv_t<T, Byte>> implicit_cast(std::span<Byte> bytes,
size_t n)
710 using value_type = copy_cv_t<T, Byte>;
712 static_assert(std::is_trivially_default_constructible_v<value_type>);
713 static_assert(std::is_trivially_destructible_v<value_type>);
715 if (
sizeof(value_type) * n > bytes.size()) {
716 throw std::bad_cast();
718 hi_axiom_not_null(bytes.data());
720 if constexpr (
alignof(value_type) != 1) {
721 if (std::bit_cast<std::uintptr_t>(bytes.data()) %
alignof(value_type) != 0) {
722 throw std::bad_cast();
726 return {
reinterpret_cast<value_type *
>(bytes.data()), n};
729template<
typename T,
byte_like Byte>
730[[nodiscard]] copy_cv_t<T, Byte>& implicit_cast(
size_t& offset, std::span<Byte> bytes)
732 using value_type = copy_cv_t<T, Byte>;
734 static_assert(std::is_trivially_default_constructible_v<value_type>);
735 static_assert(std::is_trivially_destructible_v<value_type>);
737 if (
sizeof(value_type) + offset > bytes.size()) {
738 throw std::bad_cast();
740 hi_axiom_not_null(bytes.data());
742 hilet data = bytes.data() + offset;
744 if constexpr (
alignof(value_type) != 1) {
745 if (std::bit_cast<std::uintptr_t>(data) %
alignof(value_type) != 0) {
746 throw std::bad_cast();
750 offset +=
sizeof(value_type);
751 return *
reinterpret_cast<value_type *
>(data);
754template<
typename T,
byte_like Byte>
755[[nodiscard]] std::span<copy_cv_t<T, Byte>> implicit_cast(
size_t& offset, std::span<Byte> bytes,
size_t n)
757 using value_type = copy_cv_t<T, Byte>;
759 static_assert(std::is_trivially_default_constructible_v<value_type>);
760 static_assert(std::is_trivially_destructible_v<value_type>);
762 if (
sizeof(value_type) * n + offset > bytes.size()) {
763 throw std::bad_cast();
765 hi_axiom_not_null(bytes.data());
767 hilet data = bytes.data() + offset;
769 if constexpr (
alignof(value_type) != 1) {
770 if (std::bit_cast<std::uintptr_t>(data) %
alignof(value_type) != 0) {
771 throw std::bad_cast();
775 offset +=
sizeof(value_type) * n;
776 return {
reinterpret_cast<value_type *
>(data), n};
Utilities to assert and bound check.
hi_warning_ignore_msvc(26472)
Definition int_carry.hpp:30
geometry/margins.hpp
Definition lookahead_iterator.hpp:5
The HikoGUI API version 1.
Definition lookahead_iterator.hpp:6
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:526
constexpr Out char_cast(In rhs) noexcept
Cast a character.
Definition cast.hpp:550
constexpr Out truncate(In rhs) noexcept
Cast between integral types truncating or zero-extending the result.
Definition cast.hpp:534
constexpr OutType low_bit_cast(InType value) noexcept
Return the low half of the input value.
Definition cast.hpp:580
constexpr OutType high_bit_cast(InType value) noexcept
Return the upper half of the input value.
Definition cast.hpp:589
constexpr Out saturate_cast(In rhs) noexcept
Cast a numeric value to an integer saturating on overflow.
Definition cast.hpp:251
constexpr OutType merge_bit_cast(InType hi, InType lo) noexcept
Return the upper half of the input value.
Definition cast.hpp:636
constexpr bool can_narrow_cast(In const &rhs) noexcept
Check if a value can be casted to a narrow type.
Definition cast.hpp:276
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:118
constexpr Out up_cast(In *rhs) noexcept
Cast a pointer to a class to its base class or itself.
Definition cast.hpp:65
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:518
constexpr Out narrow_cast(In const &rhs) noexcept
Cast numeric values without loss of precision.
Definition cast.hpp:377
constexpr Out wide_cast(In const &rhs) noexcept
Cast to a type which can hold all values from the input type.
Definition cast.hpp:175