template<typename _T1, typename _T2>
static constexpr bool value = std::is_same_v<
std::remove_const_t<_T1>, std::remove_const_t<_T2>
// Check if two types are equal. Ignores const.
template <typename _T1, typename _T2>
inline constexpr bool IsSame = Detail::IsSameType<_T1, _T2>::value;
static constexpr bool value = IsSame<_Ty, int16_t>
// Check if type is Numeric. Ignores const.
inline constexpr bool IsNumeric = Detail::IsNumericType<_Ty>::value;
// Supported numeric types
template <typename _Ty> concept Numeric = Type::IsNumeric<_Ty>;
// Valid Array value type (Supported numeric types + Variant)
template <typename _Ty> concept ArrayValue = Numeric<_Ty> || Type::IsSame<_Ty, Variant>;
template <ArrayValue _Ty> class Array;
template <class, template <class> class>
struct IsInstanceType : public std::false_type {};
template <class _T1, template <class> class _T2>
struct IsInstanceType<_T2<_T1>, _T2> : public std::true_type {};
static constexpr bool value = IsInstanceType<std::remove_const_t<_Ty>, Array>::value;
inline constexpr bool IsArray = Detail::IsArrayType<_Ty>::value;
template <typename _Ty> concept VariantValue = Numeric<_Ty> || Type::IsSame<_Ty, String> || Type::IsArray<_Ty>;
template <typename _Ty> concept VariantValueRaw = Numeric<_Ty> || Type::IsSame<_Ty, char16_t*> || Type::IsSame<_Ty, ArrayBody*>;
template <typename _Fr, typename _To> concept Castable = requires { static_cast<_To>(_Fr{}); };
// Type numeric identifiers
// https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-oaut/3fe7db9f-5803-4dc4-9d14-5425d3f5461f
// Enable bitmasking for Type::ID
inline constexpr ID operator&(ID rhs, ID lhs) { return (Type::ID)((uint16_t)(rhs) & (uint16_t)(lhs)); }
inline constexpr ID operator^(ID rhs, ID lhs) { return (Type::ID)((uint16_t)(rhs) ^ (uint16_t)(lhs)); }
inline constexpr ID operator|(ID rhs, ID lhs) { return (Type::ID)((uint16_t)(rhs) | (uint16_t)(lhs)); }
inline constexpr ID operator&=(ID& rhs, ID lhs) { rhs = rhs & lhs; return rhs; }
inline constexpr ID operator^=(ID& rhs, ID lhs) { rhs = rhs ^ lhs; return rhs; }
inline constexpr ID operator|=(ID& rhs, ID lhs) { rhs = rhs | lhs; return rhs; }
inline constexpr ID operator~(ID id) { return (Type::ID)(~(uint16_t)(id)); }
template <typename _Ty> consteval ID GetID()
if constexpr (IsSame<_Ty, short> || IsSame<_Ty, int16_t>)
if constexpr (IsSame<_Ty, int> || IsSame<_Ty, int32_t>)
if constexpr (IsSame<_Ty, long> || IsSame<_Ty, long long> || IsSame<_Ty, int64_t>)
if constexpr (IsSame<_Ty, float>)
if constexpr (IsSame<_Ty, double>)
if constexpr (IsSame<_Ty, String>)
if constexpr (IsSame<_Ty, Variant>)
if constexpr (IsArray<_Ty>)
return GetID<typename _Ty::ValueType>() | ID::Array;
constexpr bool IsNumericID(ID id)
return id == Type::ID::Int16
|| id == Type::ID::Double;
// Translates Excel date to Tuple containing Year, Month and Day
constexpr std::tuple<uint16_t, uint8_t, uint8_t> DeserializeDate(double date)
// I have no idea how this does what it does, but it works and it's fast.
// Credits: https://www.codeproject.com/Articles/2750/Excel-Serial-Date-to-Day-Month-Year-and-Vice-Versa
int l = date + 68569 + 2415019;
int n = int(( 4 * l ) / 146097);
l = l - int(( 146097 * n + 3 ) / 4);
int i = int(( 4000 * ( l + 1 ) ) / 1461001);
l = l - int(( 1461 * i ) / 4) + 31;
int j = int(( 80 * l ) / 2447);
uint8_t day = l - int(( 2447 * j ) / 80);
uint8_t month = j + 2 - ( 12 * l );
uint16_t year = 100 * ( n - 49 ) + i + l;
return {year, month, day};