409 lines
11 KiB
C++
409 lines
11 KiB
C++
//+-------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
//
|
|
// Copyright (C) Microsoft Corporation, 1997 - 1997
|
|
//
|
|
// File: glnk.h
|
|
//
|
|
//--------------------------------------------------------------------------
|
|
|
|
//
|
|
// GLNK.H
|
|
//
|
|
// This file defines base classes for smart linked lists.
|
|
//
|
|
|
|
#if !defined(_GLNK_H_)
|
|
#define _GLNK_H_
|
|
|
|
#include "basics.h"
|
|
|
|
// Disable "warning C4355: 'this' : used in base member initializer list"
|
|
#pragma warning ( disable : 4355 )
|
|
|
|
// Disable warning about using 'bool'
|
|
#pragma warning ( disable : 4237 )
|
|
|
|
// Forward declarations
|
|
class GLNK; // Just a linked list anchor
|
|
class GELEM; // Base class for trees or dags
|
|
class GLNKEL; // A linked element
|
|
class GLNKCHN ; // An imbeddable element
|
|
class GELEMLNK; // A containerizable element
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// class GLNKBASE: just a pair of pointers. Used as a basis for
|
|
// linked lists and bidirectional pointers.
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
class GLNKBASE // A linked list anchor
|
|
{
|
|
protected:
|
|
GLNKBASE * _plnkNext ; // The next link in the chain
|
|
GLNKBASE * _plnkPrev ; // The previous link in the chain
|
|
public:
|
|
|
|
GLNKBASE ()
|
|
: _plnkNext(NULL),
|
|
_plnkPrev(NULL)
|
|
{}
|
|
~ GLNKBASE () {};
|
|
|
|
GLNKBASE * & PlnkNext () { return _plnkNext ; }
|
|
GLNKBASE * & PlnkPrev () { return _plnkPrev ; }
|
|
|
|
private:
|
|
// Block compiler from generating invalid functions
|
|
HIDE_UNSAFE(GLNKBASE);
|
|
};
|
|
|
|
// Template to generate a pair of pointers to given subclass
|
|
template<class L>
|
|
class XLBASE
|
|
{
|
|
protected:
|
|
L * _plNext ;
|
|
L * _plPrev ;
|
|
public:
|
|
XLBASE ()
|
|
: _plNext(NULL),
|
|
_plPrev(NULL)
|
|
{}
|
|
L * & PlnkNext () { return _plNext ; }
|
|
L * & PlnkPrev () { return _plPrev ; }
|
|
const L * PlnkNext () const { return _plNext ; }
|
|
const L * PlnkPrev () const { return _plPrev ; }
|
|
private:
|
|
XLBASE(const XLBASE &);
|
|
XLBASE & operator == (const XLBASE &);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// template XLSS: simple alias template using "source" and "sink"
|
|
// terminology.
|
|
////////////////////////////////////////////////////////////////////
|
|
template<class L>
|
|
class XLSS : public XLBASE<L>
|
|
{
|
|
public:
|
|
L * & PlnkSink () { return _plNext ; }
|
|
L * & PlnkSource () { return _plPrev ; }
|
|
L * PlnkSink () const { return _plNext ; }
|
|
L * PlnkSource () const { return _plPrev ; }
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// class GLNK: basic doubly-linked list. Linkage is always
|
|
// done directly between LNKs, not their containing objects
|
|
//
|
|
////////////////////////////////////////////////////////////////////
|
|
class GLNK : public GLNKBASE // A linked list anchor
|
|
{
|
|
protected:
|
|
void Empty ()
|
|
{
|
|
PlnkPrev() = this ;
|
|
PlnkNext() = this ;
|
|
}
|
|
|
|
GLNK ()
|
|
{
|
|
Empty() ;
|
|
}
|
|
|
|
~ GLNK ()
|
|
{
|
|
Unlink() ;
|
|
}
|
|
|
|
void Link ( GLNK * plnkNext )
|
|
{
|
|
Unlink() ;
|
|
PlnkPrev() = plnkNext->PlnkPrev() ;
|
|
PlnkNext() = plnkNext ;
|
|
plnkNext->PlnkPrev()->PlnkNext() = this ;
|
|
PlnkNext()->PlnkPrev() = this ;
|
|
}
|
|
|
|
void Unlink ()
|
|
{
|
|
PlnkNext()->PlnkPrev() = PlnkPrev() ;
|
|
PlnkPrev()->PlnkNext() = PlnkNext() ;
|
|
Empty() ;
|
|
}
|
|
|
|
// Const and non-const accessor to base pointer pair
|
|
GLNK * & PlnkNext () { return (GLNK *&) _plnkNext ; }
|
|
GLNK * & PlnkPrev () { return (GLNK *&) _plnkPrev ; }
|
|
const GLNK * PlnkNext () const { return (GLNK *) _plnkNext ; }
|
|
const GLNK * PlnkPrev () const { return (GLNK *) _plnkPrev ; }
|
|
|
|
public:
|
|
// Return count of elements on list, including self
|
|
long Count () const
|
|
{
|
|
long cItem = 1 ;
|
|
|
|
for ( GLNK * plnkNext = (CONST_CAST(GLNK *, this))->PlnkNext() ;
|
|
plnkNext != this ;
|
|
plnkNext = plnkNext->PlnkNext() )
|
|
{
|
|
cItem++ ;
|
|
}
|
|
return cItem ;
|
|
}
|
|
|
|
bool BIsEmpty () const { return PlnkNext() == this ; }
|
|
|
|
private:
|
|
// Block compiler from generating invalid functions
|
|
HIDE_UNSAFE(GLNK);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// class GELEM: Base class for linkable objects
|
|
////////////////////////////////////////////////////////////////////
|
|
const int g_IGelemTypeInc = 10000;
|
|
|
|
class GELEM
|
|
{
|
|
friend class GLNKCHN ;
|
|
public:
|
|
GELEM() {}
|
|
virtual ~ GELEM () {}
|
|
|
|
enum EGELMTYPE
|
|
{
|
|
EGELM_NONE = 0,
|
|
EGELM_NODE = EGELM_NONE + g_IGelemTypeInc,
|
|
EGELM_EDGE = EGELM_NODE + g_IGelemTypeInc,
|
|
EGELM_BRANCH = EGELM_EDGE + g_IGelemTypeInc,
|
|
EGELM_LEAF = EGELM_BRANCH + g_IGelemTypeInc,
|
|
EGELM_GRAPH = EGELM_LEAF + g_IGelemTypeInc,
|
|
EGELM_TREE = EGELM_GRAPH + g_IGelemTypeInc,
|
|
EGELM_CLIQUE = EGELM_GRAPH + g_IGelemTypeInc
|
|
};
|
|
virtual INT EType () const
|
|
{ return EGELM_NONE ; }
|
|
|
|
bool BIsEType ( INT egelmType )
|
|
{
|
|
INT etype = egelmType / g_IGelemTypeInc;
|
|
INT etypeThis = EType() / g_IGelemTypeInc;
|
|
return etype == etypeThis;
|
|
}
|
|
|
|
protected:
|
|
// Return the offset of the given pointer pair from the element.
|
|
int CbOffsetFrom ( const GLNKBASE * p ) const
|
|
// One could assert that the GLNKCHN really lies within object
|
|
// boundaries by using a virtualized "sizeof" accessor. Also,
|
|
// check mod-4 and other validity conditions.
|
|
{ return ((char*)p) - ((char*)this) ; }
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// class GLNKCHN:
|
|
// A GLNK which knows its offset within the containing object.
|
|
//
|
|
// To perform doubly-linked list operations, two pieces of information
|
|
// are necessary: the location of the pointer pair and the location
|
|
// of the containing object. By giving the GLNKCHN its offset from
|
|
// the start of the object during construction, it can perform all
|
|
// necessary operations, including automatically unlinking during
|
|
// destruction.
|
|
////////////////////////////////////////////////////////////////////
|
|
class GLNKCHN : public GLNK
|
|
{
|
|
private:
|
|
int _cbOffset ; // Number of bytes offset from
|
|
// start of owning structure
|
|
public:
|
|
GLNKCHN ( GELEM * pgelemOwner )
|
|
: _cbOffset(0)
|
|
{
|
|
_cbOffset = pgelemOwner->CbOffsetFrom( this );
|
|
}
|
|
~ GLNKCHN () {}
|
|
void Link ( GELEM * pgelemNext )
|
|
{
|
|
// Assuming that the GLNKCHN onto which we're linking is at the
|
|
// same offset in the given GLNKEL as it is in *this, link it.
|
|
GLNKCHN * plnkchn = PlnkchnPtr( pgelemNext );
|
|
GLNK::Link( plnkchn ) ;
|
|
}
|
|
|
|
void Unlink ()
|
|
{
|
|
GLNK::Unlink() ;
|
|
}
|
|
|
|
GELEM * PgelemNext ()
|
|
{
|
|
return BIsEmpty()
|
|
? NULL
|
|
: PlnkchnNext()->PgelemChainOwnerPtr() ;
|
|
}
|
|
|
|
GELEM * PgelemPrev ()
|
|
{
|
|
return BIsEmpty()
|
|
? NULL
|
|
: PlnkchnPrev()->PgelemChainOwnerPtr() ;
|
|
}
|
|
|
|
protected:
|
|
// Return a pointer to the base object. given a pointer to one of
|
|
// its GLNKCHN member objects.
|
|
GELEM * PgelemChainOwnerPtr () const
|
|
{ return (GELEM *) (((SZC) this) - _cbOffset) ; }
|
|
|
|
// Given a pointer to a GELEM presumed to be of the same base
|
|
// type as this object's container, return a pointer to the
|
|
// corresponding GLNKCHN in it.
|
|
GLNKCHN * PlnkchnPtr ( const GELEM * pgelem ) const
|
|
{
|
|
#ifdef _DEBUG
|
|
// Debug version does error checking
|
|
GLNKCHN * plnkchn = (GLNKCHN *) (((SZC) pgelem) + _cbOffset);
|
|
if ( _cbOffset != plnkchn->_cbOffset )
|
|
throw GMException(EC_LINK_OFFSET,"invalid GLNKCHN offset");
|
|
return plnkchn;
|
|
#else
|
|
return (GLNKCHN *) (((SZC) pgelem) + _cbOffset);
|
|
#endif
|
|
}
|
|
|
|
// Protected accessors to GLNK pointers cast to GLNKCHN pointers
|
|
GLNKCHN * PlnkchnNext () { return (GLNKCHN *) GLNK::PlnkNext() ; }
|
|
GLNKCHN * PlnkchnPrev () { return (GLNKCHN *) GLNK::PlnkPrev() ; }
|
|
private:
|
|
HIDE_UNSAFE(GLNKCHN);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// template XCHN:
|
|
// for creating types of chains given a containing object
|
|
// which is a subclass of GELEM
|
|
////////////////////////////////////////////////////////////////////
|
|
template<class XOBJ>
|
|
class XCHN : public GLNKCHN
|
|
{
|
|
public:
|
|
XCHN ( XOBJ * pgelemOwner )
|
|
: GLNKCHN(pgelemOwner)
|
|
{}
|
|
void Link ( XOBJ * pgelemNext )
|
|
{ GLNKCHN::Link(pgelemNext); }
|
|
void Unlink ()
|
|
{ GLNKCHN::Unlink(); }
|
|
XOBJ * PgelemNext ()
|
|
{ return (XOBJ *) GLNKCHN::PgelemNext(); }
|
|
XOBJ * PgelemPrev ()
|
|
{ return (XOBJ *) GLNKCHN::PgelemPrev(); }
|
|
XOBJ * PgelemThis ()
|
|
{ return PgelemChainOwnerPtr(); }
|
|
|
|
protected:
|
|
XOBJ * PgelemChainOwnerPtr () const
|
|
{ return (XOBJ *) GLNKCHN::PgelemChainOwnerPtr(); }
|
|
|
|
XCHN * PlnkchnPtr ( const XOBJ * pgelem ) const
|
|
{ return (XCHN *) GLNKCHN::PlnkchnPtr(pgelem); }
|
|
|
|
XCHN * PlnkchnNext () { return (XCHN *) GLNKCHN::PlnkchnNext() ; }
|
|
XCHN * PlnkchnPrev () { return (XCHN *) GLNKCHN::PlnkchnPrev() ; }
|
|
private:
|
|
XCHN(const XCHN &);
|
|
XCHN & operator == (const XCHN &);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Class GLNKEL:
|
|
// Simple base class for things managed as members of linked lists.
|
|
// One or more LNKCHNs or LNKs can be contained within subclass objects;
|
|
// it contains one "implicit" GLNKCHN for linking siblings in the
|
|
// implicit tree created by NTREE.
|
|
// Trees are based upon this type.
|
|
////////////////////////////////////////////////////////////////////
|
|
class GLNKEL : public GELEM
|
|
{
|
|
public:
|
|
// Internal class for chains (doubly-linked lists)
|
|
typedef XCHN<GLNKEL> CHN;
|
|
|
|
GLNKEL ()
|
|
: _chn( this ),
|
|
_iType(0),
|
|
_iMark(0)
|
|
{}
|
|
|
|
virtual ~ GLNKEL () {}
|
|
CHN & Chn ()
|
|
{ return _chn ; }
|
|
GLNKEL * PlnkelPrev ()
|
|
{ return Chn().PgelemPrev() ; }
|
|
GLNKEL * PlnkelNext ()
|
|
{ return Chn().PgelemNext() ; }
|
|
|
|
// Return the mutable (user-definable) object type
|
|
INT & IType () { return _iType; }
|
|
INT IType() const { return _iType; }
|
|
INT & IMark () { return _iMark; }
|
|
INT IMark () const { return _iMark; }
|
|
|
|
protected:
|
|
CHN _chn ; // Primary association chain
|
|
INT _iType; // User-definable type
|
|
INT _iMark; // Network walking mark
|
|
|
|
protected:
|
|
// Throw an exception when an invalid cloning operation occurs
|
|
void ThrowInvalidClone ( const GLNKEL & t );
|
|
|
|
HIDE_UNSAFE(GLNKEL);
|
|
};
|
|
|
|
////////////////////////////////////////////////////////////////////
|
|
// Class GELEMLNK:
|
|
// Base class for linkable objects in a collection, such as
|
|
// graph. Trees are NOT based upon this type, since trees
|
|
// are not forests (they cannot have associated but disjoint
|
|
// sets of objects).
|
|
////////////////////////////////////////////////////////////////////
|
|
class GELEMLNK : public GLNKEL
|
|
{
|
|
public:
|
|
// Internal class for chains (doubly-linked lists)
|
|
typedef XCHN<GELEMLNK> CHN;
|
|
|
|
GELEMLNK () {}
|
|
virtual ~ GELEMLNK () {}
|
|
|
|
// Locate an element (other than 'this') by type
|
|
GELEMLNK * PglnkFind ( EGELMTYPE eType, bool bExact = false )
|
|
{
|
|
for ( GELEMLNK * pg = this;
|
|
pg->ChnColl().PgelemThis() != this;
|
|
pg = pg->ChnColl().PgelemNext() )
|
|
{
|
|
if ( bExact ? pg->EType() == eType : pg->BIsEType(eType) )
|
|
return pg;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
// Element chain: all items belonging to this collection
|
|
CHN & ChnColl ()
|
|
{ return (CHN &) _chn ; }
|
|
|
|
private:
|
|
HIDE_UNSAFE(GELEMLNK);
|
|
};
|
|
|
|
#endif // !defined(_GLNK_H_)
|
|
|
|
// End of glnk.h
|
|
|