ReflectedArray.h

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

@import "IObject.h"
@import "Array.h"
@import "ReflectedType.h"

namespace ceda
{
/*
Defines a base class suitable for some ModelAttribute_XXX (call it E) which wraps one of 
the elements of an array of type std::array<T,N> wrapped with ModelAttribute_array<ArrayBase,E,T,N>
*/
template <typename ArrayBase, typename T, ssize_t N>
class ArrayElementBase
{
public:
    ArrayElementBase(ArrayBase* a, ssize_t i) : a(a),i(i) {}
    
    typedef typename ArrayBase::FinalClass FinalClass;
    T& model() { return a->model()[i]; }
    FieldPath path() const { return a->path() + i; }
    FinalClass* self() { return a->self(); }
private:
    ArrayBase* a;    // Can't be by reference because this element class must be copiable
    ssize_t i;       // Index position
};

// Wraps std::array<T,N> where each element of type T is wrapped by E.  i.e. E is some
// ModelAttribute_xxx parameterised on base ArrayElementBase<Base,T,N>
template <typename E, class Base, typename T, ssize_t N>
struct ModelAttribute_array : public Base
{
    CEDA_MODEL_ATTRIBUTE_BOILERPLATE(`std::array<T,N>`, ModelAttribute_array)

    static ssize_t size() { return N; }

    E operator[](ceda::ssize_t i)
    {
        return (E&) ArrayElementBase<Base,T,N>(this,i);
    }
    
    // Element is returned by const value - still ends up prohibiting non-const method
    // calls on Element!
    const E operator[](ceda::ssize_t i) const
    {
        return const_cast<ModelAttribute_array*>(this)->operator[](i);
    }
};

@if (false)
{
    // Example
    union
    {
        // xvector<char8> s
        struct attribute_s
        {
            typedef FinalClass FinalClass;
            FinalClass* self() { return ATTRIB_CAST(FinalClass,s); }
            ceda::xvector<ceda::char8>& model() { return self()->_model_.s; }
            static void path(ceda::FieldPath& _path) { octet_t* p=_path.InitLocalBuffer(1); p[0]=0; }
            static ceda::FieldPath path() { ceda::FieldPath _path; path(_path); return _path; }
        };
        ModelAttribute_vector<attribute_s, ceda::char8> s;
        
        // int32 A[10]
        struct attribute_A
        {
            typedef FinalClass FinalClass;
            FinalClass* self() { return ATTRIB_CAST(FinalClass,A); }
            std::array<ceda::int32,10>& model() { return self()->_model_.A; }
            static void path(ceda::FieldPath& _path) { octet_t* p=_path.InitLocalBuffer(1); p[0]=1; }
            static ceda::FieldPath path() { ceda::FieldPath _path; path(_path); return _path; }
        };
        ModelAttribute_array<
            ModelAttribute_assign<ArrayElementBase<attribute_A,ceda::int32,10>,ceda::int32>,
            attribute_A, 
            ceda::int32,
            10> A;
            
        // int32 B[10][5]
        struct attribute_B
        {
            typedef FinalClass FinalClass;
            FinalClass* self() { return ATTRIB_CAST(FinalClass,B); }
            std::array<std::array<ceda::int32,10>,5>& model() { return self()->_model_.B; }
            static void path(ceda::FieldPath& _path) { octet_t* p=_path.InitLocalBuffer(1); p[0]=2; }
            static ceda::FieldPath path() { ceda::FieldPath _path; path(_path); return _path; }
        };
        ModelAttribute_array<
            ModelAttribute_array<
                ModelAttribute_assign<ArrayElementBase<ArrayElementBase<attribute_B,std::array<ceda::int32,10>,5>,ceda::int32,10>,ceda::int32>,
                ArrayElementBase<attribute_B,std::array<ceda::int32,10>,5>, 
                ceda::int32,
                10>,
            attribute_B, 
            std::array<ceda::int32,10>,
            5> B;
    };
}

struct ReflectedArray
{
    inline void* GetArrayElement(void* data, ssize_t index) const
    {
        cxAssert(data);
        cxAssert(count > 0);
        cxAssert(0 <= index && index < count);
        const TypeOps* elementOps = GetTypeOps(element);
        cxAssert(elementOps);
        cxAssert(elementOps->size > 0);
        return (octet_t*) data + index * elementOps->size;
    }
    inline const void* GetArrayElement(const void* data, ssize_t index) const
    {
        return GetArrayElement(const_cast<void*>(data), index);
    }

    const TypeOps& ops;
    ReflectedType element;
    ssize_t count;
};

@api xostream& operator<<(xostream& os, const ReflectedArray& r);

template<typename T> struct is_array { constexpr static bool value = false; };
template<typename T, size_t N> struct is_array<std::array<T,N>> { constexpr static bool value = true; };

template<typename T, size_t N>
inline const ReflectedArray& GetReflectedArray()
{
    static ReflectedArray s =
    {
        GetTypeOpsX<std::array<T,N>>(),
        GetReflectedType<T>(),
        N
    };
    return s;
}

template<typename T, size_t N>
struct GetReflectedType_class<std::array<T,N>> 
{
    static inline ReflectedType get()
    {
        ReflectedType r;
        r.tag = EReflectedTypeTag::Array;
        r.Array = &GetReflectedArray<T,N>();
        return r;
    }
};
} // namespace ceda