PathNav.h
// PathNav.h
//
// Author David Barrett-Lennard
// (C)opyright Cedanet Pty Ltd 2007-2014
@import "cxObject.h"
@import "IObject.h"
@import "FieldPath.h"
@import "ReflectionByteCode.h"
#include "Ceda/cxUtils/Stream.h"
/*
Let a "component" of a variable refer to:
- a field (i.e. member) of a struct if the variable is a struct
- an element of an array if the variable is an array
- a "mapped value" of type V if the variable is a map<K,V>
A component of a variable is in turn a variable.
Consider a variable which is of some class which has been reflected. A PathNav allows for iterating
through a path, at each step we proceed into a component of the struct/array/map.
A path can also record a key into a set. However we treat that case specially because an element
of a set is not a variable, and cannot itself be the target of operations. For that reason while
navigating a path we consider that we have gone as far as we can when we have reached the set.
A path can be empty, in which case we end with rbc_ representing the class and data_ pointing at the
whole object.
The following loop can be used to navigate a path
PathNav pn(...);
while(!pn.AtEnd())
{
pn.Next();
}
void* addr = pn.GetFieldAddress();
ReflectionByteCode rbc = pn.GetFieldType();
todo
----
Currently PathNav requires the reflected class of the object to be registered, because the
constructor initialises the byte code
static octet_t bc[] = { FT_CLASS, 0x00, 0x00 };
and a string table entry with the fully qualified name of the class. This then needs to be
looked up in the class registry.
That's not very efficient given that we start knowing the reflected class.
if (!path.empty())
{
PathNav pn(...);
{
Element e = Process_FT_CLASS(rc)
}
while(!pn.AtEnd())
{
octet_t type = GetType();
if (type == FT_CLASS)
{
Element e = Process_FT_CLASS();
}
else if (type == FT_ARRAY)
{
Element e = Process_FT_ARRAY();
}
else if (type == FT_DYNARRAY)
{
ssize_t index = Process_FT_DYNARRAY();
}
else if (type == FT_MAP)
{
xvector<octet_t> key;
Process_FT_MAP(key);
}
else if (type == FT_SET)
{
Process_FT_SET();
}
}
}
*/
namespace ceda
{
///////////////////////////////////////////////////////////////////////////////////////////////////
// PathNav
enum EPathNavMode
{
// Purpose of path navigation is to get read access to the field
PNM_READ_ACCESS,
// Purpose of path navigation is to get write access to the field
PNM_WRITE_ACCESS,
};
struct @api PathNav
{
public:
// Prepare to iterate through a given path starting from a variable
// of a class defined by 'rc', located at address 'data'.
// If 'mode' is PNM_WRITE_ACCESS then allow map entries to be created
PathNav(const FieldPath& path, void* data, EPathNavMode mode);
bool AtEnd() const { return ar_ == ar_end_ || reachedSet_; }
struct Element
{
ssize_t size;
ssize_t index;
};
Element Process_FT_CLASS(const ReflectedClass& rc);
octet_t GetType();
Element Process_FT_CLASS()
{
const ReflectedClass* rc = rbc_.GetReflectedClass();
cxAssert(rc);
return Process_FT_CLASS(*rc);
}
Element Process_FT_ARRAY();
ssize_t Process_FT_DYNARRAY();
void Process_FT_MAP(xvector<octet_t>& key);
void Process_FT_SET() { reachedSet_ = true; }
///////////////////////////////////////////////////////////////////////////////////////////////
// State
// Archive used to to deserialise elements of the path
InputArchive ar_;
InputArchive ar_end_;
// If the purpose of the path navigation is to perform an operation on a set, then
// this flag indicates whether an insertion or deletion is to be performed.
EPathNavMode mode_;
bool reachedSet_;
ReflectionByteCode rbc_saved_; // Saved from rbc_ before the call to AdvanceToUnderlyingType2()
ReflectionByteCode rbc_;
octet_t* data_;
};
struct @api PathNavX
{
PathNavX(const FieldPath& path, void* data, EPathNavMode mode);
bool AtEnd() const { return ar_ == ar_end_ || reachedSet_; }
octet_t GetType();
struct ClassMember
{
ssize_t GetNumFields() const { return rcModel->numModelFields; }
const ReflectedModelField& GetReflectedField() const { return *rf; }
ssize_t fieldIndex;
const ReflectedClass* rcModel;
const ReflectedModelField* rf;
};
void Process_FT_CLASS(const ReflectedClass& rc, ClassMember& m);
void Process_FT_CLASS(ClassMember& m)
{
return Process_FT_CLASS(*rbc_.GetReflectedClass(),m);
}
struct ArrayMember
{
int32 arraySize; // -1 for dynamic array
ssize_t index;
ssize_t elementSize;
};
void Process_FT_ARRAY(ArrayMember& m);
void Process_FT_DYNARRAY(ArrayMember& m);
struct MapMember
{
const octet_t* keyStart;
const octet_t* keyEnd;
};
void Process_FT_MAP(MapMember& m);
// Archive used to to deserialise elements of the path
InputArchive ar_;
InputArchive ar_end_;
// If the purpose of the path navigation is to perform an operation on a set, then
// this flag indicates whether an insertion or deletion is to be performed.
EPathNavMode mode_;
bool reachedSet_;
ReflectionByteCode rbc_saved_; // Saved from rbc_ before the call to AdvanceToUnderlyingType2()
ReflectionByteCode rbc_;
octet_t* data_;
};
@api void WriteFieldName(xostream& os, ptr<const IObject> obj, const FieldPath& path);
struct ObjectFieldDescriptor
{
ObjectFieldDescriptor(ptr<const IObject> obj, const FieldPath& path) : obj(obj), path(path) {}
ptr<const IObject> obj;
const FieldPath& path;
};
inline xostream& operator<<(xostream& os, ObjectFieldDescriptor d)
{
WriteFieldName(os, d.obj, d.path);
return os;
}
} // namespace ceda