IObjectVisitor.h

// IObjectVisitor.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2006

@import "cxObject.h"
@import "IObject.h"

// todo: including all these in this fundamental ceda header file isn't great, 
// for example it leads to long compile times
#include <utility>
#include <vector>
#include <deque>
#include <list>
#include <set>
#include <map>

/*
Without a definition of operator<< on ptr<T> the code would compile and silently not visit ptrs!
This is because

1)  ptr<T> is a subclass of AnyInterface

        template<typename T>
        struct ptr : public AnyInterface
        {
            ...
        };

2)  AnyInterface supports implicit conversion to void*

        struct AnyInterface
        {
            operator void*() const { return m_self; }
            ...
        };

3)  Visiting a void* is a no-op

        inline IObjectVisitor& operator<<(IObjectVisitor& v, void const*) { return v; }

Even without implicit conversion of ptr<T> to void*, with operator bool() we have implict conversion from
ptr<T> to bool, and visiting a bool is a no-op.
*/

namespace ceda
{
template <typename T> class xvector;
template <typename T, ssize_t pageSize> class xdeque;

/*
There are two different visit functions.  One takes the ptr<IObject> by value and the other by 
reference.  The latter indicates an evictable IObject, and eviction requires that the ptr be
cleared by the GC.

TODO: We need a syntax for allowing the two types of visiting.  Eg

    v << x1 << x2;
    evict(v) << y1;     // Alter type of v in order to visit cache members.
*/
struct IObjectVisitor
{
    virtual void VisitObject(ptr<const IObject> object) = 0;
    virtual void VisitEvictableObject(ptr<const IObject>& object) = 0;
};

/*
For various types we implement overloads of a free function named VisitObjects with the following signature

    void VisitObjects(IObjectVisitor& v, T const& x)

This is only implemented when there is actually something to do.
We want TypeOps to only record a visit objects function if the data type has objects that need to be visited.
This is important for performance.

has_VisitObjects<T>::value indicates whether the VisitObjects free function is defined for type T
*/


// We use existence of the method GetIObjectSysState() as an indicator of whether the object implements the IObject interface
template <typename T> using GetIObjectSysState_method_t = decltype(std::declval<T const &>().GetIObjectSysState());
template <typename T> using has_GetIObjectSysState_method = is_detected<GetIObjectSysState_method_t, T>;

//////////// visit raw pointer

/*
template <typename T>
inline void VisitPointer(IObjectVisitor& v, const T* x)
{
    if constexpr(has_GetIObjectSysState_method<T>::value)
    {
        v.VisitObject(x);
    }
}
template<typename T, std::enable_if_t<std::is_pointer<T>::value, int> = 0>
inline void VisitObjects(IObjectVisitor& v, T const& x)
{
    VisitPointer(v,x);
}
*/
template <typename T, std::enable_if_t<has_GetIObjectSysState_method<T>::value, int> = 0>
inline void VisitObjects(IObjectVisitor& v, const T* x)
{
    v.VisitObject(x);
}

//////////// visit object having VisitObjects method

template <typename T> using VisitObjects_method_t = decltype(std::declval<T const &>().VisitObjects(std::declval<IObjectVisitor&>()));
template <typename T> using has_VisitObjects_method = is_detected<VisitObjects_method_t, T>;

template <typename T, std::enable_if_t<has_VisitObjects_method<T>::value, int> = 0>
inline void VisitObjects(IObjectVisitor& v, T const& x) 
{
    x.VisitObjects(v);
}


// operator<< is always defined, regardless of whether T has something to visit.
template<typename T>
inline IObjectVisitor& operator<<(IObjectVisitor& v, T const& x) 
{ 
    if constexpr(has_VisitObjects<T>::value)
    {
        VisitObjects(v,x);
    }
    return v; 
}

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

template <typename T>
inline void VisitObjects(IObjectVisitor& v, const ptr<T>& x)
{
    if constexpr(has_GetIObjectSysState_method<T>::value)
    {
        v.VisitObject(x);
    }
}




template <typename U, typename V, std::enable_if_t<has_VisitObjects<U>::value || has_VisitObjects<V>::value, int> = 0>
inline void VisitObjects(IObjectVisitor& v, const std::pair<U,V>& x)
{
    v << x.first << x.second;
}

template <typename T, std::enable_if_t<has_VisitObjects<T>::value, int> = 0>
inline void VisitObjects(IObjectVisitor& v, const DynArray<T>& x)
{
    for (int i=0 ; i < x.size() ; ++i)
    {
        v << x[i];
    }
}

template <typename T> 
inline void VisitCollection(IObjectVisitor& v, const T& c)
{
    for (auto& e : c)
        v << e;
}

@scope
{
    @def fn(T) =
    {
        inline void VisitObjects(IObjectVisitor& v, const T& x)
        {
            VisitCollection(v,x);
        }
    }

    template<typename T, std::enable_if_t<has_VisitObjects<T>::value, int> = 0>
    fn( `xvector<T>` )
    
    template <typename T, ssize_t pageSize, std::enable_if_t<has_VisitObjects<T>::value, int> = 0>
    fn( `xdeque<T,pageSize>` )
    
    template <typename T, size_t N, std::enable_if_t<has_VisitObjects<T>::value, int> = 0>
    fn( `std::array<T,N>` )
    
    template <typename T,typename A, std::enable_if_t<has_VisitObjects<T>::value, int> = 0> 
    fn( `std::vector<T,A>` )

    template <typename T,typename A, std::enable_if_t<has_VisitObjects<T>::value, int> = 0> 
    fn( `std::deque<T,A>` )

    template <typename T,typename A, std::enable_if_t<has_VisitObjects<T>::value, int> = 0> 
    fn( `std::list<T,A>` )

    template <typename K, typename P, typename A, std::enable_if_t<has_VisitObjects<K>::value, int> = 0> 
    fn( `std::set<K,P,A>` )

    template <typename K, typename P, typename A, std::enable_if_t<has_VisitObjects<K>::value, int> = 0> 
    fn( `std::multiset<K,P,A>` )

    template <typename K,typename T,typename P,typename A, std::enable_if_t<has_VisitObjects<K>::value || has_VisitObjects<T>::value, int> = 0>
    fn( `std::map<K,T,P,A>` ) 

    template <typename K,typename T,typename P,typename A, std::enable_if_t<has_VisitObjects<K>::value || has_VisitObjects<T>::value, int> = 0>
    fn( `std::multimap<K,T,P,A>` ) 
}

} // namespace ceda