Skip to content

Instantly share code, notes, and snippets.

@michaelbartnett
Last active January 17, 2019 11:02
Show Gist options
  • Save michaelbartnett/9bb936986c08c35da3b2b4c2477be307 to your computer and use it in GitHub Desktop.
Save michaelbartnett/9bb936986c08c35da3b2b4c2477be307 to your computer and use it in GitHub Desktop.
truly horrible constepxr function template for narrowing numeric conversions. doesn't allow conversions between integral and floating point. -std=c++11 and -fno-exceptions compatible. the worst.
// what a mess
#if NDEBUG
template<typename TOut, typename TIn>
constexpr TOut narrowed(TIn&& inp) noexcept
{
return static_cast<TOut>(inp);
}
#define NARROWED(TOut, expr) static_cast<TOut>(expr)
#else
#include <type_traits>
#include <cassert>
#include <utility>
#include <exception>
#if (defined(__cpp_exceptions) && (0 != __cpp_exceptions)) || (defined(_CPPUNWIND) && (0 != _CPPUNWIND))
#define NARROWED_HAS_EXCEPTIONS_ENABLED 1
#else
#define NARROWED_HAS_EXCEPTIONS_ENABLED 0
#endif
#if NARROWED_HAS_EXCEPTIONS_ENABLED
#define NARROWED_THROW(expr) throw expr
#else
#if defined(__cplusplus) && (__cplusplus >= 201402L)
//#if defined(__cpp_constexpr) && (__cpp_constepxr >= 201304)
#define NARROWED_THROW(expr) (assert(false), TOut{})
#else
#define NARROWED_THROW(expr) (std::terminate(), TOut{})
#endif
#endif
namespace narrowed_impl_details
{
template<typename T>
constexpr T abs_diff(const T& a, const T& b)
{
return a > b ? a - b : b - a;
}
}
template<typename TIn, typename TOut>
struct narrowed_failed
{
explicit narrowed_failed(void (*f)() ) { f(); }
};
#define CONSTEXPR_NARROWED_FAIL(in_type, out_type, msg) narrowed_failed<in_type, out_type>([](){ assert(msg); })
#define NARROWED_IMPL(NARROWED_NOEXCEPT_PARAMETER) \
template \
< \
typename TOut, typename TIn, \
typename = typename std::enable_if<std::is_integral<TOut>::value && std::is_integral<TIn>::value>::type \
> \
NARROWED_CONSTEXPR TOut narrowed(TIn&& inp) noexcept(NARROWED_NOEXCEPT_PARAMETER) \
{ \
static_assert(!std::is_same<TOut, bool>::value && !std::is_same<TIn, bool>::value, "Can't narrow with bools"); \
return (static_cast<TIn>(static_cast<TOut>(std::forward<TIn>(inp))) == inp) ? \
static_cast<TOut>(std::forward<TIn>(inp)) \
: NARROWED_THROW(CONSTEXPR_NARROWED_FAIL(TIn, TOut, "Failed to narrow integer")); \
} \
\
template \
< \
typename TOut, typename TIn, \
typename = typename std::enable_if<std::is_arithmetic<TOut>::value && std::is_arithmetic<TIn>::value>::type \
> \
NARROWED_CONSTEXPR TOut narrowed(const TIn& inp, double tolerance = 0.001) noexcept(NARROWED_NOEXCEPT_PARAMETER) \
{ \
static_assert(!std::is_integral<TIn>::value, "Don't use narrowed() to convert float to int"); \
static_assert(!std::is_integral<TOut>::value, "Don't use narrowed() to convert int to float"); \
return ( \
narrowed_impl_details::abs_diff<TIn>(inp, static_cast<TIn>(static_cast<TOut>(inp))) \
< \
tolerance) ? \
static_cast<TOut>(inp) \
: NARROWED_THROW(CONSTEXPR_NARROWED_FAIL(TIn, TOut, "Failed to narrow to float")); \
}
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4297)
#elif defined(__clang__)
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wexceptions"
#elif defined(__GNUC__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wterminate"
#endif
#define NARROWED_CONSTEXPR constexpr
NARROWED_IMPL(true)
#undef NARROWED_CONSTEXPR
#ifdef _MSC_VER
#pragma warning(pop)
#elif defined(__clang__)
#pragma clang diagnostic pop
#elif defined(__GNUC__)
#pragma GCC diagnostic pop
#endif
#if NARROWED_HAS_EXCEPTIONS_ENABLED && defined(__cpp_noexcept_function_type) && (0 != __cpp_noexcept_function_type)
#define NARROWED_CONSTEXPR
NARROWED_IMPL(false)
#undef NARROWED_CONSTEXPR
#endif
#define NARROWED(TOut, expr) narrowed<TOut>(expr)
#undef NARROWED_HAS_EXCEPTIONS_ENABLED
#undef NARROWED_THROW
#undef CONSTEXPR_NARROWED_FAIL
#undef NARROWED_IMPL
#endif
#include <cstdint>
const auto ii = narrowed<int8_t>(128);
//auto ff1 = NARROWED(int8_t, 9999999.1);
constexpr auto ff2 = narrowed<float>(999999.0);
//auto fifi = narrowed<int>(99.0f);
int main()
{
double d = 1234098.432;
auto f = narrowed<float>(d);
auto i1 = narrowed<int8_t>(99);
// auto i2 = narrowed<bool>(1);
auto i3 = NARROWED(int8_t, 99);
(void)f;
(void)i1;
(void)i3;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment