ReflectedType.h

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

@import "cxObject.h"
@import "TypeOps.h"
#include "Ceda/cxUtils/xostream.h"
#include "Ceda/cxUtils/xstring.h"
#include "Ceda/cxUtils/BasicTypes.h"

namespace ceda
{
template <typename T> class openvariant;
template <typename K> class xset;
template <typename K, typename V> class xmap;

// Represents an array of count elements of type T
template<typename T>
struct Range
{
    T* data;
    ssize_t count;
};

// Represents count elements of type T with a given stride
template<typename T>
struct RangeWithStride
{
    T* data;
    ssize_t count;
    ssize_t stride;
};

struct ReflectedInterface;
struct ReflectedClass;
struct ReflectedVariant;
struct ReflectedEnum;
struct ReflectedFunctor;

struct ReflectedLeafType;
//struct ReflectedFunctionPointer;
struct ReflectedUnknown;
struct ReflectedVector;
struct ReflectedSet;
struct ReflectedDynArray;
struct ReflectedArray;
struct ReflectedMap;

struct ReflectedQualifier;
using ReflectedPointer = ReflectedQualifier;
using ReflectedInterfacePtr = ReflectedQualifier;
using ReflectedPref = ReflectedQualifier;
using ReflectedCref = ReflectedQualifier;
using ReflectedOpenVariant = ReflectedQualifier;

/////////// type qualifiers

template<typename T>
struct offsetable
{
    T value;    
};

template<typename T>
struct assignable
{
    T value;    
};

template<typename T>
struct movable
{
    T value;    
};

template<typename T> struct recursive_remove_mao_t { using type = T; };
template<typename T> struct recursive_remove_mao_t<offsetable<T>> { using type = typename recursive_remove_mao_t<T>::type; };
template<typename T> struct recursive_remove_mao_t<assignable<T>> { using type = typename recursive_remove_mao_t<T>::type; };
template<typename T> struct recursive_remove_mao_t<movable<T>> { using type = typename recursive_remove_mao_t<T>::type; };
template<typename T> struct recursive_remove_mao_t<T&> { using type = typename recursive_remove_mao_t<T>::type&; };
template<typename T> struct recursive_remove_mao_t<T*> { using type = typename recursive_remove_mao_t<T>::type*; };
template<typename T> struct recursive_remove_mao_t<T const> { using type = typename recursive_remove_mao_t<T>::type const; };
template<typename T> struct recursive_remove_mao_t<T volatile> { using type = typename recursive_remove_mao_t<T>::type volatile; };
template<typename T, size_t N> struct recursive_remove_mao_t<std::array<T,N>> { using type = std::array<typename recursive_remove_mao_t<T>::type,N>; };
template<typename T> struct recursive_remove_mao_t<DynArray<T>> { using type = DynArray<typename recursive_remove_mao_t<T>::type>; };
template<typename T> struct recursive_remove_mao_t<ptr<T>> { using type = ptr<typename recursive_remove_mao_t<T>::type>; };
template<typename T> struct recursive_remove_mao_t<pref<T>> { using type = pref<typename recursive_remove_mao_t<T>::type>; };
template<typename T> struct recursive_remove_mao_t<cref<T>> { using type = cref<typename recursive_remove_mao_t<T>::type>; };
template<typename T> struct recursive_remove_mao_t<openvariant<T>> { using type = openvariant<typename recursive_remove_mao_t<T>::type>; };
template<typename T> struct recursive_remove_mao_t<xvector<T>> { using type = xvector<typename recursive_remove_mao_t<T>::type>; };
template<typename T> struct recursive_remove_mao_t<xset<T>> { using type = xset<typename recursive_remove_mao_t<T>::type>; };
template<typename K, typename V> struct recursive_remove_mao_t<xmap<K,V>> { using type = xmap<typename recursive_remove_mao_t<K>::type,typename recursive_remove_mao_t<V>::type>; };

template <typename T>
inline const TypeOps& GetTypeOpsX()
{
    return GetTypeOps<typename recursive_remove_mao_t<T>::type>();
}

enum class ETypeQualifier
{
    Reference = 1,
    Volatile = 2,
    Const = 4,
    Assignable = 8,
    Movable = 16,
    Offsetable = 32
};

/*
    switch(rtype.tag)
    {
        case EReflectedTypeTag::Void :
            
        case EReflectedTypeTag::Leaf :
            switch(rtype.Leaf->tag)
            {
                case ELeafType::Bool :
                case ELeafType::Int8 :
                case ELeafType::Int16 :
                case ELeafType::Int32 :
                case ELeafType::Int64 :
                case ELeafType::Uint8 :
                case ELeafType::Uint16 :
                case ELeafType::Uint32 :
                case ELeafType::Uint64 :
                case ELeafType::Float32 :
                case ELeafType::Float64 :
                case ELeafType::Char8 :
                case ELeafType::Char16 :
                case ELeafType::String8 :
                case ELeafType::String16 :
            }

        case EReflectedTypeTag::Interface :
        case EReflectedTypeTag::Class :
        case EReflectedTypeTag::Variant :
        case EReflectedTypeTag::Enum :
        case EReflectedTypeTag::Functor :

        case EReflectedTypeTag::Pointer :
        case EReflectedTypeTag::InterfacePtr :
        case EReflectedTypeTag::Pref :
        case EReflectedTypeTag::Cref :
        case EReflectedTypeTag::OpenVariant :

        case EReflectedTypeTag::Unknown :
        case EReflectedTypeTag::Vector :
        case EReflectedTypeTag::Set :
        case EReflectedTypeTag::DynArray :
        case EReflectedTypeTag::Array :
        case EReflectedTypeTag::Map :
    }
*/
enum class EReflectedTypeTag
{
    Void,
    Leaf,

    Interface,
    Class,
    Variant,
    Enum,
    Functor,

    Pointer,
    InterfacePtr,
    Pref,
    Cref,
    OpenVariant,

    //FunctionPtr,
    Unknown,
    Vector,
    Set,
    DynArray,
    Array,
    Map
};

@api ConstStringZ ToString(EReflectedTypeTag t);

inline xostream& operator<<(xostream& os, EReflectedTypeTag t)
{
    os << ToString(t);
    return os;
}

struct ReflectedType
{
    // todo: pack these into single uint32?
    uint32 flags = 0;
    EReflectedTypeTag tag = EReflectedTypeTag::Void;

    union
    {
        const void* Void = nullptr;
        const ReflectedLeafType* Leaf;

        const ReflectedInterface* Interface;
        const ReflectedClass* Class;
        const ReflectedVariant* Variant;
        const ReflectedEnum* Enum;
        const ReflectedFunctor* Functor;

        const ReflectedQualifier* Pointer;
        const ReflectedQualifier* InterfacePtr;
        const ReflectedQualifier* Pref;
        const ReflectedQualifier* Cref;
        const ReflectedQualifier* OpenVariant;

        //const ReflectedFunctionPointer* FunctionPointer;
        const ReflectedUnknown* Unknown;
        const ReflectedVector* Vector;
        const ReflectedSet* Set;
        const ReflectedDynArray* DynArray;
        const ReflectedArray* Array;
        const ReflectedMap* Map;
    };

