mirror of
https://github.com/ZDoom/gzdoom.git
synced 2024-12-15 23:21:46 +00:00
Added type conversion search routines
- PType::FindConversion() can find a path to convert one type to another. This is completely generic and can handle any number of conversion functions.
This commit is contained in:
parent
4bd5bf310b
commit
c7e817dfb9
2 changed files with 215 additions and 0 deletions
153
src/dobjtype.cpp
153
src/dobjtype.cpp
|
@ -213,6 +213,159 @@ PType::~PType()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// PType :: AddConversion
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
bool PType::AddConversion(PType *target, void (*convertconst)(ZCC_ExprConstant *, class FSharedStringArena &))
|
||||||
|
{
|
||||||
|
// Make sure a conversion hasn't already been registered
|
||||||
|
for (unsigned i = 0; i < Conversions.Size(); ++i)
|
||||||
|
{
|
||||||
|
if (Conversions[i].TargetType == target)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
Conversions.Push(Conversion(target, convertconst));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// PType :: FindConversion
|
||||||
|
//
|
||||||
|
// Returns <0 if there is no path to target. Otherwise, returns the distance
|
||||||
|
// to target and fills slots (if non-NULL) with the necessary conversions
|
||||||
|
// to get there. A result of 0 means this is the target.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
int PType::FindConversion(PType *target, const PType::Conversion **slots, int numslots)
|
||||||
|
{
|
||||||
|
if (this == target)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
// The queue is implemented as a ring buffer
|
||||||
|
VisitQueue queue;
|
||||||
|
VisitedNodeSet visited;
|
||||||
|
|
||||||
|
// Use a breadth-first search to find the shortest path to the target.
|
||||||
|
MarkPred(NULL, -1, -1);
|
||||||
|
queue.Push(this);
|
||||||
|
visited.Insert(this);
|
||||||
|
while (!queue.IsEmpty())
|
||||||
|
{
|
||||||
|
PType *t = queue.Pop();
|
||||||
|
if (t == target)
|
||||||
|
{ // found it
|
||||||
|
if (slots != NULL)
|
||||||
|
{
|
||||||
|
if (t->Distance >= numslots)
|
||||||
|
{ // Distance is too far for the output
|
||||||
|
return -2;
|
||||||
|
}
|
||||||
|
t->FillConversionPath(slots);
|
||||||
|
}
|
||||||
|
return t->Distance + 1;
|
||||||
|
}
|
||||||
|
for (unsigned i = 0; i < t->Conversions.Size(); ++i)
|
||||||
|
{
|
||||||
|
PType *succ = t->Conversions[i].TargetType;
|
||||||
|
if (!visited.Check(succ))
|
||||||
|
{
|
||||||
|
succ->MarkPred(t, i, Distance + 1);
|
||||||
|
visited.Insert(succ);
|
||||||
|
queue.Push(succ);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// PType :: FillConversionPath
|
||||||
|
//
|
||||||
|
// Traces backwards from the target type to the original type and fills in
|
||||||
|
// the conversions necessary to get between them. slots must point to an
|
||||||
|
// array large enough to contain the entire path.
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void PType::FillConversionPath(const PType::Conversion **slots)
|
||||||
|
{
|
||||||
|
for (PType *node = this; node->Distance >= 0; node = node->PredType)
|
||||||
|
{
|
||||||
|
assert(node->PredType != NULL);
|
||||||
|
slots[node->Distance] = &node->PredType->Conversions[node->PredConv];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// PType :: VisitQueue :: Push
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void PType::VisitQueue::Push(PType *type)
|
||||||
|
{
|
||||||
|
Queue[In] = type;
|
||||||
|
Advance(In);
|
||||||
|
assert(!IsEmpty() && "Queue overflowed");
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// PType :: VisitQueue :: Pop
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
PType *PType::VisitQueue::Pop()
|
||||||
|
{
|
||||||
|
if (IsEmpty())
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
PType *node = Queue[Out];
|
||||||
|
Advance(Out);
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// PType :: VisitedNodeSet :: Insert
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
void PType::VisitedNodeSet::Insert(PType *node)
|
||||||
|
{
|
||||||
|
assert(!Check(node) && "Node was already inserted");
|
||||||
|
size_t buck = Hash(node) & (countof(Buckets) - 1);
|
||||||
|
node->VisitNext = Buckets[buck];
|
||||||
|
Buckets[buck] = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==========================================================================
|
||||||
|
//
|
||||||
|
// PType :: VisitedNodeSet :: Check
|
||||||
|
//
|
||||||
|
//==========================================================================
|
||||||
|
|
||||||
|
bool PType::VisitedNodeSet::Check(const PType *node)
|
||||||
|
{
|
||||||
|
size_t buck = Hash(node) & (countof(Buckets) - 1);
|
||||||
|
for (const PType *probe = Buckets[buck]; probe != NULL; probe = probe->VisitNext)
|
||||||
|
{
|
||||||
|
if (probe == node)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
//==========================================================================
|
//==========================================================================
|
||||||
//
|
//
|
||||||
// PType :: SetValue
|
// PType :: SetValue
|
||||||
|
|
|
@ -113,6 +113,7 @@ private:
|
||||||
// Map * *
|
// Map * *
|
||||||
// Prototype *+ *+
|
// Prototype *+ *+
|
||||||
|
|
||||||
|
struct ZCC_ExprConstant;
|
||||||
class PClassType;
|
class PClassType;
|
||||||
class PType : public DObject
|
class PType : public DObject
|
||||||
{
|
{
|
||||||
|
@ -128,6 +129,15 @@ public:
|
||||||
typedef PClassType MetaClass;
|
typedef PClassType MetaClass;
|
||||||
MetaClass *GetClass() const;
|
MetaClass *GetClass() const;
|
||||||
|
|
||||||
|
struct Conversion
|
||||||
|
{
|
||||||
|
Conversion(PType *target, void (*convert)(ZCC_ExprConstant *, class FSharedStringArena &))
|
||||||
|
: TargetType(target), ConvertConstant(convert) {}
|
||||||
|
|
||||||
|
PType *TargetType;
|
||||||
|
void (*ConvertConstant)(ZCC_ExprConstant *val, class FSharedStringArena &strdump);
|
||||||
|
};
|
||||||
|
|
||||||
unsigned int Size; // this type's size
|
unsigned int Size; // this type's size
|
||||||
unsigned int Align; // this type's preferred alignment
|
unsigned int Align; // this type's preferred alignment
|
||||||
PType *HashNext; // next type in this type table
|
PType *HashNext; // next type in this type table
|
||||||
|
@ -136,6 +146,10 @@ public:
|
||||||
PType(unsigned int size, unsigned int align);
|
PType(unsigned int size, unsigned int align);
|
||||||
virtual ~PType();
|
virtual ~PType();
|
||||||
|
|
||||||
|
bool AddConversion(PType *target, void (*convertconst)(ZCC_ExprConstant *, class FSharedStringArena &));
|
||||||
|
|
||||||
|
int FindConversion(PType *target, const Conversion **slots, int numslots);
|
||||||
|
|
||||||
// Sets the value of a variable of this type at (addr)
|
// Sets the value of a variable of this type at (addr)
|
||||||
virtual void SetValue(void *addr, int val);
|
virtual void SetValue(void *addr, int val);
|
||||||
|
|
||||||
|
@ -163,6 +177,54 @@ public:
|
||||||
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
|
virtual void GetTypeIDs(intptr_t &id1, intptr_t &id2) const;
|
||||||
|
|
||||||
static void StaticInit();
|
static void StaticInit();
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Stuff for type conversion searches
|
||||||
|
class VisitQueue
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VisitQueue() : In(0), Out(0) {}
|
||||||
|
void Push(PType *type);
|
||||||
|
PType *Pop();
|
||||||
|
bool IsEmpty() { return In == Out; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
// This is a fixed-sized ring buffer.
|
||||||
|
PType *Queue[64];
|
||||||
|
int In, Out;
|
||||||
|
|
||||||
|
void Advance(int &ptr)
|
||||||
|
{
|
||||||
|
ptr = (ptr + 1) & (countof(Queue) - 1);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
class VisitedNodeSet
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VisitedNodeSet() { memset(Buckets, 0, sizeof(Buckets)); }
|
||||||
|
void Insert(PType *node);
|
||||||
|
bool Check(const PType *node);
|
||||||
|
|
||||||
|
private:
|
||||||
|
PType *Buckets[32];
|
||||||
|
|
||||||
|
size_t Hash(const PType *type) { return size_t(type) >> 4; }
|
||||||
|
};
|
||||||
|
|
||||||
|
TArray<Conversion> Conversions;
|
||||||
|
PType *PredType;
|
||||||
|
PType *VisitNext;
|
||||||
|
short PredConv;
|
||||||
|
short Distance;
|
||||||
|
|
||||||
|
void MarkPred(PType *pred, int conv, int dist)
|
||||||
|
{
|
||||||
|
PredType = pred;
|
||||||
|
PredConv = conv;
|
||||||
|
Distance = dist;
|
||||||
|
}
|
||||||
|
void FillConversionPath(const Conversion **slots);
|
||||||
};
|
};
|
||||||
|
|
||||||
// Not-really-a-type types --------------------------------------------------
|
// Not-really-a-type types --------------------------------------------------
|
||||||
|
|
Loading…
Reference in a new issue