Improves numeric_cast compilation (#3995)

* Improves numeric_cast compilation:
before: 872s
after: 824s (5.5% reduction)

Signed-off-by: Esteban Papp <81431996+amznestebanpapp@users.noreply.github.com>
This commit is contained in:
Esteban Papp
2021-09-10 08:41:19 -07:00
committed by GitHub
parent ba331b29f7
commit 81749ac318
6 changed files with 106 additions and 89 deletions
@@ -8,14 +8,25 @@
#pragma once
// This is disabled by default because it puts in costly runtime checking of casted values.
// You can either change it here to enable it across the engine, or use push/pop_macro to enable per file/feature.
// Note that if using push/pop_macro, you may get some of the functions not inline and the definition coming from
// another compilation unit, in such case, you will have to push/pop_macro on that compilation unit as well.
// #define AZ_NUMERICCAST_ENABLED 1
#if !AZ_NUMERICCAST_ENABLED
#define aznumeric_cast static_cast
#else
#include <AzCore/Casting/numeric_cast_internal.h>
#include <AzCore/std/typetraits/is_arithmetic.h>
#include <AzCore/std/typetraits/is_class.h>
#include <AzCore/std/typetraits/is_enum.h>
#include <AzCore/std/typetraits/is_floating_point.h>
#include <AzCore/std/typetraits/is_integral.h>
#include <AzCore/std/typetraits/is_same.h>
#include <AzCore/std/typetraits/is_signed.h>
#include <AzCore/std/typetraits/is_unsigned.h>
#include <AzCore/std/typetraits/remove_cvref.h>
#include <AzCore/std/typetraits/underlying_type.h>
#include <AzCore/std/utils.h>
@@ -28,7 +39,7 @@
// enabled.
//
// Because we can't do partial function specialization, I'm using enable_if to chop up the implementation into one of these
// implementations. If none of these fit, then we will get a compile error because it is an unknown conversionr.
// implementations. If none of these fit, then we will get a compile error because it is an unknown conversion.
//
//--------------------------------------------
// TYPE <- TYPE DigitLoss
@@ -51,85 +62,7 @@
// (K) Floating Floating Y
*/
// This is disabled by default because it puts in costly runtime checking of casted values.
// You can either change it here to enable it across the engine, or use push/pop_macro to enable per file/feature.
// Note that if using push/pop_macro, you may get some of the functions not inline and the definition coming from
// another compilation unit, in such case, you will have to push/pop_macro on that compilation unit as well.
// #define AZ_NUMERICCAST_ENABLED 1
#if AZ_NUMERICCAST_ENABLED
#define AZ_NUMERIC_ASSERT(expr, ...) AZ_Assert(expr, __VA_ARGS__)
#else
#define AZ_NUMERIC_ASSERT(expr, ...) void(0)
#endif
#pragma push_macro("max")
#undef max
namespace NumericCastInternal
{
template <typename ToType, typename FromType>
inline constexpr typename AZStd::enable_if<
!AZStd::is_integral<FromType>::value || !AZStd::is_floating_point<ToType>::value
, bool> ::type UnderflowsToType(const FromType& value)
{
return (value < static_cast<FromType>(std::numeric_limits<ToType>::lowest()));
}
template <typename ToType, typename FromType>
inline constexpr typename AZStd::enable_if<
AZStd::is_integral<FromType>::value && AZStd::is_floating_point<ToType>::value
, bool> ::type UnderflowsToType(const FromType& value)
{
return (static_cast<ToType>(value) < std::numeric_limits<ToType>::lowest());
}
template <typename ToType, typename FromType>
inline constexpr typename AZStd::enable_if<
!AZStd::is_integral<FromType>::value || !AZStd::is_floating_point<ToType>::value
, bool> ::type OverflowsToType(const FromType& value)
{
return (value > static_cast<FromType>(std::numeric_limits<ToType>::max()));
}
template <typename ToType, typename FromType>
inline constexpr typename AZStd::enable_if<
AZStd::is_integral<FromType>::value && AZStd::is_floating_point<ToType>::value
, bool> ::type OverflowsToType(const FromType& value)
{
return (static_cast<ToType>(value) > std::numeric_limits<ToType>::max());
}
template <typename ToType, typename FromType>
inline constexpr typename AZStd::enable_if<
AZStd::is_integral<FromType>::value && AZStd::is_integral<ToType>::value
&& std::numeric_limits<FromType>::digits <= std::numeric_limits<ToType>::digits
&& AZStd::is_signed<FromType>::value && AZStd::is_unsigned<ToType>::value
, bool> ::type FitsInToType(const FromType& value)
{
return !NumericCastInternal::UnderflowsToType<ToType>(value);
}
template <typename ToType, typename FromType>
inline constexpr typename AZStd::enable_if<
AZStd::is_integral<FromType>::value && AZStd::is_integral<ToType>::value
&& (std::numeric_limits<FromType>::digits > std::numeric_limits<ToType>::digits)
&& AZStd::is_unsigned<FromType>::value
, bool> ::type FitsInToType(const FromType& value)
{
return !NumericCastInternal::OverflowsToType<ToType>(value);
}
template <typename ToType, typename FromType>
inline constexpr typename AZStd::enable_if<
(!AZStd::is_integral<FromType>::value || !AZStd::is_integral<ToType>::value)
|| ((std::numeric_limits<FromType>::digits <= std::numeric_limits<ToType>::digits) && (AZStd::is_unsigned<FromType>::value || AZStd::is_signed<ToType>::value))
|| ((std::numeric_limits<FromType>::digits > std::numeric_limits<ToType>::digits) && AZStd::is_signed<FromType>::value)
, bool> ::type FitsInToType(const FromType& value)
{
return !NumericCastInternal::OverflowsToType<ToType>(value) && !NumericCastInternal::UnderflowsToType<ToType>(value);
}
} // namespace AZ
// INTEGER -> INTEGER
// (A) Not losing digits or risking sign loss
@@ -276,8 +209,10 @@ inline constexpr auto aznumeric_cast(FromType&& value) ->
return static_cast<ToType>(value);
}
#endif
// This is a helper class that lets us induce the destination type of a numeric cast
// It should never be directly used by anything other than azlossy_caster.
// It should never be directly used by anything other than aznumeric_caster.
namespace AZ
{
template <typename FromType>
@@ -295,7 +230,7 @@ namespace AZ
FromType m_value;
};
}
} // namespace AZ
// This is the primary function we should use when doing numeric casting, since it induces the
// type we need to cast to from the code rather than requiring an explicit coupling in the source.
@@ -305,4 +240,3 @@ inline constexpr AZ::NumericCasted<FromType> aznumeric_caster(FromType value)
return AZ::NumericCasted<FromType>(value);
}
#pragma pop_macro("max")
@@ -0,0 +1,81 @@
/*
* Copyright (c) Contributors to the Open 3D Engine Project.
* For complete copyright and license terms please see the LICENSE at the root of this distribution.
*
* SPDX-License-Identifier: Apache-2.0 OR MIT
*
*/
#pragma once
#include <AzCore/std/typetraits/conditional.h>
#include <AzCore/std/typetraits/is_floating_point.h>
#include <AzCore/std/typetraits/is_integral.h>
#include <AzCore/std/typetraits/is_signed.h>
#include <AzCore/std/typetraits/is_unsigned.h>
#include <limits>
namespace NumericCastInternal
{
template<typename ToType, typename FromType>
inline constexpr typename AZStd::enable_if<!AZStd::is_integral<FromType>::value || !AZStd::is_floating_point<ToType>::value, bool>::type
UnderflowsToType(const FromType& value)
{
return (value < static_cast<FromType>(std::numeric_limits<ToType>::lowest()));
}
template<typename ToType, typename FromType>
inline constexpr typename AZStd::enable_if<AZStd::is_integral<FromType>::value && AZStd::is_floating_point<ToType>::value, bool>::type
UnderflowsToType(const FromType& value)
{
return (static_cast<ToType>(value) < std::numeric_limits<ToType>::lowest());
}
template<typename ToType, typename FromType>
inline constexpr typename AZStd::enable_if<!AZStd::is_integral<FromType>::value || !AZStd::is_floating_point<ToType>::value, bool>::type
OverflowsToType(const FromType& value)
{
return (value > static_cast<FromType>(std::numeric_limits<ToType>::max()));
}
template<typename ToType, typename FromType>
inline constexpr typename AZStd::enable_if<AZStd::is_integral<FromType>::value && AZStd::is_floating_point<ToType>::value, bool>::type
OverflowsToType(const FromType& value)
{
return (static_cast<ToType>(value) > std::numeric_limits<ToType>::max());
}
template<typename ToType, typename FromType>
inline constexpr typename AZStd::enable_if<
AZStd::is_integral<FromType>::value && AZStd::is_integral<ToType>::value &&
std::numeric_limits<FromType>::digits <= std::numeric_limits<ToType>::digits && AZStd::is_signed<FromType>::value &&
AZStd::is_unsigned<ToType>::value,
bool>::type
FitsInToType(const FromType& value)
{
return !NumericCastInternal::UnderflowsToType<ToType>(value);
}
template<typename ToType, typename FromType>
inline constexpr typename AZStd::enable_if<
AZStd::is_integral<FromType>::value && AZStd::is_integral<ToType>::value &&
(std::numeric_limits<FromType>::digits > std::numeric_limits<ToType>::digits) && AZStd::is_unsigned<FromType>::value,
bool>::type
FitsInToType(const FromType& value)
{
return !NumericCastInternal::OverflowsToType<ToType>(value);
}
template<typename ToType, typename FromType>
inline constexpr typename AZStd::enable_if<
(!AZStd::is_integral<FromType>::value || !AZStd::is_integral<ToType>::value) ||
((std::numeric_limits<FromType>::digits <= std::numeric_limits<ToType>::digits) &&
(AZStd::is_unsigned<FromType>::value || AZStd::is_signed<ToType>::value)) ||
((std::numeric_limits<FromType>::digits > std::numeric_limits<ToType>::digits) && AZStd::is_signed<FromType>::value),
bool>::type
FitsInToType(const FromType& value)
{
return !NumericCastInternal::OverflowsToType<ToType>(value) && !NumericCastInternal::UnderflowsToType<ToType>(value);
}
}
@@ -8,14 +8,14 @@
#pragma once
#include <AzCore/Casting/numeric_cast.h>
#include <AzCore/Casting/numeric_cast_internal.h>
#include <AzCore/Serialization/Json/JsonSerialization.h>
#include <AzCore/std/string/string_view.h>
#include <AzCore/std/string/osstring.h>
namespace AZ
{
//! A helper function to casts between numeric types, and consider the data which is being converted comse from user data.
//! A helper function to casts between numeric types, and consider the data which is being converted comes from user data.
//! If a conversion from FromType to ToType will not cause overflow or underflow, the result is stored in result, and the function returns Success
//! Otherwise, the target is left untouched.
template <typename ToType, typename FromType>
@@ -24,9 +24,9 @@ namespace AZ
{
using namespace JsonSerializationResult;
if (NumericCastInternal::FitsInToType<ToType>(value))
if (NumericCastInternal::template FitsInToType<ToType>(value))
{
result = aznumeric_cast<ToType>(value);
result = static_cast<ToType>(value);
return reporting("Successfully cast number.", ResultCode(Tasks::Convert, Outcomes::Success), path);
}
else
@@ -35,6 +35,7 @@ set(FILES
Asset/AssetInternal/WeakAsset.h
Casting/lossy_cast.h
Casting/numeric_cast.h
Casting/numeric_cast_internal.h
Component/Component.cpp
Component/Component.h
Component/ComponentApplication.cpp