#include <iostream>
#include <limits>
using namespace std;
#if defined(_MSC_VER) && _MSC_VER < 1900
#define CONST_EXPPR
#else
#define CONST_EXPPR constexpr
#endif
/*
* Helper tempaltes which returns common minimum and maximum values for integer types
*/
template<typename DestinationType, typename SourceType, bool commonSignes, bool DestSourceEqual>
struct HelperCommonLimits
{
static CONST_EXPPR SourceType max()
{
return static_cast<SourceType>(std::numeric_limits<DestinationType>::max());
}
static CONST_EXPPR SourceType min()
{
return static_cast<SourceType>(std::numeric_limits<DestinationType>::min());
}
};
template<typename DestinationType, typename SourceType>
struct HelperCommonLimits<DestinationType, SourceType, false, false>
{
static CONST_EXPPR SourceType max()
{
return static_cast<SourceType>(std::numeric_limits<DestinationType>::max());
}
static CONST_EXPPR SourceType min()
{
return 0;
}
};
template<typename DestinationType, typename SourceType>
struct HelperCommonLimits<DestinationType, SourceType, false, true>
{
static CONST_EXPPR SourceType max()
{
return static_cast<SourceType>(std::numeric_limits<typename std::make_signed<DestinationType>::type>::max());
}
static CONST_EXPPR SourceType min()
{
return 0;
}
};
/*
* Helper template used to detect if size on destination type is bigger.
* If signs are same than simple casting is enough.
* if sings are different zero minimum is applied
*/
template<typename DestinationType, typename SourceType, bool singedToUnsigned, bool DestIsBigger>
struct HelperNorrowCast
{
static CONST_EXPPR DestinationType Cast(SourceType arg)
{
using HelperType = HelperCommonLimits<DestinationType,
SourceType,
std::is_signed<SourceType>::value == std::is_signed<DestinationType>::value,
(sizeof(DestinationType) == sizeof(SourceType))>;
return static_cast<DestinationType>(
HelperType::min() >= arg ? HelperType::min() :
HelperType::max() <= arg ? HelperType::max() :
arg);
}
};
template<typename DestinationType, typename SourceType>
struct HelperNorrowCast<DestinationType, SourceType, false, true>
{
static CONST_EXPPR DestinationType Cast(SourceType arg)
{
return static_cast<DestinationType>(arg);
}
};
template<typename DestinationType, typename SourceType>
struct HelperNorrowCast<DestinationType, SourceType, true, true>
{
static CONST_EXPPR DestinationType Cast(SourceType arg)
{
return static_cast<DestinationType>(arg >= 0 ? arg : 0);
}
};
/**
* Used to do integer casting in such way that if argument value exceeds destination type range
* result value is returns respctivly maximum or minimum values of result type.
*/
template<typename DestinationType, typename SourceType>
CONST_EXPPR DestinationType narrow_cast(SourceType arg)
{
static_assert(std::is_integral<DestinationType>::value, "Only integer types are supported");
static_assert(std::is_integral<SourceType>::value, "Only integer types are supported");
return HelperNorrowCast<DestinationType, SourceType,
std::is_signed<SourceType>::value && std::is_unsigned<DestinationType>::value,
(sizeof(DestinationType) > sizeof(SourceType))>::Cast(arg);
}
#define EXPECT_EQ(x, y) if (x != y) cout << "Failed test for " << #x \
<< "\nexpected: " << y \
<< "\n actual: " << x << endl; else
int main() {
// to smaller type signed to signed
EXPECT_EQ(narrow_cast<short>(100000), 32767);
EXPECT_EQ(narrow_cast<short>(-100000), -32768);
EXPECT_EQ(narrow_cast<short>(0), 0);
EXPECT_EQ(narrow_cast<short>(-1), -1);
// to smaller type unsigned to unsigned
EXPECT_EQ(narrow_cast<unsigned short>(100000u), 65535u);
EXPECT_EQ(narrow_cast<unsigned short>(60000u), 60000u);
EXPECT_EQ(narrow_cast<unsigned short>(0u), 0u);
EXPECT_EQ(narrow_cast<unsigned short>(1u), 1u);
// to smaller type signed to signed to unsigned
EXPECT_EQ(narrow_cast<unsigned short>(100000), 65535);
EXPECT_EQ(narrow_cast<unsigned short>(-100000), 0);
EXPECT_EQ(narrow_cast<unsigned short>(0), 0);
EXPECT_EQ(narrow_cast<unsigned short>(-1), 0);
// to smaller type unsigned to signed
EXPECT_EQ(narrow_cast<short>(100000u), 32767);
EXPECT_EQ(narrow_cast<short>(60000u), 32767);
EXPECT_EQ(narrow_cast<short>(0u), 0);
EXPECT_EQ(narrow_cast<short>(1u), 1);
// to same size type signed to signed ////
EXPECT_EQ(narrow_cast<int>(0x7fffffff), 0x7fffffff);
EXPECT_EQ(narrow_cast<int>(-2147483648), -2147483648);
EXPECT_EQ(narrow_cast<int>(0), 0);
EXPECT_EQ(narrow_cast<int>(-1), -1);
// to same size type unsigned to unsigned
EXPECT_EQ(narrow_cast<unsigned int>(0xffffffffu), 0xffffffffu);
EXPECT_EQ(narrow_cast<unsigned int>(60000u), 60000u);
EXPECT_EQ(narrow_cast<unsigned int>(0u), 0u);
EXPECT_EQ(narrow_cast<unsigned int>(1u), 1u);
// to same size type signed to signed to unsigned
EXPECT_EQ(narrow_cast<unsigned int>(0x7fffffff), 0x7fffffffu);
EXPECT_EQ(narrow_cast<unsigned int>(-2147483648), 0u);
EXPECT_EQ(narrow_cast<unsigned int>(0), 0u);
EXPECT_EQ(narrow_cast<unsigned int>(-1), 0u);
// to same size type unsigned to signed
EXPECT_EQ(narrow_cast<int>(0xffffffffu), 0x7fffffff);
EXPECT_EQ(narrow_cast<int>(0x7fffffffu), 0x7fffffff);
EXPECT_EQ(narrow_cast<int>(0x80000000u), 0x7fffffff);
EXPECT_EQ(narrow_cast<int>(0u), 0);
EXPECT_EQ(narrow_cast<int>(1u), 1);
// to bigger size type signed to signed ////
EXPECT_EQ(narrow_cast<int64_t>(0x7fffffff), 0x7fffffffll);
EXPECT_EQ(narrow_cast<int64_t>(-2147483648), -2147483648ll);
EXPECT_EQ(narrow_cast<int64_t>(0), 0);
EXPECT_EQ(narrow_cast<int64_t>(-1), -1ll);
// to bigger size type unsigned to unsigned
EXPECT_EQ(narrow_cast<uint64_t>(0xffffffffu), 0xffffffffull);
EXPECT_EQ(narrow_cast<uint64_t>(60000u), 60000ull);
EXPECT_EQ(narrow_cast<uint64_t>(0u), 0ull);
EXPECT_EQ(narrow_cast<uint64_t>(1u), 1ull);
// to bigger size type signed to signed to unsigned
EXPECT_EQ(narrow_cast<uint64_t>(0x7fffffff), 0x7fffffffull);
EXPECT_EQ(narrow_cast<uint64_t>(-2147483648), 0ull);
EXPECT_EQ(narrow_cast<uint64_t>(0), 0ull);
EXPECT_EQ(narrow_cast<uint64_t>(-1), 0ull);
// to bigger size type unsigned to signed
EXPECT_EQ(narrow_cast<int64_t>(0xffffffffu), 0xffffffffll);
EXPECT_EQ(narrow_cast<int64_t>(0x7fffffffu), 0x7fffffffll);
EXPECT_EQ(narrow_cast<int64_t>(0x80000000u), 0x80000000ll);
EXPECT_EQ(narrow_cast<int64_t>(0u), 0ll);
EXPECT_EQ(narrow_cast<int64_t>(1u), 1ll);
return 0;
}