2010-08-06

Handles and Templates

Sometimes you might want to use a template class that would inherit Standard_Transient (or its subclass). As you need to pair it with a handle counterpart (which would have to inherit respective Handle_Standard_Transient subclass), you can't do this with the DEFINE_STANDARD_HANDLE macro directly. This post explains how you could do this.

ACIS and Parasolid formats (like IGES or STEP for instance) represent a file as a list of entities that have id's. To represent an ACIS or a Parasolid file in memory, the Base_FileModel class is used. It basically provides access to the file header and manages a set of entities where an entity is of course specific to each format. To ease sharing Base_FileModel it should be manipulated by handle (i.e. inherit Standard_Transient). Here is how this is done:

template class Base_FileModel : public Base_Transient
{
public:

//! Defines a type to index entities.
/*! @todo It must be signed (e.g. to address -1 in ACIS. Should it be consistent with size_t (for 64bit port)?
*/
typedef int Index_t;


//! Defines an entity type.
/*! Equals to the template parameter.*/
typedef En Entity_t;


//! Returns a number of entities.
/*! An empty model returns 0.*/
size_t NbEntities() const { return myRevEntMap.Size() - 1; }

//! Adds an entity.
/*! Returns a rank number with which \a theEntity has been added into the model. By
default, a new rank number is maximum rank number + 1.
Null entities are ignored.

This method is not thread-safe.

\sa Bind().
*/
Index_t Add (const Entity_t& theEntity)
{
Index_t aRank = myNullRank;
if (!theEntity.IsNull()) {
aRank = myEntVec.Size() + myNullRank;
Bind (aRank, theEntity);
}
return aRank;
}
...
protected:

//! Constructor.
/*! \a theNullRank defines a null rank used to designate a null entity (e.g. 0 for
Parasolid or -1 for ACIS).
*/
Base_FileModel (const FileHeader_t& theHeader, const Index_t theNullRank) :
myHeader (theHeader), myNullRank (theNullRank)
{
const Entity_t aNull;
myEntVec.SetValue (Index (theNullRank), aNull);
myRevEntMap.Bind (aNull, theNullRank);
}

...

NCollection_Vector myEntVec;
NCollection_DataMap myRevEntMap;
const Index_t myNullRank;


private:
Base_FileModel (const Base_FileModel&);
Base_FileModel& operator= (const Base_FileModel&);
};


Here is how a particular class (for ACIS) is defined:

DEFINE_STANDARD_HANDLE(ACISBase_Model,Base_Transient)

class ACISBase_Model : public Base_FileModel
{
public:

//! Constructor
/*! Creates a model with a new ACISBase_FileHeader.*/
ACISBase_Model() : Base_FileModel (
new ACISBase_FileHeader(), EntityNullRank) {}

...
};

Similarly in ACISBase_Model.cxx:

IMPLEMENT_STANDARD_HANDLE(ACISBase_Model,Base_Transient)
IMPLEMENT_STANDARD_RTTIEXT(ACISBase_Model,Base_Transient)

That's it !