ReflectedClass.h

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

@import "IObject.h"
@import "ReflectedType.h"
@import "ReflectedField.h"
@import "ReflectedFunction.h"
@import "ReflectedQualifier.h"
@import "ReflectionRegistries.h"

namespace ceda
{

///////////////////////////////////////////////////////////////////////////////////////////////////
// ReflectedClassMethod

$struct+ ReflectedClassMethod <<os>>
{
    $bool IsConst() const { return isConst; }
    $ConstStringZ GetName() const { return name; }
    $ssize_t GetNumArgs() const { return numArgs; }
    $const ReflectedArg& GetArg(ssize_t i) const { return args[i]; }
    void Write(xostream& os) const;

    ConstStringZ name;
    ReflectedReturnType returnType;
    const ReflectedArg* args;
    ssize_t numArgs;
    const octet_t* metaData;
    bool isConst;                 // Was the method declared with the 'const' qualfifier?
    FunctionPointer address;      // Address of associated global function
};

inline const ReflectedFunction& AsReflectedFunction(const ReflectedClassMethod& rcm)
{
    // Assumes that the ReflectedClassMethod begins with all the members defined in ReflectedFunction
    return reinterpret_cast<const ReflectedFunction&>(rcm);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
// ReflectedClass

/*
Model fields
------------

Model fields are recorded in the order in which they were added in the $schema.  This means
that dropped fields are mixed together with existing fields.  The index position is immutable and
used for generating operations on the model.
*/

typedef ptr<IObject> (__stdcall *CreateClassVariableFn)();

template <typename T, typename I>
inline ptr<IObject> __stdcall CreateIObjectClassInstance()
{
    return ptr<I>(new T());
}

template <typename T>
inline CreateClassVariableFn GetCreateClassVariableFn()
{
    if constexpr (implements_IObject<T>::value && std::is_default_constructible<T>::value)
        return (CreateClassVariableFn) (ptr<IObject> (*)()) CreateIObjectClassInstance<T, typename ClassTypeTraits<T>::firstIObjectInterface_t>;
    else
        return nullptr;
}

$enum+ EReflectedClassBits
{
    RCF_CLASS,      // 1 = class, 0 = struct
    RCF_PURE_MODEL,
    RCF_HAS_EMBEDDED_MODEL,
    RCF_REFERENCES_MODEL,
    RCF_ADT,
    RCF_ALLOW_PASS_BY_VALUE,
    RCF_ALLOW_RETURN_BY_VALUE,
    RCF_REPLICATE_ON_DEMAND
};

$enum+ class ETypeOperations
{
    HasDefaultConstructor,
    HasCopyConstructor,
    HasCopyAssignment,
    HasDestructor,
    HasSerialise,
    HasDeserialise,
    HasVisitObjects,
    HasVisitPrefs,
    HasCompareEqual,
    HasCompareLess,
    HasPrint,
    HasLowerBound,
    HasUpperBound
};

$struct+ ReflectedClass <<os>>
{
    bool operator<(const ReflectedClass& rhs) const { return this < &rhs; }
    bool operator==(const ReflectedClass& rhs) const { return this == &rhs; }

    $uint32 GetFlags() const { return flags; }
    $uint32 GetTypeOperationFlags() const { return ops.GetTypeOperationFlags(); }

    $bool IsStruct() const { return (flags & (1 << RCF_CLASS)) == 0; }
    $bool IsPureModel() const { return (flags & (1 << RCF_PURE_MODEL)) != 0; }
    $bool HasEmbeddedModel() const { return (flags & (1 << RCF_HAS_EMBEDDED_MODEL)) != 0; }
    $bool ReferencesModel() const { return (flags & (1 << RCF_REFERENCES_MODEL)) != 0; }
    $bool IsAdt() const { return (flags & (1 << RCF_ADT)) != 0; }
    $bool ReplicateOnDemand() const { return (flags & (1 << RCF_REPLICATE_ON_DEMAND)) != 0; }

    // Get keyword which is either "model", "adt", "struct" or "class"
    $ConstStringZ GetKeyWord() const;

    $ConstStringZ GetName() const { return name; }
    $ssize_t GetSize() const { return ops.size; }
    
    $ssize_t GetNumFields() const { return numFields; }
    $const ReflectedField& GetField(ssize_t i) const { return fields[i]; }

    void Write(xostream& os) const;

    // Equivalent to HasEmbeddedModel() || ReferencesModel()
    bool HasOrRefsModel() const { return modelFields != nullptr; }
    
    // May throw ReflectedClassNotFoundException
    const ReflectedClass& GetModel(ssize_t& offset) const
    {
        cxAssert(modelFields);
        if (numModelFields == 0)
        {
            offset = modelFields[0].offset;
            return FindReflectedClass(modelFields[0].name);
        }
        else
        {
            offset = 0;
            return *this;
        }
    }
    
    $ssize_t GetNumModelFields() const { return numModelFields; }
    $const ReflectedModelField& GetModelField(ssize_t i) const { return modelFields[i]; }

    $ssize_t GetNumMethods() const { return numMethods; }
    $const ReflectedClassMethod& GetMethod(ssize_t i) const { return methods[i]; }

    $ssize_t GetNumIndirectInterfaces() const { return numIndirectInterfaces; }
    $ssize_t GetNumDirectInterfaces() const { return numDirectInterfaces; }
    $ssize_t GetNumInterfaces() const { return numIndirectInterfaces + numDirectInterfaces; }
    $ConstStringZ GetInterfaceName(ssize_t i) const { return interfaces[i]; }
    
    $bool CanCreate() const { return createFn != nullptr; }
    $ptr<IObject> Create() const 
    { 
        if (createFn) 
        {
            ptr<IObject> p = createFn();
            RegisterGcObject(p);
            return p;
        }
        else return null; 
    }

    //////////////// State //////////////////

    uint32 flags;        // Meaning of bits defined by EReflectedClassBits
    @if (!CEDA_ENABLE_STRING_TABLES)
    {
        const octet_t* byteCode;
    }
    ConstStringZ name;
    const octet_t* metaData;

    const ReflectedField* fields;
    ssize_t numFields;
    const ReflectedModelField* modelFields;
    ssize_t numModelFields;
    const ReflectedDepField* depFields;
    ssize_t numDepFields;
    
    const ReflectedClassMethod* methods;  // actually pointers to global functions where the 
                                            // first argument is a pointer to the class or struct
    ssize_t numMethods;
    
    ConstStringZ const* stringTable;
    ConstStringZ const* interfaces;
    ssize_t numIndirectInterfaces;            // Number of indirectly inherited interfaces
    ssize_t numDirectInterfaces;              // Number of directly specified interfaces
    const IObject::FnTable* IObjectFnTable;
    
    // Create object on the heap.
    // Does not register the object with the current CSpace set in TLS
    CreateClassVariableFn createFn;       
    
    const TypeOps& ops;
};

/*
A ptr<T> is a subclass of AnyInterface which is a reflected class.
IsReflectedClass<ptr<T>>::value is true.
However we don't want a ptr<T> to be merely treated as a reflected class when we generate a ReflectedType,
we have more specialised support for reflection of a ptr<T>.
*/
template<typename T>
struct GetReflectedType_class<T, std::enable_if_t<!std::is_const<T>::value && !std::is_volatile<T>::value && IsReflectedClass<T>::value && !is_ptr<T>::value>>
{
    static inline ReflectedType get()
    {
        ReflectedType r;
        r.tag = EReflectedTypeTag::Class;
        r.Class = &GetReflectedClass<T>();
        return r;
    }
};
} // namespace ceda