Skip to content

Commit bc02873

Browse files
committed
Add narrowing 'as' cast and static assert failure instead of nonesuch return
1 parent b1754db commit bc02873

File tree

1 file changed

+29
-3
lines changed

1 file changed

+29
-3
lines changed

include/cpp2util.h

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -552,9 +552,35 @@ auto is( X const& x ) -> bool {
552552
//-------------------------------------------------------------------------------------------------------------
553553
// Built-in as (partial)
554554
//
555-
template< typename C >
556-
auto as(...) -> auto {
557-
return nonesuch;
555+
template <typename... Ts>
556+
inline constexpr auto program_violates_type_safety_guarantee = sizeof...(Ts) < 0;
557+
558+
template< typename C, typename... Ts >
559+
auto as(Ts... ts) -> auto {
560+
static_assert(program_violates_type_safety_guarantee<Ts...>, "safe 'as' cast is not defined");
561+
}
562+
563+
struct narrowing_error : public std::exception
564+
{
565+
const char* what() const noexcept override { return "narrowing_error"; }
566+
};
567+
568+
template< typename C, typename X >
569+
requires ( !std::is_same_v<C, X>
570+
&& std::is_arithmetic_v<C> && std::is_arithmetic_v<X>
571+
)
572+
auto as( const X& x ) -> auto
573+
requires (!requires { C{x}; })
574+
{
575+
constexpr const bool is_different_signedness = (std::is_signed_v<C> != std::is_signed_v<X>);
576+
const C value = static_cast<C>(x);
577+
578+
if (static_cast<X>(value) != x || (is_different_signedness && ((value < C{}) != (x < X{}))))
579+
{
580+
throw narrowing_error{};
581+
}
582+
583+
return value;
558584
}
559585

560586
template< typename C, typename X >

0 commit comments

Comments
 (0)