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