Compare.h
// Compare.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2022
@import "Reflection.h"
#include "Ceda/cxUtils/Detect.h"
#include "Ceda/cxUtils/BasicTypes.h"
#include "Ceda/cxUtils/xostream.h"
#include "Ceda/cxUtils/xstring.h"
/*
We need both < and == comparisons
---------------------------------
If < is defined then all 6 comparison operations can be deduced:
a < b <--> a < b
a <= b <--> not (b < a)
a > b <--> b < a
a >= b <--> not (a < b)
a == b <--> not (a < b) and not (b < a)
a != b <--> (a < b) or (b < a)
Note however that == is not redundant because some types support == but not <
For this reason in the functions below we provide both Lt and Eq versions. This also means that
regardless of which of the 6 comparison operations we need to perform, it can be achieved with
just a single call (either to a Lt or an Eq function).
Equality testing
----------------
In python a bool is treated as an integer, and so can be compared with an integer.
Do we do likewise in C++?
Proposal:
bool is its own type and cannot be implicit compared to any other type
The numeric types are:
int8, int16, int32, int64, uint8, uint16, uint32, uint64, char8, char16, float32, float64
These can be compared to each other
string8, string16 can be compared - they are different encodings of the same thing (unicode text strings)
class : It is assumed each class is its own independent type
(or maybe we support coercions between classes)
TypeOps function to allow comparison with any other type
--------------------------------------------------------
ECompareResult compare(T& lhs, const ReflectedType& rhs_type, const void* rhs);
*/
namespace ceda
{
struct ReflectedType;
struct TypeOps;
enum class ECompareResult
{
None,
Less,
Equal,
Greater,
NotEqual
};
inline ECompareResult Reverse(ECompareResult e)
{
if (e == ECompareResult::Less)
return ECompareResult::Greater;
if (e == ECompareResult::Greater)
return ECompareResult::Less;
return e;
}
@api ConstStringZ ToString(ECompareResult r);
inline xostream& operator<<(xostream& os, ECompareResult r)
{
os << ToString(r);
return os;
}
@api ECompareResult Compare(const TypeOps& ops, const void* data1, const void* data2);
#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable : 4804 4805 4244)
#endif
template<typename T1, typename T2>
inline ECompareResult Compare(const T1& x, const T2& y)
{
if constexpr (has_compare_equal<T1,T2>::value)
{
// Otherwise if == exists then we can return equal or not-equal
if (x == y)
{
return ECompareResult::Equal;
}
if constexpr (has_compare_less<T1,T2>::value)
{
return (x < y) ? ECompareResult::Less : ECompareResult::Greater;
}
else
{
return ECompareResult::NotEqual;
}
}
else
{
// No comparisons at all
return ECompareResult::None;
}
}
#ifdef _MSC_VER
#pragma warning(pop)
#endif
/*
The flag checkReverse is used to avoid a infinite recursion given that to compare(T1,T2) we'd like to try compare(T2,T1)
*/
template<typename T>
ECompareResult RVCompare(const T& v, const ReflectedType& rtype, const void* data, bool checkReverse = true)
{
if (rtype.tag == EReflectedTypeTag::Leaf)
{
switch(rtype.Leaf->tag)
{
case ELeafType::Bool : return Compare(v, *reinterpret_cast<const bool*>(data));
case ELeafType::Int8 : return Compare(v, *reinterpret_cast<const int8*>(data));
case ELeafType::Int16 : return Compare(v, *reinterpret_cast<const int16*>(data));
case ELeafType::Int32 : return Compare(v, *reinterpret_cast<const int32*>(data));
case ELeafType::Int64 : return Compare(v, *reinterpret_cast<const int64*>(data));
case ELeafType::Uint8 : return Compare(v, *reinterpret_cast<const uint8*>(data));
case ELeafType::Uint16 : return Compare(v, *reinterpret_cast<const uint16*>(data));
case ELeafType::Uint32 : return Compare(v, *reinterpret_cast<const uint32*>(data));
case ELeafType::Uint64 : return Compare(v, *reinterpret_cast<const uint64*>(data));
case ELeafType::Float32 : return Compare(v, *reinterpret_cast<const float32*>(data));
case ELeafType::Float64 : return Compare(v, *reinterpret_cast<const float64*>(data));
case ELeafType::Char8 : return Compare(v, *reinterpret_cast<const char8*>(data));
case ELeafType::Char16 : return Compare(v, *reinterpret_cast<const char16*>(data));
case ELeafType::String8 : return Compare(v, *reinterpret_cast<const string8*>(data));
case ELeafType::String16 :return Compare(v, *reinterpret_cast<const string16*>(data));
default:
return ECompareResult::None;
}
}
if (const TypeOps* ops = GetTypeOps(rtype))
{
if (&GetTypeOps<T>() == ops)
{
return Compare(*ops, &v, data);
}
if constexpr( has_GetReflectedType<T>::value )
{
if (checkReverse && ops->rvCompareFn)
{
return Reverse(ops->rvCompareFn(data, GetReflectedType<T>(), &v, false));
}
}
}
return ECompareResult::None;
}
@api ECompareResult Compare(const ReflectedType& lhs_type, const void* lhs, const ReflectedType& rhs_type, const void* rhs);
} // namespace ceda