    const TypeOps* ops = nullptr;
};

inline ReflectedType AsReflectedType(const ReflectedClass& rc) { return { 0, EReflectedTypeTag::Class, {&rc} }; }
inline ReflectedType AsReflectedType(const ReflectedVariant& rv) { return { 0, EReflectedTypeTag::Variant, {&rv} }; }

@api void WriteReflectedType(xostream& os, const ReflectedType& r);

inline xostream& operator<<(xostream& os, const ReflectedType& r)
{
    WriteReflectedType(os, r);
    return os;
}

@api const TypeOps* CalcTypeOps(ReflectedType r);

inline const TypeOps* GetTypeOps(ReflectedType r) { return r.ops; }

@api void ConstructReflectedVariable(const ReflectedType& r, void* data);
@api void ConstructReflectedArrayVariable(const ReflectedType& r, ssize_t count, void* data);
@api void AssignReflectedVariable(const ReflectedType& r, void* lhs, const void* rhs);

inline bool IsReference(const ReflectedType& r)
{
    return (r.flags & (uint32)ETypeQualifier::Reference) != 0;
}
inline bool IsVolatile(const ReflectedType& r)
{
    return (r.flags & (uint32)ETypeQualifier::Volatile) != 0;
}
inline bool IsConst(const ReflectedType& r)
{
    return (r.flags & (uint32)ETypeQualifier::Const) != 0;
}
inline bool IsAssignable(const ReflectedType& r)
{
    return (r.flags & (uint32)ETypeQualifier::Assignable) != 0;
}
inline bool IsMovable(const ReflectedType& r)
{
    return (r.flags & (uint32)ETypeQualifier::Movable) != 0;
}
inline bool IsOffsetable(const ReflectedType& r)
{
    return (r.flags & (uint32)ETypeQualifier::Offsetable) != 0;
}

template <typename T, typename Enable=void> struct GetReflectedType_class 
{ 
};

// has_GetReflectedType<T>::value is true if and only if GetReflectedType_class<T>::get() exists
template <typename T> using has_GetReflectedType_t = decltype(GetReflectedType_class<T>::get());
template <typename T> using has_GetReflectedType = is_detected<has_GetReflectedType_t, T>;

template<typename T, std::enable_if_t<has_GetReflectedType<T>::value, int> = 0> 
inline ReflectedType GetReflectedType() 
{ 
    ReflectedType r = GetReflectedType_class<T>::get(); 
    r.ops = CalcTypeOps(r);
    return r;
}

template<>
struct GetReflectedType_class<void> 
{
    static inline ReflectedType get()
    {
        ReflectedType r;
        r.tag = EReflectedTypeTag::Void;
        r.Void = nullptr;
        return r;
    }
};

template<typename T>
struct GetReflectedType_class<offsetable<T>> 
{
    static inline ReflectedType get()
    {
        ReflectedType r = GetReflectedType<T>();
        r.flags |= (uint32) ETypeQualifier::Offsetable;
        return r;
    }
};

template<typename T>
struct GetReflectedType_class<assignable<T>> 
{
    static inline ReflectedType get()
    {
        ReflectedType r = GetReflectedType<T>();
        r.flags |= (uint32) ETypeQualifier::Assignable;
        return r;
    }
};

template<typename T>
struct GetReflectedType_class<movable<T>> 
{
    static inline ReflectedType get()
    {
        ReflectedType r = GetReflectedType<T>();
        r.flags |= (uint32) ETypeQualifier::Movable;
        return r;
    }
};

template<typename T>
struct GetReflectedType_class<T&> 
{
    static inline ReflectedType get()
    {
        ReflectedType r = GetReflectedType<T>();
        r.flags |= (uint32) ETypeQualifier::Reference;
        return r;
    }
};

template<typename T>
struct GetReflectedType_class<const T> 
{
    static inline ReflectedType get()
    {
        ReflectedType r = GetReflectedType<T>();
        r.flags |= (uint32) ETypeQualifier::Const;
        return r;
    }
};

template<typename T>
struct GetReflectedType_class<volatile T, std::enable_if_t<!std::is_const_v<T>>> 
{
    static inline ReflectedType get()
    {
        ReflectedType r = GetReflectedType<T>();
        r.flags |= (uint32) ETypeQualifier::Volatile;
        return r;
    }
};

// Test whether types r1 and r2 are compatible, i.e. that it would be reasonable to
// reinterpret a variable of type r1 as a variable of type r2.
// The following differences are ignored
//     1.   All metadata
//     2.   const or volatile qualifiers
//     3.   typedefs
//     4.   signed/unsigned differences
// Arrays must have the same size.
// A bool is regarded as incompatible to an integer type.
@api bool IsEquivVariableTypes(const ReflectedType& r1, const ReflectedType& r2);

} // namespace ceda