ReflectionByteCode.h

// ReflectionByteCode.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2007

@import "IObject.h"
@import "Reflection.h"
@import "ReflectionByteCodeTypes.h"
#include <algorithm>
#include <utility>

namespace ceda
{

// Bits returned by ReflectionByteCode::AdvanceToUnderlyingType2()
// These are type qualifiers that don't affect the way values of that type are encoded
$enum+ class EByteCodeQualifier
{
    Volatile = 1,
    Const = 2,
    Assignable = 4,
    Movable = 8,
    Offsettable = 16,
};

inline bool bcqIsVolatile(uint32 q) { return (q & EByteCodeQualifier::Volatile) != 0; }
inline bool bcqIsConst(uint32 q) { return (q & EByteCodeQualifier::Const) != 0; }
inline bool bcqIsAssignable(uint32 q) { return (q & EByteCodeQualifier::Assignable) != 0; }
inline bool bcqIsMovable(uint32 q) { return (q & EByteCodeQualifier::Movable) != 0; }
inline bool bcqIsOffsettable(uint32 q) { return (q & EByteCodeQualifier::Offsettable) != 0; }

struct MetaData;

struct TypeQualifiers
{
    uint32 bcq;             // Qualifier bits as per EByteCodeQualifier
    xvector<MetaData> mdv;  // All metadata on the type in the order encounted (can be multiple because of typedefs)
};

///////////////////////////////////////////////////////////////////////////////////////////////////
// ReflectionByteCode

/*
A ReflectionByteCode consists of three pointers into read-only memory, typically static initialised
memory.  No ownership semantic is implied, so the ReflectionByteCode destructor does nothing, and
coping a ReflectionByteCode only involves copying the pointers so it is relatively cheap. To the 
extent that the underlying reflection information is recorded in static initialised memory, it is 
reasonable to regard a ReflectionByteCode as a value type.
*/

$struct+ ReflectionByteCode
{
    ReflectionByteCode() : byteCode(nullptr), stringTable(nullptr), mapOrSetFns(nullptr) {}

    ReflectionByteCode(const ReflectedInterface& r);
    ReflectionByteCode(const ReflectedClass& r);
    ReflectionByteCode(const ReflectedVariant& r);
    ReflectionByteCode(const ReflectedGlobalFunction& r);
    ReflectionByteCode(const ReflectedGlobalVariable& r);
    ReflectionByteCode(const ReflectedTypedef& r);
    ReflectionByteCode(const ReflectedEnum& r);
    ReflectionByteCode(const ReflectedFunctor& r);
    
    ReflectionByteCode(const octet_t* bc, ConstStringZ const* stringTable, const PtrMapOrSetFns* mapOrSetFns = nullptr) : 
        byteCode(bc), 
        stringTable(stringTable),
        mapOrSetFns(mapOrSetFns) {}
    ReflectionByteCode(const ReflectedModelField& f, ConstStringZ const* stringTable) :
        byteCode(f.byteCode),
        stringTable(stringTable),
        mapOrSetFns(f.mapOrSetFns) {}

    @if (CEDA_ENABLE_STRING_TABLES)
    {
        // The given stringTable should have a single entry which is the name of the typedef
        static ReflectionByteCode ForTypedef(ConstStringZ const* stringTable)
        {
            static const octet_t bc[] = { FT_TYPEDEF,0x00,0x00 };
            return ReflectionByteCode(bc,stringTable);
        }
        static ReflectionByteCode ForConstString8Z()
        {
            static ConstStringZ stringTable[] = { "ceda::ConstString8Z" };
            return ForTypedef(stringTable);
        }
        static ReflectionByteCode ForConstString16Z()
        {
            static ConstStringZ stringTable[] = { "ceda::ConstString16Z" };
            return ForTypedef(stringTable);
        }
    }
    @else
    {
        static ReflectionByteCode ForConstString8Z()
        {
            constexpr octet_t bc[] = "\x22" "ceda::ConstString8Z";
            static_assert(bc[0] == FT_TYPEDEF);
            return ReflectionByteCode(bc,nullptr);
        }
        static ReflectionByteCode ForConstString16Z()
        {
            constexpr octet_t bc[] = "\x22" "ceda::ConstString16Z";
            static_assert(bc[0] == FT_TYPEDEF);
            return ReflectionByteCode(bc,nullptr);
        }
    }

    // Can be used for simple byte codes such a FT_VOID, FT_BOOL, FT_INT32 etc.
    static ReflectionByteCode MakeLeaf(octet_t bc)
    {
        static const octet_t s[] =
        {
            FT_VOID,      
            FT_BOOL,      
            FT_INT8,      
            FT_INT16,     
            FT_INT32,     
            FT_INT64,     
            FT_INT128,    
            FT_UINT8,     
            FT_UINT16,    
            FT_UINT32,    
            FT_UINT64,    
            FT_UINT128,   
            FT_FLOAT32,   
            FT_FLOAT64,   
            FT_CHAR8,     
            FT_CHAR16,    
            FT_STRING8,   
            FT_STRING16   
        };
        cxAssert(bc <= FT_STRING16);
        cxAssert(bc == s[bc]);
        return ReflectionByteCode(&s[bc],nullptr);
    }
        
    $ReflectionByteCode Clone() { return *this; }

    /////////////////////////////////////////// Methods ///////////////////////////////////////////

    template<typename T>
    void ReadValue(T& value)
    {
        value = *reinterpret_cast<const T*>(byteCode);
        byteCode += sizeof(T);
    }
    void ReadValue(xstring& value)
    {
        value = ReadString();
    }
    
    $bool IsNull() const { return byteCode == nullptr; }

    $uint8 ReadType() { return *byteCode++; }
    $uint8 ReadByte() { return *byteCode++; }
    $bool ReadBool() { octet_t v; ReadValue(v); return v != 0; }
    $int16 ReadInt16() { int16 v; ReadValue(v); return v; }
    $int32 ReadInt32() { int32 v; ReadValue(v); return v; }
    $float64 ReadFloat64() { float64 v; ReadValue(v); return v; }
    @if (CEDA_ENABLE_STRING_TABLES)
    {
        $ConstStringZ ReadString() { return stringTable[ReadInt16()]; }
    }
    @else
    {
        $ConstStringZ ReadString() 
        { 
            ConstStringZ r = (const char*) byteCode;
            while(*byteCode) ++byteCode;
            ++byteCode;
            return r;
        }
    }
    
    ReflectionByteCode& operator++()           // Prefix
    {
        ++byteCode;
        return *this;
    }
    octet_t operator*() const { return *byteCode; }

    $ReflectionByteCode& increment()
    {
        return ++(*this);
    }

    $void ScanPastBool() { byteCode += sizeof(octet_t); }
    $void ScanPastInt16() { byteCode += sizeof(int16); }
    $void ScanPastInt32() { byteCode += sizeof(int32); }
    @if (CEDA_ENABLE_STRING_TABLES)
    {
        $void ScanPastString() { byteCode += sizeof(int16); }
    }
    @else
    {
        $void ScanPastString() 
        { 
            while(*byteCode) ++byteCode;
            ++byteCode;
        }
    }
    $void ScanPastFloat64() { byteCode += sizeof(float64); }

    // Returns nullptr if class not registered
    $const ReflectedInterface* GetReflectedInterface() const;
    $const ReflectedClass* GetReflectedClass() const;
    $const ReflectedVariant* GetReflectedVariant() const;
    $const ReflectedTypedef* GetReflectedTypedef() const;
    $const ReflectedEnum* GetReflectedEnum() const;
    $const ReflectedFunctor* GetReflectedFunctor() const;
    
    // Scan past one item of metadata
    $void ScanMetaDataItem();

    // Scan past the type
    $void ScanType();

    // Advance past all the metadata, const, volatile qualifiers and through the typedefs.
    $void AdvanceToUnderlyingType();
    
    // Advance past all the metadata, const, volatile qualifiers and through the typedefs.
    // Returns bit values as per EByteCodeQualifier.
    $uint32 AdvanceToUnderlyingType2();

    // Advance past all the metadata, const, volatile qualifiers and through the typedefs.
    // Returns bit values as per EByteCodeQualifier and the sequence of metadata
    TypeQualifiers AdvanceToUnderlyingTypeGettingMetadata();

    // Get the type after scanning past all the metadata, const, volatile qualifiers and through 
    // the typedefs.
    $uint8 GetUnderlyingType() const;

    // Does this type represent a POD (Plain Old Data)?
    $bool IsPOD() const;
    
    // Does this type have a const qualifier?
    $bool IsConst() const;
    
    // Retrieve the size of the type, if necessary scanning past initial meta data.
    $ssize_t GetTypeSize() const;

    // Retrieve the size of the type assuming AdvanceToUnderlyingType() has already been called
    $ssize_t GetTypeSizeOnUnderlyingType() const;
    
    void swap(ReflectionByteCode& rhs)
    {
        using std::swap;
        swap(byteCode, rhs.byteCode);
        swap(stringTable, rhs.stringTable);
        swap(mapOrSetFns, rhs.mapOrSetFns);
    }

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

    const octet_t* byteCode;
    ConstStringZ const* stringTable;
    const PtrMapOrSetFns* mapOrSetFns;
};

////////////////////////// Get byte code for types ///////////////////////////

// global variable

inline ReflectionByteCode GetByteCode(const ReflectedGlobalVariable& g)
{
    return ReflectionByteCode(g.byteCode, g.stringTable);
}

// typedef 

inline ReflectionByteCode GetByteCode(const ReflectedTypedef& t)
{
    return ReflectionByteCode(t.byteCode, t.stringTable);
}

// global function

inline ReflectionByteCode GetReturnTypeByteCode(const ReflectedGlobalFunction& f)
{
    return ReflectionByteCode(f.returnType.byteCode, f.stringTable);
}

inline ReflectionByteCode GetArgByteCode(const ReflectedGlobalFunction& f, const ReflectedArg& a)
{
    return ReflectionByteCode(a.byteCode, f.stringTable);
}

// functor

inline ReflectionByteCode GetReturnTypeByteCode(const ReflectedFunctor& f)
{
    return ReflectionByteCode(f.returnType.byteCode, f.stringTable);
}

inline ReflectionByteCode GetArgByteCode(const ReflectedFunctor& f, const ReflectedArg& a)
{
    return ReflectionByteCode(a.byteCode, f.stringTable);
}

///////// class (fields and methods)

// fields

inline ReflectionByteCode GetByteCode(const ReflectedClass& c, const ReflectedField& f)
{
    return ReflectionByteCode(f.byteCode, c.stringTable);
}

inline ReflectionByteCode GetByteCode(const ReflectedClass& c, const ReflectedModelField& f)
{
    return ReflectionByteCode(f.byteCode, c.stringTable, f.mapOrSetFns);
}

inline ReflectionByteCode GetByteCodeOfModelField(const ReflectedClass& c, ssize_t i)
{
    return GetByteCode(c, c.GetModelField(i));
}

// methods

inline ReflectionByteCode GetReturnTypeByteCode(const ReflectedClass& c, const ReflectedClassMethod& m)
{
    return ReflectionByteCode(m.returnType.byteCode, c.stringTable);
}

inline ReflectionByteCode GetArgByteCode(const ReflectedClass& c, const ReflectedArg& a)
{
    return ReflectionByteCode(a.byteCode, c.stringTable);
}

///////// variant

inline ReflectionByteCode GetByteCode(const ReflectedVariant& v, const ReflectedField& f)
{
    return ReflectionByteCode(f.byteCode, v.stringTable);
}

///////// interface (attributes and methods)

// attributes

inline ReflectionByteCode GetByteCode(const ReflectedInterface& i, const ReflectedAttribute& a)
{
    return ReflectionByteCode(a.byteCode, i.stringTable);
}

// methods

inline ReflectionByteCode GetReturnTypeByteCode(const ReflectedInterface& i, const ReflectedInterfaceMethod& m)
{
    return ReflectionByteCode(m.returnType.byteCode, i.stringTable);
}

inline ReflectionByteCode GetArgByteCode(const ReflectedInterface& i, const ReflectedArg& a)
{
    return ReflectionByteCode(a.byteCode, i.stringTable);
}

///////////////////////////////////////////////////////////////////////////////////////////////////
/*
todo: we currently don't support overloading of reflected functions, so it is necessary to
give them all unique names.
*/

$function+ inline ReflectionByteCode GetGlobalVariableByteCode(const ReflectedGlobalVariable& g)
{
    return GetByteCode(g);
}

$function+ inline ReflectionByteCode GetTypeDefByteCode(const ReflectedTypedef& t)
{
    return GetByteCode(t);
}

$function+ inline ReflectionByteCode GetGlobalFunctionReturnTypeByteCode(const ReflectedGlobalFunction& f)
{
    return GetReturnTypeByteCode(f);
}

$function+ inline ReflectionByteCode GetGlobalFunctionArgByteCode(const ReflectedGlobalFunction& f, const ReflectedArg& a)
{
    return GetArgByteCode(f,a);
}

$function+ inline ReflectionByteCode GetFunctorReturnTypeByteCode(const ReflectedFunctor& f)
{
    return GetReturnTypeByteCode(f);
}

$function+ inline ReflectionByteCode GetFunctorArgByteCode(const ReflectedFunctor& f, const ReflectedArg& a)
{
    return GetArgByteCode(f,a);
}

$function+ inline ReflectionByteCode GetFieldByteCode(const ReflectedClass& c, const ReflectedField& f)
{
    return GetByteCode(c,f);
}

$function+ inline ReflectionByteCode GetVariantFieldByteCode(const ReflectedVariant& v, const ReflectedField& f)
{
    return GetByteCode(v,f);
}

$function+ inline ReflectionByteCode GetModelFieldByteCode(const ReflectedClass& c, const ReflectedModelField& f)
{
    return GetByteCode(c,f);
}

$function+ inline ReflectionByteCode GetClassMethodReturnTypeByteCode(const ReflectedClass& c, const ReflectedClassMethod& m)
{
    return GetReturnTypeByteCode(c,m);
}

$function+ inline ReflectionByteCode GetClassMethodArgByteCode(const ReflectedClass& c, const ReflectedArg& a)
{
    return GetArgByteCode(c,a);
}

$function+ inline ReflectionByteCode GetInterfaceAttributeByteCode(const ReflectedInterface& i, const ReflectedAttribute& a)
{
    return GetByteCode(i,a);
}

$function+ inline ReflectionByteCode GetInterfaceMethodReturnTypeByteCode(const ReflectedInterface& i, const ReflectedInterfaceMethod& m)
{
    return GetReturnTypeByteCode(i,m);
}

$function+ inline ReflectionByteCode GetInterfaceMethodArgByteCode(const ReflectedInterface& i, const ReflectedArg& a)
{
    return GetArgByteCode(i,a);
}

///////////////////////////////////////////////////////////////////////////////////////////////////

/*
Retrieve the TypeOps for the given ReflectionByteCode

This works on the following

    -   The following basic types

            bool, int8, int16, int32, int64, uint8, uint16, uint32, uint64, float32, float64,
            char8, char16, string8, string16
            
    -   enum types (all enum types are treated like int32)
    
    -   Pointers (all pointers are treated like void*)
    
    -   ptr<T> (these are treated like AnyInterface)
    
    -   pref<T>, cref<T>
    
    -   reflected classes (using $class+, $struct+, $adt+, $model+), since it is possible to bind to
        the TypeOps using the ReflectedClass
        
    -   reflected variants, since it is possible to bind to the TypeOps using the ReflectedVariant
    
    -   Registered typeops (using $type+)
    
Otherwise this function returns nullptr.    
*/

@api const TypeOps* FindTypeOps(ReflectionByteCode rbc);

///////////////////////////////////////////////////////////////////////////////////////////////////

// Displays ReflectionByteCode as a type
@api xostream& operator<<(xostream& os, ReflectionByteCode rbc);

// Test whether variable types rbc1 and rbc2 are compatible, i.e. that it would be reasonable to
// reinterpret a variable of type rbc1 as a variable of type rbc2.
// 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(ReflectionByteCode rbc1, ReflectionByteCode rbc2);

///////////////////////////////////////////////////////////////////////////////////////////////////

/*
Metadata grammar:

    literal:  string_literal
              bool_lteral
              integer_literal
              float_literal

    functor:  functor_name |
              functor_name ( args )

    element:  literal | functor | list
    
    args:     element
              args, element

    list:     [ args ]

    metadata: list
*/

///////////////////////////////////////////////////////////////////////////////////////////////////
// MetaData

$struct+ MetaData : public ReflectionByteCode
{
    MetaData() : ReflectionByteCode() {}
    MetaData(const octet_t* bc, ConstStringZ const* stringTable) : ReflectionByteCode(bc,stringTable) {}
    MetaData(ReflectionByteCode rbc) : ReflectionByteCode(rbc) {}

    //$bool Empty() const { return !(byteCode != nullptr && *byteCode >= MDT_FLAGS && *byteCode != MDT_LIST_0); }
    $bool IsMoreMetaData() const
    { 
        return byteCode && (*byteCode >= MDT_FLAGS); 
    }

    $bool IsMetaDataList() const
    {
        return byteCode && (*byteCode >= MDT_LIST_0); 
    }

    $bool IsEmptyMetaDataList() const
    {
        return byteCode && (*byteCode == MDT_LIST_0);
    }

    $bool IsNonemptyMetaDataList() const
    { 
        return byteCode && (*byteCode > MDT_LIST_0); 
    }

    // Used for when existence of a functor with arity 0 is used to indicate a boolean meta-data 
    // value.  Return value is false if there was an error.
    $bool ExistsFunctor0(ConstStringZ name, bool& exists, xstring& errorReason) const;

    inline bool ExistsFunctor0(ConstStringZ name) const
    {
        xstring errorReason;
        bool exists;
        return ExistsFunctor0(name, exists, errorReason) && exists;
    }
        
    // Find the meta-data value for a functor of arity 1 with the given name
    $bool GetBool(ConstStringZ name, bool& value, xstring& errorReason) const;
    $bool GetInt(ConstStringZ name, int32& value, xstring& errorReason) const;
    $bool GetDouble(ConstStringZ name, float64& value, xstring& errorReason) const;
    $bool GetString(ConstStringZ name, xstring& value, xstring& errorReason) const;

    $xstring GetString2(ConstStringZ name) const;

    template<typename T>
    bool GetNamedValue(ConstStringZ name, T& value, octet_t type, xstring& errorReason) const;

    bool AssignReflectedEnumVariableFromMetaData(const char* name, const ReflectedEnum& re, int32& value, xstring& errorReason) const;
    void AssignReflectedClassVariableFromMetaData(xstring& name, const ReflectedClass& rc, void* data) const;
    void AssignReflectedVariantVariableFromMetaData(xstring& name, const ReflectedVariant& rv, void* data) const;
    void AssignReflectedArrayVariableFromMetaData(xstring& name, ReflectionByteCode rbc, ssize_t size, void* data) const;
    void AssignReflectedVariableFromMetaData(xstring& name, ReflectionByteCode rbc, void* data) const;

    template<typename T>
    bool GetEnum(ConstStringZ name, T& value, xstring& errorReason) const
    {
        return AssignReflectedEnumVariableFromMetaData(name, ceda::GetReflectedEnum<T>(), (int32&) value, errorReason);
    }

    template<typename T>
    bool tryget_(ConstStringZ name, T& value, xstring& errorReason) const
    {
        int32 v;
        bool success = GetInt(name, v, errorReason);
        if (success) 
        {
            value = (T) v;
            if (v != value) 
            {
                success = false;
                errorReason = "Metadata out of range";
            }
        }
        else
        {
            float64 v;
            success = GetDouble(name, v, errorReason);
            if (success) 
            {
                value = (T) v;
                if (v != value) 
                {
                    success = false;
                    errorReason = "Metadata out of range";
                }
            }
        }
        return success;
    }


    template<typename T>
    inline bool get(ConstStringZ name, T& value, xstring& errorReason) const
    {
        if constexpr(std::is_base_of_v<EnumBase<T>,T>)
        {
            return GetEnum(name, value, errorReason);
        }
        else if constexpr(std::is_same_v<T, bool>)
        {
            return GetBool(name, value, errorReason);
        }
        else if constexpr(std::is_integral_v<T>)
        {
            return tryget_(name, value, errorReason);
        }
        else if constexpr(std::is_floating_point_v<T>)
        {
            float64 d;
            bool success = GetDouble(name, d, errorReason);
            if (success) value = (T) d;
            return success;
        }
        else if constexpr(std::is_same_v<T, string8>)
        {
            return GetString(name, value, errorReason);
        }
        else if constexpr(std::is_same_v<T, string16>)
        {
            string8 v;
            bool success = GetString(name, v, errorReason);
            if (success) value = AsString16(v);
            return success;
        }
        else if constexpr(IsReflectedClass<T>::value)
        {
            xstring nameStr = name;
            AssignReflectedClassVariableFromMetaData(nameStr, ceda::GetReflectedClass<T>(), &value);
            return true;
        }
        else
        {
            static_assert(dependent_false<T>::value, "Type unavailable for metadata");
        }
    }

    template<typename T>
    inline bool get(ConstStringZ name, T& value) const
    {
        xstring errorReason;
        return get(name, value, errorReason);
    }

    template<typename T>
    inline bool get(const xstring& name, T& value) const
    {
        return get(name.c_str(), value);
    }

    // Test whether functor of arity 0 exists with the given name, or a boolean functor of arity 1 which is true
    inline bool Test(ConstStringZ name) const
    {
        if (ExistsFunctor0(name)) return true;
        bool b;
        return get(name,b) && b;
    }
};

////////////////////////// Get Metadata ///////////////////////////

$function+ MetaData GetMetaData(const ReflectionByteCode& c);

inline MetaData GetMetaData(const ReflectedClass& c)
{
    return MetaData(c.metaData, c.stringTable);
}

inline MetaData GetMetaData(const ReflectedVariant& v)
{
    return MetaData(v.metaData, v.stringTable);
}

inline MetaData GetMetaData(const ReflectedInterface& i)
{
    return MetaData(i.metaData, i.stringTable);
}

inline MetaData GetMetaData(const ReflectedEnum& e)
{
    return MetaData(e.metaData, e.stringTable);
}

inline MetaData GetMetaData(const ReflectedFunctor& f)
{
    return MetaData(f.metaData, f.stringTable);
}

inline MetaData GetMetaData(const ReflectedGlobalFunction& g)
{
    return MetaData(g.metaData, g.stringTable);
}

// Get the metadata on the given class method
inline MetaData GetMetaData(const ReflectedClass& c, const ReflectedClassMethod& m)
{
    return MetaData(m.metaData, c.stringTable);
}

// Get the metadata on the given interface method
inline MetaData GetMetaData(const ReflectedInterface& i, const ReflectedInterfaceMethod& m)
{
    return MetaData(m.metaData, i.stringTable);
}

////////////////////////// Get display strings ///////////////////////////

@api xstring GetDisplayString(const ReflectedEnum& re, ssize_t tagIndex);
@api xstring GetDisplayString(const ReflectedClass& rc, const ReflectedModelField& rf);
@api xstring GetDisplayString(const ReflectedVariant& rv, const ReflectedField& rf);

///////////////////////////////////////////////////////////////////////////////////////////////////
// ReadMetaData

/*
Helper class to process the metatype byte code

It is assumed that top level functor names in the meta-data are never repeated.  A scan of the 
meta data simply returns the first match by name, and returns the arity.  The arity has information
value, and the functor can act like a named, variable length list.  Alternatively the caller may
impose constraints on the arity.

Another way of saying this is that we don't overload *logical meaning* on arity in the sense that a 
given functor name can only appear once in the meta-data.  However, we do support variable length
functors.

TODO: The front end could employ a validation file that describes what functors are supported and
any other restructions.  Most generally a grammar could be specified!
*/

$struct+ ReadMetaData : public ReflectionByteCode
{
    ReadMetaData() {}
    ReadMetaData(const octet_t* bc, ConstStringZ const* stringTable) : ReflectionByteCode(bc,stringTable) {}
    ReadMetaData(MetaData md) : ReflectionByteCode(md) {}
    ReadMetaData(ReflectionByteCode rbc) : ReflectionByteCode(rbc) {}
    
    //$bool Empty() const { return !(byteCode != nullptr && *byteCode >= MDT_FLAGS && *byteCode != MDT_LIST_0); }
    $bool IsMoreMetaData() const
    { 
        return byteCode && (*byteCode >= MDT_FLAGS); 
    }

    $bool IsMetaDataList() const
    {
        return byteCode && (*byteCode >= MDT_LIST_0); 
    }

    $bool IsEmptyMetaDataList() const
    {
        return byteCode && (*byteCode == MDT_LIST_0);
    }

    $bool IsNonemptyMetaDataList() const
    { 
        return byteCode && (*byteCode > MDT_LIST_0); 
    }
    
    static inline bool TypeIsFunctor(octet_t type) { return MDT_FUNCTOR_0 <= type && type < MDT_LIST_0; }
    static inline bool TypeIsList(octet_t type) { return MDT_LIST_0 <= type; }
    static inline bool IsValidFunctorArity(ssize_t arity) { return 0 <= arity && arity < MDT_LIST_0 - MDT_FUNCTOR_0; }

    void ScanType(octet_t type);
    void ScanType() { ScanType(ReadType()); }

    void ScanTypes(ssize_t count);

    // Find the functor with given name.  Returns true if found, and sets 'arity' to the arity of 
    // the functor.   Otherwise returns false.
    bool ScanForFunctor(ConstStringZ name, int32& arity);

    // Find the unary functor with given name, and argument type. Returns true if found, and byte 
    // code position is ready to read the argument value.
    // If not found writes a description of the problem to errorReason.  There are three possible
    // errors.
    // *    Functor with given name not found
    // *    Functor exists but arity is not one
    // *    Functor exists, arity is one but argument type is wrong
    bool ScanForUnaryFunctor(ConstStringZ name, octet_t type, xstring& errorReason);

    // Find the functor with given name and arity.  Returns true if found, and byte code position 
    // is ready to read the functor arguments (if arity > 0).
    //bool ScanForFunctor(ConstStringZ name, int32 arity);

    // Find the unary functor with given name, and argument type.  Returns true if found, and byte 
    // code position is ready to read the argument value.
    //bool ScanForUnaryFunctor(ConstStringZ name, EMetaDataType type);

    ConstStringZ ScanMetaDataForDisplayString();
};

template<typename T>
bool MetaData::GetNamedValue(ConstStringZ name, T& value, octet_t type, xstring& errorReason) const
{
    ReadMetaData rmd(*this);
    if (rmd.ScanForUnaryFunctor(name,type,errorReason))
    {
        rmd.ReadValue(value);
        return true;
    }
    return false;
}

///////////////////////////////////////////////////////////////////////////////////////////////////

// todo: doesn't belong here
@api const ReflectedInterface& GetInterface(const ReflectedClass& rc, ssize_t i);


///////////////////////////////////////////////////////////////////////////////////////////////////
// GetReflectionByteCode<T>

/*
Ideally GetReflectionByteCode<T> would be fully supported for any T without any special effort by
the application programmer.  Unfortunately it is difficult to recursively generate byte code 
statically, so this hasn't been done at present.

GetReflectionByteCode<T>() is implemented in terms of _GetReflected(const T*), which is heavily 
overloaded.  For example for a reflected class T, xcpp generates a function

    const ReflectedClass& _GetReflected(const T*)
    
Therefore GetReflectionByteCode<T>() returns the ReflectionByteCode for reflected classes because
the following constructor

    ReflectionByteCode::ReflectionByteCode(const ReflectedClass& r);
    
means there is an implicit conversion from const ReflectedClass& to ReflectionByteCode.

GetReflectionByteCode<T>() works for

    -   reflected classes, enums, functors, variants, interfaces
        (but not for reflected typedefs, global functions or global variables).
    
    -   Types declared with $type
    
    -   The following basic types:
            bool,
            int8,int16,int32,int64,
            uint8,uint16,uint32,uint64,
            float32,float64,
            char8,char16,string8,string16
            
    -   ptr<const T>, ptr<T> where T is a reflected interface
    
    -   openvariant<const T>, openvariant<T> where T is a reflected interface
    
    -   pref<const T>, pref<T> where T is a reflected interface
        cref<const T>, cref<T> where T is a reflected class
        (see overloads of _GetReflected() in pref.h in cxPersistStore)
*/

template <typename T>
inline ReflectionByteCode GetReflectionByteCode()
{
    /*
    We essentially want to invoke _GetReflected(T*).
    
    However we want to treat arrays specially - i.e. we want GetReflectionByteCode<T[N]>() to 
    instead invoke _GetReflected(std::array<T,N>*)
    
    Reason: consider the following $type declaration:

        $type int32[3];
        
    This actually implements an overload

        ReflectionByteCode _GetReflected(std::array<int32,3> const*);
    */
    return _GetReflected( (typename MapArrayType<T>::type*) 0 );
}

/*
Partial template specialisations of function templates are not permitted in C++.  This limits 
the ability to write specialisations of GetReflectionByteCode<T>().
For this reason we tend to use overloads of _GetReflected(T*) instead.
However, in the following cases we prefer full specialisations of GetReflectionByteCode<T>(). This
is because it handles const correctly, and void is handled in a much nicer way. 
We don't want an overload _GetReflected(const void*) because it captures cases where no 
overload is otherwise available. 
*/

@for ( (T,BC) in 
    [ 
        (void,FT_VOID),
        (bool,FT_BOOL), 
        (int8,FT_INT8),
        (int16,FT_INT16),
        (int32,FT_INT32),
        (int64,FT_INT64),
        (uint8,FT_UINT8),
        (uint16,FT_UINT16),
        (uint32,FT_UINT32),
        (uint64,FT_UINT64),
        (float32,FT_FLOAT32),
        (float64,FT_FLOAT64),
        (char8,FT_CHAR8),
        (char16,FT_CHAR16),
        (string8,FT_STRING8),
        (string16,FT_STRING16)
    ])
{
    template <>
    inline ReflectionByteCode GetReflectionByteCode<T>()
    {
        static octet_t bc[] = {BC};
        return ReflectionByteCode(bc,nullptr);
    }
    template <>
    inline ReflectionByteCode GetReflectionByteCode<const T>()
    {
        static octet_t bc[] = {FT_CONST, BC};
        return ReflectionByteCode(bc,nullptr);
    }
    
}

template <typename T>
ReflectionByteCode _GetReflected(const ptr<const T>*)
{
    @if (CEDA_ENABLE_STRING_TABLES)
    {
        static octet_t bc[] = { FT_INTERFACE_POINTER, FT_CONST, FT_INTERFACE, 0x00, 0x00 };
        return ReflectionByteCode(bc,&GetReflectedInterface<T>().name);
    }
    @else
    {
        static_assert(false);
    }
}

template <typename T>
ReflectionByteCode _GetReflected(const ptr<T>*)
{
    @if (CEDA_ENABLE_STRING_TABLES)
    {
        static octet_t bc[] = { FT_INTERFACE_POINTER, FT_INTERFACE, 0x00, 0x00 };
        return ReflectionByteCode(bc,&GetReflectedInterface<T>().name);
    }
    @else
    {
        static_assert(false);
    }
}

/*
_GetReflected(const T*) is implemented for the basic types, so GetReflectionByteCode<T>() works for these types.
*/

/*
@for ( (T,BC) in 
    [ 
        //(void,FT_VOID),       // _GetReflected(const void*) is dangerous because it captures cases where no
                                // overload is otherwise available. 
        (bool,FT_BOOL), 
        (int8,FT_INT8),
        (int16,FT_INT16),
        (int32,FT_INT32),
        (int64,FT_INT64),
        (uint8,FT_UINT8),
        (uint16,FT_UINT16),
        (uint32,FT_UINT32),
        (uint64,FT_UINT64),
        (float32,FT_FLOAT32),
        (float64,FT_FLOAT64),
        (char8,FT_CHAR8),
        (char16,FT_CHAR16),
        (string8,FT_STRING8),
        (string16,FT_STRING16)
    ])
{
    inline ReflectionByteCode _GetReflected(const T*)
    {
        static octet_t bc[] = {BC};
        return ReflectionByteCode(bc,nullptr);
    }
    
}
*/

///////////////////////////////////////////////////////////////////////////////////////////////////
// TypeToReflectionByteCode

@if (false)
{
    template <typename T>
    struct TypeToReflectionByteCode
    {
    };

    @for ( (T,BC) in 
        [ 
            (bool,FT_BOOL), 
            (int8,FT_INT8),
            (int16,FT_INT16),
            (int32,FT_INT32),
            (int64,FT_INT64),
            (uint8,FT_UINT8),
            (uint16,FT_UINT16),
            (uint32,FT_UINT32),
            (uint64,FT_UINT64),
            (float32,FT_FLOAT32),
            (float64,FT_FLOAT64),
            (char8,FT_CHAR8),
            (char16,FT_CHAR16),
            (string8,FT_STRING8),
            (string16,FT_STRING16)
        ])
    {
        template <> 
        struct TypeToReflectionByteCode<T>
        {
            static ReflectionByteCode Get()
            {
                static octet_t bc[] = {BC};
                return ReflectionByteCode(bc,nullptr);
            }
        };
    }

    template <typename T>
    struct TypeToReflectionByteCode< xvector<T> >
    {
        static ReflectionByteCode Get()
        {
            static octet_t bc[] = {BC};
            return ReflectionByteCode(bc,nullptr);
        }
    };
}

} // namespace ceda