Created
April 19, 2025 18:25
-
-
Save brokenprogrammer/c392ab0d12be5b0c00db74e5f56ac57c to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is code that I wrote for the q2 angelscript project to help improve the API that the q2 code use to check and get types | |
// from the yyjson library. | |
// It is nothing ground breaking but it was interesting to write at the time. | |
template<typename TargetType, typename SourceType> | |
constexpr bool q2as_type_in_range(SourceType value) | |
{ | |
// Prevent bool as TargetType or SourceType to avoid edge cases | |
static_assert(!std::is_same_v<TargetType, bool>, "TargetType cannot be bool"); | |
static_assert(!std::is_same_v<SourceType, bool>, "SourceType cannot be bool"); | |
if constexpr (std::is_same_v<TargetType, SourceType>) | |
{ | |
return true; | |
} | |
constexpr bool is_target_integer = std::numeric_limits<TargetType>::is_integer; | |
constexpr bool is_source_integer = std::numeric_limits<SourceType>::is_integer; | |
constexpr bool is_target_signed = std::numeric_limits<TargetType>::is_signed; | |
constexpr bool is_source_signed = std::numeric_limits<SourceType>::is_signed; | |
constexpr TargetType max = std::numeric_limits<TargetType>::max(); | |
constexpr TargetType min = std::numeric_limits<TargetType>::lowest(); | |
// Target and Source are integers | |
if constexpr (is_target_integer && is_source_integer) | |
{ | |
if constexpr (is_target_signed && is_source_signed) | |
{ | |
return value <= max && value >= min; | |
} | |
if constexpr (!is_target_signed && !is_source_signed) | |
{ | |
return value <= max; | |
} | |
if constexpr (is_target_signed && !is_source_signed) | |
{ | |
return value <= static_cast<SourceType>(max); | |
} | |
if constexpr (!is_target_signed && is_source_signed) | |
{ | |
return value >= 0 && static_cast<uint64_t>(value) <= static_cast<uint64_t>(max); | |
} | |
} | |
// Both floating-point | |
if constexpr (!is_target_integer && !is_source_integer) | |
{ | |
if constexpr (std::is_same_v<TargetType, float>) | |
{ | |
if (std::isinf(value) || std::isnan(value)) | |
{ | |
return false; | |
} | |
float f_value = static_cast<float>(value); | |
// Check for loss of precision. | |
if (value != static_cast<double>(f_value)) | |
{ | |
return false; | |
} | |
} | |
return value <= max && value >= min; | |
} | |
// Integer to floating point. | |
if constexpr (!is_target_integer && is_source_integer) | |
{ | |
if constexpr (std::is_same_v<TargetType, float>) | |
{ | |
float f_value = static_cast<float>(value); | |
// Check for loss of precision. | |
if (value != static_cast<double>(f_value)) | |
{ | |
return false; | |
} | |
return f_value <= max && f_value >= min; | |
} | |
else | |
{ | |
// Check for loss of precision. | |
if (static_cast<SourceType>(static_cast<double>(value)) != value) | |
{ | |
return false; | |
} | |
double d_value = static_cast<double>(value); | |
return d_value <= max && d_value >= min; | |
} | |
} | |
// Floating point to integer | |
if constexpr (is_target_integer && !is_source_integer) | |
{ | |
if (std::isinf(value) || std::isnan(value)) | |
{ | |
return false; | |
} | |
// Don't allow decimals | |
if (std::trunc(value) != value) | |
{ | |
return false; | |
} | |
if constexpr (is_target_signed) | |
{ | |
double double_max = static_cast<double>(max); | |
double double_min = static_cast<double>(min); | |
return value <= double_max && value >= double_min; | |
} | |
return value >= 0 && value <= static_cast<double>(max); | |
} | |
// Should not hit this. | |
return false; | |
} | |
template<typename T> | |
bool q2as_type_can_be(yyjson_mut_val *val) | |
{ | |
if (yyjson_mut_get_tag(val) == (YYJSON_TYPE_NUM | YYJSON_SUBTYPE_UINT)) | |
{ | |
return q2as_type_in_range<T, uint64_t>(val->uni.u64); | |
} | |
else if (yyjson_mut_get_tag(val) == (YYJSON_TYPE_NUM | YYJSON_SUBTYPE_SINT)) | |
{ | |
return q2as_type_in_range<T, int64_t>(val->uni.i64); | |
} | |
else if (yyjson_mut_get_tag(val) == (YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL)) | |
{ | |
return q2as_type_in_range<T, double>(val->uni.f64); | |
} | |
return false; | |
} | |
template<typename T> | |
bool q2as_type_can_be(yyjson_val *val) | |
{ | |
if (yyjson_get_tag(val) == (YYJSON_TYPE_NUM | YYJSON_SUBTYPE_UINT)) | |
{ | |
return q2as_type_in_range<T, uint64_t>(val->uni.u64); | |
} | |
else if (yyjson_get_tag(val) == (YYJSON_TYPE_NUM | YYJSON_SUBTYPE_SINT)) | |
{ | |
return q2as_type_in_range<T, int64_t>(val->uni.i64); | |
} | |
else if (yyjson_get_tag(val) == (YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL)) | |
{ | |
return q2as_type_in_range<T, double>(val->uni.f64); | |
} | |
return false; | |
} | |
template<typename T> | |
T q2as_get_value(yyjson_val *val) | |
{ | |
if (yyjson_get_tag(val) == (YYJSON_TYPE_NUM | YYJSON_SUBTYPE_UINT)) | |
{ | |
return (T) (val->uni.u64); | |
} | |
else if (yyjson_get_tag(val) == (YYJSON_TYPE_NUM | YYJSON_SUBTYPE_SINT)) | |
{ | |
return (T) (val->uni.i64); | |
} | |
else if (yyjson_get_tag(val) == (YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL)) | |
{ | |
return (T) (val->uni.f64); | |
} | |
return 0; | |
} | |
template<typename T> | |
T q2as_get_value(yyjson_mut_val *val) | |
{ | |
if (yyjson_mut_get_tag(val) == (YYJSON_TYPE_NUM | YYJSON_SUBTYPE_UINT)) | |
{ | |
return (T) (val->uni.u64); | |
} | |
else if (yyjson_mut_get_tag(val) == (YYJSON_TYPE_NUM | YYJSON_SUBTYPE_SINT)) | |
{ | |
return (T) (val->uni.i64); | |
} | |
else if (yyjson_mut_get_tag(val) == (YYJSON_TYPE_NUM | YYJSON_SUBTYPE_REAL)) | |
{ | |
return (T) (val->uni.f64); | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment