Skip to content

Instantly share code, notes, and snippets.

@uemuraj
Created January 22, 2023 03:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save uemuraj/da8aa535bbbf007d8749044385f6b5ec to your computer and use it in GitHub Desktop.
Save uemuraj/da8aa535bbbf007d8749044385f6b5ec to your computer and use it in GitHub Desktop.
narrow cast のお試し。
#include "pch.h"
#include <limits>
#include <stdexcept>
#include <type_traits>
#if !defined(__cpp_if_constexpr) || !defined(__cpp_static_assert)
#error
#endif
template<typename T, typename U>
constexpr T narrow_cast(U && u)
{
// 参照を取り除いた型を基に判断
using ValueT = std::remove_reference_t<T>;
using ValueU = std::remove_reference_t<U>;
// 大きい型への変換はコンパイルエラーとする
constexpr int DigitsT = std::numeric_limits<ValueT>::digits + std::is_signed<ValueT>::value;
constexpr int DigitsU = std::numeric_limits<ValueU>::digits + std::is_signed<ValueU>::value;
static_assert(DigitsT <= DigitsU);
if constexpr (DigitsT < DigitsU)
{
// 変換先の最小値と最大値
constexpr auto min = std::numeric_limits<ValueT>::min();
constexpr auto max = std::numeric_limits<ValueT>::max();
// 変換先と変換元が、それぞれ符号付きかどうか
if constexpr (std::is_signed_v<ValueT> && std::is_signed_v<ValueU>)
{
if (u < min || max < u)
{
throw std::bad_cast();
}
}
else if constexpr (std::is_unsigned_v<ValueT> && std::is_unsigned_v<ValueU>)
{
if (max < u)
{
throw std::bad_cast();
}
}
else if constexpr (std::is_signed_v<ValueU>)
{
if (u < 0 || ((ValueU) max) < u)
{
throw std::bad_cast();
}
}
else if constexpr (std::is_signed_v<ValueT>)
{
if (((ValueU) max) < u)
{
throw std::bad_cast();
}
}
}
return static_cast<T>(std::forward<U>(u));
}
TEST(NarrowCastTest, None)
{
EXPECT_EQ(0, narrow_cast<long>((long) 0));
EXPECT_EQ(0, narrow_cast<long>((unsigned long) 0));
EXPECT_EQ(0, narrow_cast<unsigned long>((long) 0));
EXPECT_EQ(0, narrow_cast<unsigned long>((unsigned long) 0));
EXPECT_EQ(-1, narrow_cast<long>((long) -1));
EXPECT_EQ(-1, narrow_cast<long>((unsigned long) -1));
EXPECT_EQ(-1, narrow_cast<unsigned long>((long) -1));
EXPECT_EQ(-1, narrow_cast<unsigned long>((unsigned long) -1));
#if 0
// コンパイルエラーとなる
EXPECT_EQ(0, narrow_cast<long long>((long) 0));
EXPECT_EQ(0, narrow_cast<long long>((unsigned long) 0));
EXPECT_EQ(0, narrow_cast<unsigned long long>((long) 0));
EXPECT_EQ(0, narrow_cast<unsigned long long>((unsigned long) 0));
#endif
}
TEST(NarrowCastTest, Signed)
{
EXPECT_EQ(LONG_MIN, narrow_cast<long>((long long) LONG_MIN));
EXPECT_EQ(LONG_MAX, narrow_cast<long>((long long) LONG_MAX));
EXPECT_THROW(narrow_cast<long>((long long) LONG_MIN - 1), std::bad_cast);
EXPECT_THROW(narrow_cast<long>((long long) LONG_MAX + 1), std::bad_cast);
}
TEST(NarrowCastTest, Unsigned)
{
EXPECT_EQ(0, narrow_cast<unsigned long>((unsigned long long) 0));
EXPECT_EQ(ULONG_MAX, narrow_cast<unsigned long>((unsigned long long) ULONG_MAX));
EXPECT_THROW(narrow_cast<unsigned long>((unsigned long long) -1), std::bad_cast);
EXPECT_THROW(narrow_cast<unsigned long>((unsigned long long) ULONG_MAX + 1), std::bad_cast);
}
TEST(NarrowCastTest, SignedToUnsigned)
{
EXPECT_EQ(0, narrow_cast<unsigned long>((long long) 0));
EXPECT_EQ(ULONG_MAX, narrow_cast<unsigned long>((long long) ULONG_MAX));
EXPECT_THROW(narrow_cast<unsigned long>((long long) -1), std::bad_cast);
EXPECT_THROW(narrow_cast<unsigned long>((long long) ULONG_MAX + 1), std::bad_cast);
}
TEST(NarrowCastTest, UnsignedToSigned)
{
EXPECT_EQ(0, narrow_cast<long>((unsigned long long) 0));
EXPECT_EQ(LONG_MAX, narrow_cast<long>((unsigned long long) LONG_MAX));
EXPECT_THROW(narrow_cast<long>((unsigned long long) - 1), std::bad_cast);
EXPECT_THROW(narrow_cast<long>((unsigned long long) LONG_MAX + 1), std::bad_cast);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment