Skip to content

Instantly share code, notes, and snippets.

@brokenprogrammer
Created April 19, 2025 18:25
Show Gist options
  • Save brokenprogrammer/c392ab0d12be5b0c00db74e5f56ac57c to your computer and use it in GitHub Desktop.
Save brokenprogrammer/c392ab0d12be5b0c00db74e5f56ac57c to your computer and use it in GitHub Desktop.
// 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