Side effects of uninitialized variables using controlled_vars

The controlled_vars library allows you to define variables that you never initialize. This is very practical if you have a large number of variables or a large number of objects of a class.

The idea is pretty simple and once you remove the debug, the variable becomes 1 to 1 equivalent to a char, unsigned char, signed char, short, unsigned short, int, unsigned, long, unsigned long, etc. In other words, it goes really fast.

However, in debug mode, the class adds a bool to know whether the variable was initialized or not. If not yet initialized, you cannot read the value or you get an exception.

The following shows an example of a class with a field named f_value and a get_value() and set_value(). It is followed by two example, one of which throws because f_value is not yet defined when accessed.

class Foo
{
public:
  Foo()
  {
  }

  int get_value()
  {
    return f_value;
  }
  void set_value(int v)
  {
    f_value = v;
  }

private:
  controlled_vars::rint32_t   f_value;
};

// Usage 1: proper
Foo f;
f.set_value(33);
int v = f.get_value(); // v is 33

// Usage 2: invalid
Foo f;
int v = f.get_value(); // this throws, value undefined

This can be dearly helpful to debug classes where all your variable members do not need to always be initialized (remember, though, that variables used localy in a single function do not need to be variable members, so if you were using such variables, make sure to redesign your class to move those variables and make them local only.)

This class, as is, has a problem because the variable may not be defined when you access the f_value. This means the following also throws:

Foo f1, f2;
f1 = f2;

Since the constructor does not initialize the f_value member, the default copy operator attempts to copy it by doing something like this:

f1.f_value = f2.f_value;

To palliate to this problem, you have to define the copy operator and work out how the f_value field needs to be copied. If all your other functions are all proper, copying the value here is safe. However, you cannot assume that the f_value variable member in f1 is defined as a result of making a copy of f2.

Foo::Foo& Foo::Foo(Foo const& rhs)
{
  if(this != &rhs)
  {
#ifdef CONTROLLED_VARS_DEBUG
    if(rhs.f_value.is_initialized())
#endif
    {
        f_value = rhs.f_value;
    }
  }
  return *this;
}

Notice the #ifdef. Unfortunately, the is_initialized() function is only defined in debug mode. We may want to have it always available, although it would lie in case the variable is or is not initialized.

However, if you plan your classes properly, you should not have to check on whether the f_value field is initialized using the is_initialized() function. Instead, your class should either know that it is or not initialized by some other means, and if it can't, then it probably should make use of an auto-init or even need-init variable type instead.

Syndicate content

Snap! Websites
An Open Source CMS System in C++

Contact Us Directly