dcsimg
December 9, 2016
Hot Topics:

Mike's Grab Bag of Useful Stuff

  • April 8, 2005
  • By Mike McShaffry
  • Send Email »
  • More Articles »

Supporting Optional Variables with Optional<T>

A really favorite template of mine is one that encapsulates optional variables. Every variable stores values, but they don't store whether the current value is valid. Optional variables store this information to indicate if the variable is valid or initialized. Think about it, how many times have you had to use a special return value to signify some kind of error case?

Take a look at this code, and you'll see what I'm talking about:

bool DumbCalculate1(int &spline)
{
   //imagine some code here....
   //
   //The return value is the error, and the value of spline is
   //invalid return
   false;
}

#define ERROR_IN_DUMBCALCULATE (-8675309)
int DumbCalculate2()
{
   //imagine some code here....
   //
   //The return value is a "special" value, we hope could never be
   //actually calculated
   return ERROR_IN_DUMBCALCULATE;
}

int _tmain(void)
{
   ////////////////////////////////////////////////////////////////
   //
   //Dumb way #1 - use a return error code, and a reference to get
   //to your data.
   //
   int dumbAnswer1;
   if (DumbCalculate1(dumbAnswer1))
   {
      //do my business...
   }

   ////////////////////////////////////////////////////////////////
   //Dumb way #2 - use a "special" return value to signify an error
   int dumbAnswer2 = DumbCalculate2();
   if (dumbAnswer2 != ERROR_IN_DUMBCALCULATE)
   {
      //do my business...
   }

}

There are two evil practices in this code. The first practice, "Dumb Way #1" requires that you use a separate return value for success or failure. This causes problems because you can't use the return value DumbCalculate1() function as the parameter to another function because the return value is an error code:

AnotherFunction(DumbCalculate1());    //whoops.Can't do this!

The second practice I've seen that drives me up the wall is using a "special" return value to signify an error. This is illustrated in the DumbCalculate2() call. In many cases, the value chosen for the error case is a legal value, although it may be one that will "almost never" happen. If those chances are one in a million and your game sells a million copies, how many times per day do you think someone is going to get on the phone and call your friendly customer service people? Too many.

Here's the code for optional<T>, a template class that solves this problem.

#pragma once

////////////////////////////////////////////////////////////////////
//optional.h
//
//An isolation point for optionality, provides a way to define
//objects having to provide a special "null" state.
//
//In short:
//
//struct optional<T>
//{
//bool m_bValid;
//
//T m_data;
//};
//
//

#include <new>
#include <assert.h>

class optional_empty {};
template <unsigned long size>
class optional_base
{
public:
   //Default -invalid.

   optional_base():m_bValid(false){}

   optional_base & operator =(optional_base const & t)
   {
      m_bValid = t.m_bValid;
      return *this;
   }

   //Copy constructor
   optional_base(optional_base const & other)
      :m_bValid(other.m_bValid) { }

   //utility functions
   bool const valid()const   { return m_bValid; }
   bool const invalid()const { return !m_bValid; }

   protected:
      bool m_bValid;
      char m_data [size ];    //storage space for T
   };

   template <class T>
   class optional :public optional_base<sizeof(T)>
   {
   public:
   //Default -invalid.

   optional(){}
   optional(T const & t) {construct(t); m_bValid = (true);}
   optional(optional_empty const &) { }
   optional & operator = (T const & t)
   {
   if (m_bValid)
   {
      *GetT()=t;
   }
   else
   {
      construct(t);
      m_bValid = true;    //order important for exception safety.
   }

   return *this;
   }

   //Copy constructor
   optional(optional const & other)
   {
      if (other.m_bValid)
      {
         construct(*other);
         m_bValid = true;    //order important for exception safety.
      }
   }

   optional & operator =(optional const & other)
   {
      assert(!(this ==& other));    //don't copy over self!
      if (m_bValid)
      {                    //first,have to destroy our original.
      m_bValid = false;    //for exception safety if destroy() throws.
      //(big trouble if destroy() throws, though)
      destroy();
   }

   if (other.m_bValid)
   {
      construct(*other);
      m_bValid = true;    //order vital.

   }
   return *this;
}

bool const operator == (optional const & other)const
{
   if ((! valid()) && (!other.valid())) { return true; }
   if (valid() ^ other..valid()) {return false;}
   return ((**this) == (*other));
}

bool const operator < (optional const & other) const
{
   //equally invalid - not smaller.
   if ((! valid()) && (!other.valid())) {return false;}
   //I'm not valid, other must be, smaller.
   if (! valid()) {return true;}
   //I'm valid, other is not valid, I'm larger
   if (! other.valid()) {return false;}

   return ((**this) < (*other));
}

~optional(){if (m_bValid)destroy();}

//Accessors.

T const & operator * () const       {assert(m_bValid);return *GetT();}
T & operator * ()                   {assert(m_bValid);return *GetT();}
T const * const operator -> ()const {assert(m_bValid);return GetT();}
T       * const operator -> ()      {assert(m_bValid);return GetT();}

//This clears the value of this optional variable and makes it
//invalid once again.
void clear()
{
   if (m_bValid)
   {
      m_bValid =false;
      destroy();
   }
}

//utility functions
bool const valid()const      {return m_bValid;}
bool const invalid()const    {return !m_bValid;}

private:


   T const *const GetT()const 
      {return reinterpret_cast<T const *const>(m_data);}
   T *const GetT()
   {return reinterpret_cast<T * const>(m_data);}
      void construct(T const >t) { new (GetT())T(t); }
      void destroy(){ GetT()->~T(); }
};




Page 2 of 3



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Thanks for your registration, follow us on our social networks to keep up-to-date
Rocket Fuel