//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 1997 // // File: gelem.h // //-------------------------------------------------------------------------- // // GELEM.H // #ifndef _GELEM_H_ #define _GELEM_H_ // Disable warning about using 'bool' #pragma warning ( disable : 4237 ) #include "glnk.h" #include "leakchk.h" class GNODE; class GEDGE; // Classes for the type-safe links in arcs to nodes and links in nodes // to arcs. class GEDGLNK : public XLSS {}; class GNODLNK : public XLSS {}; //////////////////////////////////////////////////////////////////// // class GNODE: Base class for node in a graph or tree. Subclassed // from GELEM to imbedded LNKs will know how to compute // proper offsets. //////////////////////////////////////////////////////////////////// class GNODE : public GELEMLNK { friend class GEDGE; public: GNODE (); virtual ~ GNODE (); // Accessors (const and non-const) // Return the source arc cursor (or NULL) // Return the sink arc cursor (or NULL) virtual GEDGE * & PedgeSource () { return _glkArcs.PlnkSource() ; } virtual GEDGE * & PedgeSink () { return _glkArcs.PlnkSink() ; } virtual GEDGE * PedgeSource () const { return _glkArcs.PlnkSource() ; } virtual GEDGE * PedgeSink () const { return _glkArcs.PlnkSink() ; } // Return counts of arcs in the given direction virtual UINT CSourceArc () const; virtual UINT CSinkArc () const; // Return counts of arcs in the given direction filtering by type virtual UINT CSourceArcByEType ( int eType ) const; virtual UINT CSinkArcByEType ( int eType ) const; virtual INT EType () const { return EGELM_NODE ; } LEAK_VAR_ACCESSOR protected: // Return the correct insertion point for a new arc, source or sink. // Called whenever a new arc is created to maintain ordering of arcs. // Default behavior is to add the new arc as the last. virtual GEDGE * PedgeOrdering ( GEDGE * pgedge, bool bSource ); // Notification routine called when an arc dies; used to adjust cursors virtual void ArcDeath ( GEDGE * pgedge, bool bSource ); protected: // Arc cursors: pointers to one source and one sink arc GEDGLNK _glkArcs; LEAK_VAR_DECL HIDE_UNSAFE(GNODE); }; DEFINEVP(GNODE); //////////////////////////////////////////////////////////////////// // class GEDGE: Base class for an arc in a graph. //////////////////////////////////////////////////////////////////// class GEDGE : public GELEMLNK { public: // Internal class for chains (doubly-linked lists) of arcs typedef XCHN CHN; // Constructor requires source and sink nodes GEDGE ( GNODE * pgnSource, GNODE * pgnSink ); virtual ~ GEDGE (); // Accessors: // Return source and sink arc chains CHN & ChnSource () { return _lkchnSource; } CHN & ChnSink () { return _lkchnSink; } // Return source and sink node GNODE * & PnodeSource() { return _glkNodes.PlnkSource(); } GNODE * & PnodeSink() { return _glkNodes.PlnkSink(); } virtual INT EType () const { return EGELM_EDGE ; } LEAK_VAR_ACCESSOR protected: // Chain of all arcs originating in the same source node CHN _lkchnSource; // Chain of all arcs terminating in the same sink node CHN _lkchnSink; // Source and sink node pointers GNODLNK _glkNodes; LEAK_VAR_DECL HIDE_UNSAFE(GEDGE); }; //////////////////////////////////////////////////////////////////// // class GNODENUM_BASE: // // Base class for generic linkable object enumerators. // Each enumerator can enumerate up or down (source or sink) // and forward or reverse in the chain. //////////////////////////////////////////////////////////////////// class GNODENUM_BASE { // typedefs for pointer-to-member-function pointer types typedef bool (*PFFOLLOW) (GEDGE *); typedef GEDGE::CHN & (GEDGE::*PARCCHN)(); typedef GEDGE * (GEDGE::CHN::*PNX)(); typedef GNODE * & (GEDGE::*PARCDGN)(); public: // Construct an enumerator. GNODENUM_BASE ( bool bSource, // true ==> enumerate source (parent) arcs bool bDir = true, // true ==> follow arc ordering forwards bool bBoth = false ) ; // true ==> enumerate other arcs also // Set the enumerator to have a new base; iDir == -1 means don't // change direction flag; otherwise, it's really a "bool". void Reset ( bool bSource, int iDir = 0, int bBoth = 0 ) ; // Set the arc following test function pointer. To use, declare // a function like "bool BMyFollow (GEDGE * pge)", and pass its // address to "SetPfFollow()". It will be called during enumeration // to see if an arc should be followed. Alternatively, you can // override "BFollow()" in your templated-derived subclass. void SetPfFollow ( PFFOLLOW pfFollow ) { _pfFollow = pfFollow ; } // Set the intrinsic type of arc to follow (i.e., "EType()"); -1 ==> all. void SetETypeFollow ( int iEgelmTypeMin = -1, int iEgelmTypeMax = -1 ) { _iETypeFollowMin = iEgelmTypeMin; _iETypeFollowMax = iEgelmTypeMax; } // Set the user-definable type of arc to follow (i.e., "IType()"); -1 ==> all. void SetITypeFollow ( int iITypeFollowMin = -1, int iITypeFollowMax = -1 ) { _iITypeFollowMin = iITypeFollowMin; _iITypeFollowMax = iITypeFollowMax; } // Return the edge used for the current position GEDGE * PgedgeCurrent () { return _pedgeCurrent; } protected: // Position to the next pointer; return NULL when done. bool BNext () ; // Assign the node being enumerated and the starting point void Set ( GNODE * pnode ); // Overrideable routine to check arc type virtual bool BFollow ( GEDGE * pedge ); // Call either the "follower" function or the virtualized follower. bool BFollowTest ( GEDGE * pedge ) { return _pfFollow ? (*_pfFollow)(pedge) : BFollow(pedge); } // Set the starting point for this mode of iteration void SetStartPoint (); protected: PARCCHN _pfChn; // Pointer to member function to return chain PNX _pfNxPv; // Ptr to mbr func to move forward or back PARCDGN _pfPgn; // Ptr to member func to get node from arc GEDGE * _pedgeNext; // Next arc GEDGE * _pedgeStart; // Starting arc GEDGE * _pedgeCurrent; // Arc used for recent answer GNODE * _pnodeCurrent; // Recent answer GNODE * _pnodeBase; // Node of origin PFFOLLOW _pfFollow; // Follow override bool _bDir; // Horizontal direction of enumeration bool _bSource; // Vertical direction of enumeration bool _bBoth; // Enumerate arcs in both directions bool _bPhase; // Phase of the search (for _bBoth == true) // These values determine which kinds of arcs are to be followed; // -1 implies not set. Used by BFollow(). int _iETypeFollowMin; // Follow this canonical type of arc int _iETypeFollowMax; int _iITypeFollowMin; // Follow this user-defined type of arc int _iITypeFollowMax; }; //////////////////////////////////////////////////////////////////// // template GNODENUM: // // Generic enumeration class for nodes. All conversions are type-safe; // exceptions will be thrown if accesses are made to objects which are // node subclasses of GNODE. // // Each enumerator can enumerate up or down (source or sink) // and forward or reverse in the chain. // // * Use Set() to set the starting node point. // // * Use the post-increment operator to advance. // // * Use Pcurrent() or pointer-dereference operator to get the // current node pointer. // // * Enumerators are reusable. To restart, reissue "Set()"; to // change enumeration parameters, use Reset(). // //////////////////////////////////////////////////////////////////// template class GNODENUM : public GNODENUM_BASE { public: GNODENUM ( bool bSrc, bool bDir = true, bool bBoth = false ) : GNODENUM_BASE( bSrc, bDir, bBoth ) {} void Set ( GND * pgnd ) { GNODENUM_BASE::Set( pgnd ); } bool operator++ (int i) { return BNext() ; } bool operator++ () { return BNext() ; } GND * PnodeCurrent () { GND * pnode = NULL; if ( _pnodeCurrent ) DynCastThrow( _pnodeCurrent, pnode ); return pnode; } GND * operator -> () { return PnodeCurrent() ; } GND * operator * () { return PnodeCurrent() ; } void Reset ( bool bSrc, int iDir = -1, int iBoth = -1 ) { GNODENUM_BASE::Reset( bSrc, iDir, iBoth ) ; } protected: bool BNext () { return GNODENUM_BASE::BNext() ; } HIDE_UNSAFE(GNODENUM); }; //////////////////////////////////////////////////////////////////// // class GRPH: a generalized graph // // This is a linkable object because it acts as the anchor // for its linked list, which connects all enumerable items // in this collection. //////////////////////////////////////////////////////////////////// class GRPH : public GELEMLNK { public: GRPH () {} virtual ~ GRPH () { Clear(); } virtual void AddElem ( GELEMLNK & gelemlnk ) { gelemlnk.ChnColl().Link( this ); } virtual INT EType () const { return EGELM_GRAPH ; } // Remove all elements from the graph void Clear () { GELEMLNK * pgelem; while ( pgelem = ChnColl().PgelemNext() ) delete pgelem; } HIDE_UNSAFE(GRPH); }; //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// // Inline member functions //////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////// inline GNODE :: GNODE () { LEAK_VAR_UPD(1) } inline GNODE :: ~ GNODE () { GEDGE * pedge = NULL; while ( pedge = PedgeSource() ) delete pedge; while ( pedge = PedgeSink() ) delete pedge; LEAK_VAR_UPD(-1) } inline GEDGE :: GEDGE ( GNODE * pnodeSource, GNODE * pnodeSink ) : _lkchnSource(this), _lkchnSink(this) { if ( pnodeSource == NULL || pnodeSink == NULL ) throw GMException( EC_NULLP, "attempt to construct a GEDGE without linkage" ); // Bind to the pair of nodes PnodeSource() = pnodeSource; PnodeSink() = pnodeSink; // Determine insertion point into source and sink chains, and // inform nodes of our existence GEDGE * pedgeSource = pnodeSource->PedgeOrdering( this, false ); GEDGE * pedgeSink = pnodeSink->PedgeOrdering( this, true ); if ( pedgeSource ) { ChnSource().Link( pedgeSource ); } if ( pedgeSink ) { ChnSink().Link( pedgeSink ); } LEAK_VAR_UPD(1) } inline GEDGE :: ~ GEDGE () { PnodeSource()->ArcDeath( this, false ); PnodeSink()->ArcDeath( this, true ); LEAK_VAR_UPD(-1) } inline UINT GNODE :: CSourceArc () const { return PedgeSource() ? PedgeSource()->ChnSink().Count() : 0; } inline UINT GNODE :: CSinkArc () const { return PedgeSink() ? PedgeSink()->ChnSource().Count() : 0; } // Return counts of arcs in the given direction filtering by type inline UINT GNODE :: CSourceArcByEType ( int eType ) const { UINT cArcs = 0; GEDGE * pgedgeStart = PedgeSource(); GEDGE * pgedge = pgedgeStart; while ( pgedge ) { if ( pgedge->EType() == eType ) ++cArcs; pgedge = pgedge->ChnSink().PgelemNext(); if ( pgedge == pgedgeStart ) break; } return cArcs; } inline UINT GNODE :: CSinkArcByEType ( int eType ) const { UINT cArcs = 0; GEDGE * pgedgeStart = PedgeSink(); GEDGE * pgedge = pgedgeStart; while ( pgedge ) { if ( pgedge->EType() == eType ) ++cArcs; pgedge = pgedge->ChnSource().PgelemNext(); if ( pgedge == pgedgeStart ) break; } return cArcs; } // Return the correct insertion point for a new edge, // source or sink. inline GEDGE * GNODE :: PedgeOrdering ( GEDGE * pedge, bool bSource ) { GEDGE * pedgeOrdering = NULL; if ( bSource ) { pedgeOrdering = PedgeSource(); if ( ! pedgeOrdering ) PedgeSource() = pedge; } else { pedgeOrdering = PedgeSink(); if ( ! pedgeOrdering ) PedgeSink() = pedge; } return pedgeOrdering; } inline void GNODE :: ArcDeath ( GEDGE * pedge, bool bSource ) { if ( bSource ) { if ( pedge == PedgeSource() ) PedgeSource() = pedge->ChnSink().PgelemNext(); if ( pedge == PedgeSource() ) PedgeSource() = NULL; } else { if ( pedge == PedgeSink() ) PedgeSink() = pedge->ChnSource().PgelemNext(); if ( pedge == PedgeSink() ) PedgeSink() = NULL; } } inline GNODENUM_BASE :: GNODENUM_BASE ( bool bSource, bool bDir, bool bBoth ) : _pfChn(NULL), _pfNxPv(NULL), _pfPgn(NULL), _pfFollow(NULL), _iETypeFollowMin(-1), _iETypeFollowMax(-1), _iITypeFollowMin(-1), _iITypeFollowMax(-1), _pnodeBase(NULL) { Reset( bSource, bDir, bBoth ) ; } // Set the base object for the enumeration inline void GNODENUM_BASE :: Reset ( bool bSource, int iDir, int iBoth ) { if ( iDir >= 0 ) _bDir = iDir ; if ( iBoth >= 0 ) _bBoth = iBoth; _bSource = bSource; _pfNxPv = _bDir ? & GEDGE::CHN::PgelemNext : & GEDGE::CHN::PgelemPrev; if ( _bSource ) { _pfChn = & GEDGE::ChnSink; _pfPgn = & GEDGE::PnodeSource; } else { _pfChn = & GEDGE::ChnSource; _pfPgn = & GEDGE::PnodeSink; } _pedgeStart = _pedgeNext = _pedgeCurrent = NULL; _pnodeCurrent = NULL; _bPhase = false; } // Set the starting point of the iteration. If 'pnode' is NULL, // use the original node. inline void GNODENUM_BASE :: Set ( GNODE * pnode ) { if ( pnode ) _pnodeBase = pnode; SetStartPoint(); BNext(); } inline void GNODENUM_BASE :: SetStartPoint () { _pedgeNext = _bSource ? _pnodeBase->PedgeSource() : _pnodeBase->PedgeSink(); _pedgeStart = _pedgeNext; } // Follow the arc if the constraints are met, both inherent type (EType()) // and user-definable type (IType(). inline bool GNODENUM_BASE :: BFollow ( GEDGE * pedge ) { if ( _iETypeFollowMin >= 0 ) { int etype = pedge->EType(); if ( _iETypeFollowMax < 0 ) { // Just the "min" is set; compare equal if ( etype !=_iETypeFollowMin ) return false; } else { if ( etype < _iETypeFollowMin || etype > _iETypeFollowMax ) return false; } } if ( _iITypeFollowMin >= 0 ) { int itype = pedge->IType(); if ( _iITypeFollowMax < 0 ) { // Just the "min" is set; compare equal if ( itype !=_iITypeFollowMin ) return false; } else { if ( itype < _iITypeFollowMin || itype > _iITypeFollowMax ) return false; } } return true; } inline bool GNODENUM_BASE :: BNext () { _pnodeCurrent = NULL; _pedgeCurrent = NULL; do { while ( _pedgeNext == NULL ) { // If we're not iterating both directions, // or this is phase 2, exit. if ( _bPhase || ! _bBoth ) return false; // Set "2nd phase" flag, invert source/sink Reset( !_bSource ); _bPhase = true; SetStartPoint(); } if ( BFollowTest( _pedgeNext ) ) { _pedgeCurrent = _pedgeNext; _pnodeCurrent = (_pedgeNext->*_pfPgn)(); } GEDGE::CHN & chn = (_pedgeNext->*_pfChn)(); _pedgeNext = (chn.*_pfNxPv)(); if ( _pedgeStart == _pedgeNext ) _pedgeNext = NULL; } while ( _pnodeCurrent == NULL ); return _pnodeCurrent != NULL; } #endif