Cluebat-man to the rescue

A weblog dedicated to Visual C++, interoperability and other stuff.

Fun with templates: Implementing a tri-state value

A couple of weeks ago I was working on a text file parser and I needed to know when certain variables had been assigned a value so that my algorithm could skip the remainder of that file section.

Since the ordering of data is not guaranteed explicitly (and I wanted to keep my algorithms reusable for other parts of the program) I had to find a way to keep track of which variables had been assigned to and which not.

The easiest way to do this was to keep a Boolean flag for each variable that I had to track, but this is error prone and cumbersome to maintain.

The better solution would be to use a template class J that could convert to and from the required datatype, and with the appropriate functionality for keeping a state flag. That way each of the variables had all the tracking functionality built in while still allowing me to assign and read from them like I would with a normal string or int or whatever.

So I slapped together a class that did just what I wanted and continued programming. But I thought it would be neat to fully work out the details and decided to spend some of my precious free time. The main requirement I placed on this class was that I wanted it to be used as transparently as possible, allowing me to use TriStateValue instances instead of their contained type without modifying any of the code if at all possible.

Base stuff

The basic features of this class are very simple.

 

template <class _ValType> struct TriStateVal
  {
  private:
    bool Sentinel;
    _ValType Value;

    void VerifyState(void) const
    {
      if(!Sentinel)
        throw ExTriStateVal();
    }

  public:
    typedef _ValType value_type;

    TriStateVal() :
      Sentinel(false), Value(_ValType()) {}

    explicit TriStateVal(_ValType const &NewVal) :
      Sentinel(true), Value(NewVal) {}

    TriStateVal(TriStateVal<_ValType> const &original) :
      Sentinel(original.Sentinel), Value(original.Value) {}
  };

The data is kept private to keep others from looking at it, because that would defeat the whole purpose of this class.

The VerifyState method is also for internal use only. Its purpose if pretty obvious, and it will be called anytime an operation is performed that relies on Value having been assigned a valid value.

The 3 constructors do nothing more than initializing the internal variables. The conversion constructor has been marked explicit for the reasons detailed in an earlier blog post.

Assigning and casting

The 2 major features of our class is that we want to assign a value of type _ValType, or that we want to assign it to a variable of type _ValType. This is handled by the following operators.

TriStateVal<_ValType>& operator =(_ValType const &NewVal)
{
  Value = NewVal;
  Sentinel = true;
  return *this;
}

operator _ValType()
{
  VerifyState();
  return Value;
}

Assigning a value automatically sets the sentinel to true during the assignment. The type cast throws an exception if the sentinel is not set. It’s pretty boring stuff actually

Then there are a couple of methods included for convenience

void Clear()
{
  Value = _ValType();
  Sentinel = false;
}

bool HasValue()
{
  return Sentinel;
}
   
_ValType GetValue()
{
  VerifyState();
  return Value;
}

Implementing the standard operators

With all that stuff out of the way, I decided it would be nice to implement all the standard operators that would allow you to e.g. add and subtract 2 TriStateValues as if they were real value types.

The nice thing about templates is that I can implement them all and don’t worry about whether _ValType actually supports them or not. There will only be a compiler error if you use them and they are not implemented.

The implementation is very simple. A lot of methods are identical, save for the actual operator. For example, the implementation of the + and the – operator are exactly the same except for the operation that is performed.

This is where I decided to take the easy road and use macros.

I defined a macro for each type of operation, and then used that macro to implement all operations that have the same structure.

I am not going to list all the individual operators and implementations here. Instead I will list one operator macro and the it is used in the code

Standard arithmetic operations

These operations perform simple arithmetic operations on the 2 operands, and then returns a new TriStaveVal instance with the result.

#define TSV_OP(OP)\
    TriStateVal<_ValType>
      operator OP (TriStateVal<_ValType> const &rhs)\
   
{\
      VerifyState();\
      rhs.VerifyState();\
      return TriStateVal<_ValType>(Value OP rhs.Value);\
    }

Making this a macro gives me the advantage that I have to implement it only once. This prevents copy / paste errors and it also increases readability.

The function itself will verify that the right hand side and left hand side arguments have a valid value, and then perform an operation on both values and return the result. This macro implements the following operations in the class body:

    TSV_OP(+);
    TSV_OP(-);
   
TSV_OP(*);
    TSV_OP(/);
    TSV_OP(%);
    TSV_OP(^);
    TSV_OP(&);
    TSV_OP(|);

Other considerations

I chose not to implement the * and & operators because that wouldlead to the possibility of Value being out of sync with Sentinel. Ditto for the -> and ->* operators.

I didn’t see any use for a ‘,’ overload, a function call operator or a an array subscript so I left those out as well.

The end result is IMO a good balance between transparency and simplicity. The code is available for download as usual. If you experience problems or have suggestions you are welcome to drop me a message.

Attachment: TriStateValue.zip
Posted: Oct 11 2007, 06:48 AM by vanDooren | with 1 comment(s)
Filed under:

Comments

Cluebat-man to the rescue said:

One of the things that everyone has to do sooner or later is parsing command line arguments into program

# December 17, 2008 5:44 AM