Extensible enums
I thought this might be of interest readers of this group.
I work in an application in the CAD/CAE domain. We rely on external CAD
applications to manipulate CAD models. Many, if not all, CAD applications
allow the parameters of a CAD model to be changed one parameter at a time,
and then allow you to regenerate the model -- think of the ability to
change the radius and height of a cylinder in two distinct steps and to
regenerate the model in a third step.
Before we use a CAD model for CAE operations, such as meshing the model,
we want to make sure that the model does not need regeneration. To detect
whether the model needs regeneration, we ask the the CAD model, through a
proxy object, whether it is up to date. The CAD model can be out of date
for various types of modifications. The two that we are concerned with
right now are: modification of parameters that affect the geometry of the
model and modifications of tags that do not affect the geometry of the
model. If the model is not up to date, we want to know whether it is
due to modification of parameters or tags.
I needed a type to return the status of a CAD model when the
corresponding proxy object's member function isUpToDate() is called. I
started with an enum that looked like:
enum UptodateStatus
{
UPTODATE = 0,
PARAMETERS_MODIFIED = 00001,
TAGS_MODIFIED = 00002
};
I started with the declaration of Proxy::isUpToDate() from
virtual bool isUpToDate(int& reason) = 0;
The expectation was that if parameters have been modified,
ConcreteProxy::isUpToDate() would set the output argument thus:
reason |= PARAMETERS_MODIFIED;
If tags have been modified, ConcreteProxy::isUpToDate() would set
the output argument thus:
reason |= TAGS_MODIFIED;
While I was doing that, I realized that the file that contained the
definition of the enum would need to be modified if we found another
reason for the model not being up-to-date. The file would be open for
modification, by design. I did not like that. I started thinking how I
can provide a method for generating enum-like numbers that allow creation
of new integer constants without needing to modify an existing .h file.
The numbers generated must follow the pattern that allows use of the
bitwise OR, AND, and XOR operations to detect the reasons for the CAD
model not being up-to-date.
I came up with a design that involves two main classes and other derived
types that would be able do meet the requirements.
The first class is a class template. Its design and implementation allow
it to be used elsewhere that have similar requirements. It helps define
classes that can depend on it to get a value that is like an enum. The
second class holds a value and provides a function to generate numbers
that follow the sequence 1, 2, 4, 8, ...
We can create a type that will server the purpose of indicating when
parameters have been modified as follows:
struct ParametersModified : public Enum<ParametersModified,
UpToDateStatus>{};
We can create a type that will server the purpose of indicating when
tags have been modified as follows:
struct TagsModified : public Enum<TagsModified,
UpToDateStatus>{};
The declaration of Proxy::isUpToDate() needs to be
virtual bool isUpToDate(UpToDateStatus& status) = 0;
If parameters have been modified, ConcreteProxy::isUpToDate() would set the
output argument thus:
status |= ParametersModified::getValue();
If tags have been modified, ConcreteProxy::isUpToDate() would set
the output argument thus:
status |= TagsModified::getValue();
Clients that want to check whether parameters have been modified would
use:
if ( status.isSet(ParametersModified::getValue()) )
{
// Do stuff when parameters have been modified.
}
The creation and use of ParametersModified and TagsModified have been
decoupled from the main two classes while allowing UpToDateStatus to work
with values associated with them.
=======================================================================
/*
The sole purpose of this class is to help derived classes get a unique
integer value associated with the derived class. The first template
parameter makes sure that a we get a distinct template instantiation for
the derived class. The second tempalte parameter is used to generate the
value.
*/
template <typename T, typename ValueGenerator>
struct Enum
{
static int getValue()
{
static int value = ValueGenerator::getNextValue();
return value;
}
};
class J_MSC_UpToDateStatus
{
public:
static int getNextValue()
{
static int value = 1;
int returnValue = value;
value *= 2;
return returnValue;
}
J_MSC_UpToDateStatus() : value_(0){}
bool isSet(int val) { return (( value_ & val ) == val); }
void operator|=(int rhs) { value_ |= rhs; }
void operator&=(int rhs) { value_ &= rhs; }
void operator^=(int rhs) { value_ &= rhs; }
private:
int value_;
};
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]