Coerce.h

// Coerce.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2022

#pragma once
#ifndef Ceda_cxUtils_Coerce_H
#define Ceda_cxUtils_Coerce_H

#include "cxUtils.h"
#include "Detect.h"
#include "MathExt.h"

namespace ceda
{
template<typename T1, typename T2> struct is_subtype_as_subset : std::false_type {};
template<typename T> struct is_subtype_as_subset<T,T> : std::true_type {};

template<> struct is_subtype_as_subset<int8,int16> : std::true_type {};
template<> struct is_subtype_as_subset<int8,int32> : std::true_type {};
template<> struct is_subtype_as_subset<int8,int64> : std::true_type {};
template<> struct is_subtype_as_subset<int16,int32> : std::true_type {};
template<> struct is_subtype_as_subset<int16,int64> : std::true_type {};
template<> struct is_subtype_as_subset<int32,int64> : std::true_type {};

template<> struct is_subtype_as_subset<uint8,uint16> : std::true_type {};
template<> struct is_subtype_as_subset<uint8,uint32> : std::true_type {};
template<> struct is_subtype_as_subset<uint8,uint64> : std::true_type {};
template<> struct is_subtype_as_subset<uint16,uint32> : std::true_type {};
template<> struct is_subtype_as_subset<uint16,uint64> : std::true_type {};
template<> struct is_subtype_as_subset<uint32,uint64> : std::true_type {};

template<> struct is_subtype_as_subset<int8,float32> : std::true_type {};
template<> struct is_subtype_as_subset<int16,float32> : std::true_type {};
template<> struct is_subtype_as_subset<uint8,float32> : std::true_type {};
template<> struct is_subtype_as_subset<uint16,float32> : std::true_type {};

template<> struct is_subtype_as_subset<int8,float64> : std::true_type {};
template<> struct is_subtype_as_subset<int16,float64> : std::true_type {};
template<> struct is_subtype_as_subset<int32,float64> : std::true_type {};
template<> struct is_subtype_as_subset<uint8,float64> : std::true_type {};
template<> struct is_subtype_as_subset<uint16,float64> : std::true_type {};
template<> struct is_subtype_as_subset<uint32,float64> : std::true_type {};
template<> struct is_subtype_as_subset<float32,float64> : std::true_type {};

/*
template<typename T1, typename T2> bool equal_values(const T1& t1, const T2& t2)
{
    // bool, char, char8_t char16_t, char32_t, wchar_t, short, int, long, long long
    if constexpr( std::is_integral<T1>::value && std::is_integral<T2>::value )
        return (long long) t1 == (long long) t2;
    else
        return t1 == t2;
}
*/

// assign a from b
template<typename To, typename From>
bool coerce(To& a, const From& b, bool allowExplicitConversions)
{
    if constexpr (is_subtype_as_subset<From,To>::value)
    {
        a = b;
        return true;
    }
    else if constexpr (has_implicit_cast<To,From>::value)
    {
        // C++ allows for implicit conversions which are lossy and produce warnings, so we use a static cast to silence the warnings
        a = static_cast<To>(b);

        if constexpr(std::is_arithmetic<To>::value && std::is_arithmetic<From>::value)
        {
            return allowExplicitConversions || IsNumericallyEqual(a,b);
        }
        else
        {
            return allowExplicitConversions || a==b;
        }
    }
    else if constexpr (has_static_cast_t<To,From>::value)
    {
        if (allowExplicitConversions)
        {
            a = static_cast<To>(b);
        }
    }
    return false;
}

} // namespace ceda

#endif // include guard