Skip to content

Instantly share code, notes, and snippets.

@dlOuOlb
Last active January 1, 2024 06:11
Show Gist options
  • Save dlOuOlb/f21c082e850750500c0baa8a223d4b66 to your computer and use it in GitHub Desktop.
Save dlOuOlb/f21c082e850750500c0baa8a223d4b66 to your computer and use it in GitHub Desktop.
A function template about checked integer casting.
#if __cplusplus < 202002L
# error "This code is written in C++20."
#else
export module dlOuOlb.numeric:checked_cast;
import <format>; /* std::format */
import <limits>; /* std::numeric_limits */
import <stdexcept>; /* std::(overflow|underflow)_error */
import <string>; /* std::(to_)?string */
namespace dlOuOlb::numeric
{
template<class Integer> requires
std::numeric_limits<Integer>::is_integer
static const std::string description =
std::format
(
R"({1} {0}-digit integer)",
std::numeric_limits<Integer>::digits,
std::numeric_limits<Integer>::is_signed? R"(signed)": R"(unsigned)"
);
template<class Target, class Source> requires
std::numeric_limits<Target>::is_integer &&
std::numeric_limits<Source>::is_integer
static auto explain( const Source Value ) noexcept( false )->std::string
{
return std::format
(
R"({2:+#X} cannot be preserved when being casted from {1} to {0}.)",
description<Target>,
description<Source>,
Value
);
}
export extern R"(C++)"
template<class Target, class Source> requires
std::numeric_limits<Target>::is_integer &&
std::numeric_limits<Source>::is_integer
constexpr auto checked_cast( const Source Value ) noexcept
(
std::numeric_limits<Source>::digits <= std::numeric_limits<Target>::digits &&
(
std::numeric_limits<Target>::is_signed ||
!std::numeric_limits<Source>::is_signed
)
)->Target
{
if constexpr( !std::numeric_limits<Source>::is_signed )
if constexpr( std::numeric_limits<Source>::digits <= std::numeric_limits<Target>::digits )
return static_cast<Target>( Value );
else if( static_cast<Source>( std::numeric_limits<Target>::max( ) ) < Value )
throw std::overflow_error { explain<Target>( Value ) };
else
return static_cast<Target>( Value );
else if constexpr( std::numeric_limits<Target>::is_signed )
if constexpr( std::numeric_limits<Source>::digits <= std::numeric_limits<Target>::digits )
return static_cast<Target>( Value );
else if( Value < static_cast<Source>( std::numeric_limits<Target>::min( ) ) )
throw std::underflow_error { explain<Target>( Value ) };
else if( static_cast<Source>( std::numeric_limits<Target>::max( ) ) < Value )
throw std::overflow_error { explain<Target>( Value ) };
else
return static_cast<Target>( Value );
else
if( Value < 0 )
throw std::underflow_error { explain<Target>( Value ) };
else if constexpr( std::numeric_limits<Source>::digits <= std::numeric_limits<Target>::digits )
return static_cast<Target>( Value );
else if( static_cast<Source>( std::numeric_limits<Target>::max( ) ) < Value )
throw std::overflow_error { explain<Target>( Value ) };
else
return static_cast<Target>( Value );
}
}
#endif
#if __cplusplus < 202002L
# error "This code is written in C++20."
#else
export module dlOuOlb.numeric;
export import:checked_cast;
#endif
#if __cplusplus < 202002L
# error "This code is written in C++20."
#else
# include <iostream> /* std::(cerr|cout|endl) */
# include <stdexcept> /* std::(overflow|underflow)_error */
import dlOuOlb.numeric; /* dlOuOlb::numeric */
extern R"(C++)" auto main( void ) noexcept( false )->signed int
{
namespace S = std;
namespace OuO = dlOuOlb;
try
{
const constexpr signed long int Source = __cplusplus;
const unsigned short int Target = OuO::numeric::checked_cast<decltype( Target )>( Source );
static_cast<void>( S::cout << R"(__cplusplus : )" << Target << S::endl );
}
catch( const S::overflow_error &Error )
{
static_cast<void>( S::cerr << Error.what( ) << S::endl );
}
catch( const S::underflow_error &Error )
{
static_cast<void>( S::cerr << Error.what( ) << S::endl );
}
{
return 0;
}
}
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment