Interfaces.h

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

@import "cxObject.h"
@import "TypeTraits.h"
@import "TypeOps.h"

namespace ceda
{
    class xostream;
    struct IObject;
    struct ReflectedClass;

    ///////////////////////////////////////////////////////////////////////////////////////////////////
    /*
    Note that NullInterface, AnyInterface, AnyConstInterface each provide implicit conversions to
    either const void* or void*.  

    Therefore the following operators

        *   comparison == != < <= > >=
        *   boolean conversion, allowing for expressions like
    
                if (p) ...
                if (!p) ...

    are supported uniformly on

        T*                  (for any T)
        NullInterface
        AnyInterface
        AnyConstInterface
        ptr<T>              (for any T)
        ptr<const T>        (for any T)
    */

    ///////////////////////////////////////////////////////////////////////////////////////////////////
    // NullInterface

    struct NullInterface 
    {
        // Needed to allow default initialisation of an object of const type
        // See : http://stackoverflow.com/questions/4674332/declaring-a-const-instance-of-a-class
        NullInterface() {}

        operator const void*() const { return nullptr; }
    };
    const NullInterface null;

    ///////////////////////////////////////////////////////////////////////////////////////////////////
    // AnyInterface

    typedef void (*FunctionPointer)();

    $struct+ AnyInterface
    {
        AnyInterface() : m_self(nullptr), m_table(nullptr) {}
        AnyInterface(NullInterface) : m_self(nullptr), m_table(nullptr) {}
        AnyInterface(std::nullptr_t) : m_self(nullptr), m_table(nullptr) {}
        AnyInterface(void* self, const FunctionPointer* table) : m_self(self), m_table(table) {}
    
        // With implicit conversion to void* clients get
        //      1)  == != < <= > >=  over T* and ptr<T> for any T
        //      2)  boolean conversions.
        operator void*() const { return m_self; }
    
        $void* self() const { return m_self; }

        void* m_self;
        const FunctionPointer* m_table;
    };

    $struct+ AnyConstInterface
    {
        AnyConstInterface() : m_self(nullptr), m_table(nullptr) {}
        AnyConstInterface(AnyInterface v) : m_self(v.m_self), m_table(v.m_table) {}
        AnyConstInterface(NullInterface) : m_self(nullptr), m_table(nullptr) {}
        AnyConstInterface(std::nullptr_t) : m_self(nullptr), m_table(nullptr) {}
        AnyConstInterface(const void* self, const FunctionPointer* table) : m_self(self), m_table(table) {}
    
        // With implicit conversion to void* clients get
        //      1)  == != < <= > >=  over T* and ptr<T> for any T
        //      2)  boolean conversions.
        operator const void*() const { return m_self; }

        $const void* self() const { return m_self; }

        const void* m_self;
        const FunctionPointer* m_table;
    };

    ///////////////////////////////////////////////////////////////////////////////////////////////////
    // ptr<T>

    template <typename T>
    struct ptr : public AnyInterface
    {
        ptr() {}
        ptr(NullInterface) {}
        ptr(std::nullptr_t) {}
        ptr(void* self, const FunctionPointer* table) : AnyInterface(self,table) {}

        ~ptr() {} // becomes non-POD and thus works in python marshalling

        template <typename P>
        ptr(P* rhs)
        {
            // According to the standard, reinterpret_cast<> is not allowed to cast away const-ness.  This makes it 
            // useful for testing const-compatibility!
            (void) reinterpret_cast<T*>( (P*) 0 );

            ((T*)(this))->CoerceFrom(rhs);
        }

        template <typename B>
        ptr(const ptr<B>& rhs)
        {
            // According to the standard, reinterpret_cast<> is not allowed to cast away const-ness.  This makes it 
            // useful for testing const-compatibility!
            (void) reinterpret_cast<T*>( (B*) 0 );

            ((T*)(this))->AssignFrom(*rhs);
        }
    
        T& operator*() const { return (T&)(*this); }
        T* operator->() const { return (T*)(this); }
    };

    template <typename T>
    struct ptr<const T> : public AnyConstInterface
    {
        ptr() {}
        ptr(NullInterface) {}
        ptr(std::nullptr_t) {}
        ptr(const void* self, const FunctionPointer* table) : AnyConstInterface(self,table) {}
        ~ptr() {} // becomes non-POD and thus works in python marshalling

        template <typename P>
        ptr(P* rhs) { ((const T*)(this))->CoerceFrom(rhs); }

        template <typename B>
        ptr(const ptr<B>& rhs) { ((const T*)(this))->AssignFrom(*rhs); }
    
        const T& operator*() const { return (const T&)(*this); }
        const T* operator->() const { return (const T*)(this); }
    };

    template <typename T>
    ptr<T> const_interface_cast(ptr<const T> x)
    {
        return ptr<T>(const_cast<void*>(x.m_self), x.m_table);
    }

    @api void WriteIObjectPtr(xostream& os, ptr<const IObject> p);

    // Note that this assumes that ptr<T> is used with interface T that inherits from IObject
    template <typename T>
    xostream& operator<<(xostream& os, ptr<T> p)
    {
        if constexpr (is_superinterface_of<IObject,T>::value)
        {
            WriteIObjectPtr(os,p);
        }
        else
        {
            os << p.m_self;
        }
        return os;
    }

    template <typename T>
    ptr<T> make_ptr(void* self, const typename T::FnTable* table)
    {
        return ptr<T>(self, reinterpret_cast<const FunctionPointer*>(table));
    }
    template <typename T>
    ptr<const T> make_ptr(const void* self, const typename T::FnTable* table)
    {
        return ptr<const T>(self, reinterpret_cast<const FunctionPointer*>(table));
    }

} // namespace ceda