Null-state — part II

In the previous post we introduced the concept of a null-state. Now it is time to expand a bit more on the subject. We will see how null-state is even more useful in C++11, whether it is worth using it, and we will see how we can make non-nullable types nullable with a library tool.

Move constructor and move assignment

Move operations in C++11 allow us to steal the contents of the moved-from object. However, some sanity rules still apply: the argument of the move constructor and move assignment must be left in the state where it is safe to destroy such an object or assign a new value to it. We described in the previous post what safe means. For objects that model a unique resource ownership, there cannot exist two objects that own the same resource, so if we construct a new object using move constructor, we have to make the original object aware that it no longer owns the resource. A null-value is exactly what we need to assign to the original. This is how we would implement a move constructor for a smart pointer that behaves like std::unique_ptr:

template <typename T>
class smart_ptr
{
  T* ptr_ = nullptr;

public:
  smart_ptr(smart_ptr && rhs)
    : ptr_{rhs.ptr_}        // copy construct
  { 
    rhs.ptr_ = nullptr;     // reset the original
  }
  // rest of the interface
};

For many types, and especially for types modeling unique resource ownership, the moved-from state is the same as default-constructed state. And in fact, typically a move constructor could be implemented by move constructing members one by one, and then calling the default constructor for the moved-from object. Similar reasoning applies to move-assignment. Well, I do not really recommend calling default constructor in the middle of object’s life-time; this is only to show a conceptual pattern.

Pros and cons of enabling a null-state

Not every type is capable of storing a null-state. For instance, consider std::lock_guard. This is one of the types of locks that can lock a mutex. Its interface is dead-simple: it locks a mutex in constructor (naturally the constructor is explicit and takes a reference to mutex type) and unlocks it in destructor. No default constructor, no move operations: no null-state. If object of type std::lock_guard is successfully created, it always contains a resource to be managed.

So why do we need a std::lock_guard if we have std::unique_lock with full move semantics and null-state? First, it makes our intentions clear: if we do not need to move our resource, let’s be explicit about it; slimmer interface causes less bugs. Second, and more importantly, it is about efficiency. We said in the previous post that we usually need not expose the check for null-value in the public interface. This is true, but internally, we have to perform this check very often: nearly upon every operation. How does destructor look for std::lock_guard and std::unique_lock? Most probably like this:

template <typename M>
std::lock_gurd<M>::~lock_gurd()
{
  mutex_.unlock();
}

template <typename M>
std::unique_lock<M>::~unique_lock()
{
  if (mutex_) {
    mutex_->unlock();
  }
}

And destructor is only one example. Locks have a small interface but for other types with many member functions, in each function you need to deal with the special case. What do you do when someone wants to dereference your smart pointer?

template <typename T>
T* smart_ptr<T>::operator->()
{
  if (!ptr_) {
    // do what?
  }
  return ptr_;
} 

Shall you throw an exception? But this is a logic error, and it is not clear how such exception should be handled. But even an if-statement may cost some run-time performance. Perhaps you should skip the check and just blindly forward the raw pointer (and put the responsibility for making sure that the pointer is non-null on the user)? But this is very uncomfortable and may encourage programmer errors. Note that guards (like std::lock_guard), by not supporting a null state, make it impossible for the user to make a logic error. They make the code safer. On the other hand, you loose flexibility: you do not have move semantics anymore, and no two-phase initialization. The trade-off here is between safety and flexibility. The downsides of two-phase initialization are covered in more detail in Bjarne Stroustrup’s Appendix E to “The C++ Programming Language.”

Enabling null-state

Now, having said all that about the null-state, we will see how to enable a null-state for types that do not have it already. You may need it if you need a nullable int, that either has an integral value or a special value not-an-int. Or you may need it if the third-party library type you are using provides a RAII-like, or guard-like interface, that forces a single-phase initialization and resource release in destructor, but in your program you have to, for some reason, acquire the resource after the construction, or release it before object destruction. In either case what you need is Boost.Optional.

The first case is fairly trivial, especially if you are already familiar with boost::optional:

boost::optional<int> i;   // i contains a null-value
i = 1;                    // assign value 1
i = 0;                    // assign value 0
i = boost::none;          // assign null-value

if (i == boost::none) {}  // check for null-value
else if (*i == 0)     {}  // check for value 0
else if (*i == 1)     {}  // check for value 1

Note that when it comes to setting the value of i, assigning ordinary values and a special value none that represent a null-state looks the same. Accessing the value of i is not uniform. This is for a good reason. As we said in the previous post, the interface of nullable type can only be used in a limited way when the value currently stored is a null-value. Boost.Optional makes a good job of differentiating the part of the interface that always works from the one that may not work. The safe operations are only assigning a new state and querying for the null-state. In order to use the other, potentially unsafe, part of the interface you have to use operators * and ->. Calling the two operators says that you are confident you are operating on a valid object.

When RAII disturbs…

The second case above is more interesting. You might ask why someone would want to abandon the RAII idiom and go back to a solution that appears just inferior. Here is a use case. Consider that you have to run three actions in the fixed order:

void runAction1( Resource1 & );
void runAction2( Resource1 &, Resource2 & );
void runAction3( Resource2 & );

Of course, the two resources need to be acquired before using them and released once we are done. This is how we would write it conceptually, if we could assume that none of the operations ever fails or throws exceptions:

Result run3Actions( Parameter param ) // BAD!!
{
  Resource1 res1;
  Resource2 res2;

  res1.acquire(param);       // res1 scope
  runAction1( res1 );        //
  res2.acquire(param);       //    // res2 scope
  runAction2( res1, res2 );  //    //
  res1.release();            //    //
  runAction3( res2 );              //
  res2.release();                  //
}

Note that the scopes of the two resources overlap. we cannot clearly divide our function into nested scopes that correspond to lifetimes of the resources. This example represents a real-life situation if you imagine that runAction2 is a critical operation, and runAction3 (and perhaps runAction1) is logging, which we can even skip if it fails.

Of course, this code is unacceptable, because it does not take into account that acquire operations, and the three actions may fail. So, we want to rewrite run3Actions using RAII idiom. Especially, that the library author that provides the interface to the two resources also knows that RAII is probably always what we want and the only interface he provides are scope guards:

Result run3Actions( Parameter param ) // selfish
{
  Resource1Guard res1{param};
  Resource2Guard res2{param};

  runAction1( res1 );
  runAction2( res1, res2 );   
  runAction3( res2 );
}

This solution is somewhat elegant: you first acquire all resources that you will need, and once you are sure you have them all, you just run the actions. But it has one problem.

In my other post on resource ownership I tried to define the notion of resource. Here, I want to stress one aspect of a resource: someöne else will also need to use resources we are acquiring. If they need it at the moment, but we own the resource right now, they will be locked, or thrown a refusal exception — they will be disturbed and delayed. Using a resource is a critical part of the program, and we should be only acquiring them for as short a period as possible, for the sake of efficiency: not only our program’s but of the entire operating system. In our example above, we hold the ownership of res2 while executing runAction1, which does not require the resource. Similarly, we unnecessarily hold res1 when calling runAction3.

So, how about this?

Result run3Actions( Parameter param ) // slow and risky
{
  {
    Resource1Guard res1{param};
    runAction1( res1 );
  }
  {
    Resource1Guard res1{param};
    Resource2Guard res2{param};
    runAction2( res1, res2 );  
  }  
  {
    Resource2Guard res2{param};
    runAction3( res2 );
  } 
}

It does not have the above-mentioned problem, but it introduces a different one. Now we have to acquire the same resource two times in a row for two subsequent actions. Resource acquisition is critical, may lock us, may slow us down, may fail; calling it four times rather than two is wrong, especially that acquiring the same resource twice may give us different resource properties in each acquisition, and it may not work for our actions.

So the first example, although incorrect (for exception-safety reasons), acquired and released resources in correct places. We need to follow the same pattern, but in exception-safe manner. Scopes are overlapping, so scope guards will not work. We need to be able to defer resource acquisition or release one resource before it is destroyed. But because the library provider has only given us the guard-like interface, we need to introduce the desired null-state ourselves with Boost.Optional.

Result run3Actions( Parameter param ) // not quite there yet
{
  boost::optional<Resource2Guard> res2;   // null-state
  boost::optional<Resource1Guard> res1{ Resource1Guard{param} };  // res 1 scope
                                                                  //
  runAction1( *res1 );                                            //
  res2 = Resource2Guard{param};           // res2 scope           //
  runAction2( *res1, *res2 );             //                      //
  res1 = boost::none;                     //                      //
  runAction3( *res2 );                    //
}

And we are almost there. Now we optimally acquire and release resources (assigning value none correctly releases a resource). Note also that we do not explicitly check if optional resources have been initialized before using them. This is because we can clearly see that they are initialized. The only remaining problem is that this code does not compile. This is because copy-initialization and assignment of optional<T> uses copy-initialization and assignment of T; and guard classes are non-copyable (and not even moveable) and do not (and must not) provide these operations. What we need is an in-place construction of the resources, similar to placement-new syntax or the functionality offered by member function emplace in C++11 containers. Because Boost needs to work with C++03 compilers, it cannot offer this functionality. Instead, there is an another utility in Boost that will enable the in-place construction:

Result run3Actions( Parameter param ) // good!
{
  boost::optional<Resource2Guard> res2; 
  boost::optional<Resource1Guard> res1{ boost::in_place(param) }; 

  runAction1( *res1 );
  res2 = boost::in_place(param);
  runAction2( *res1, *res2 );
  res1 = boost::none;
  runAction3( *res2 );
}

We can still improve our function a bit. We could not implement it with guards, because there were two overlapping scopes. We removed both scopes, but in fact we could leave one scope out and replace one optional with a regular guard. We can add a scope for either res1 or res2. Let’s pick res1:

Result run3Actions( Parameter param ) // even better!
{
  boost::optional<Resource2Guard> res2;
  {                                // res1 scope
    Resource1Guard res1{ param };  //
    runAction1( res1 );            //
    res2 = boost::in_place(param); //           // res2 scope
    runAction2( res1, *res2 );     //           //
  }                                //           //
  runAction3( *res2 );                          //
}

This solution is even better because we are not using res1 with a null-state where we do not need a null-state. And, as we already told, enabling null-state comes with the risk of entering an undefined behaviour.

Note that we can achieve the same result without boost::optional by using free store allocation and a smart pointer:

Result run3Actions( Parameter param ) // slower
{
  std::unique_ptr<Resource2Guard> res2;
  {                                          // res1 scope
    Resource1Guard res1{ param };            //
    runAction1( res1 );                      //
    res2.reset( new Resource2Guard{param} ); //       // res2 scope
    runAction2( res1, *res2 );               //       //
  }                                          //       //
  runAction3( *res2 );                                //
}

However this solution suffers from efficiency. Free store (heap) allocation may be really slow compared to stack allocation guaranteed by boost::optional.

And thus we shifted the topic from the concept of null-state to Fernando Cacciola’s Boost.Optional library. In fact, this is the Boost library that I use most often in my work.

This entry was posted in programming and tagged , . Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s