More C++ Idioms/Construction Tracker

From Wikibooks, open books for an open world
Jump to: navigation, search

Construction Tracker[edit]

Intent[edit]

To identify the data member that throws an exception when initialization of two or more objects in the constructor's initialization list can throw the same exception type

Also Known As[edit]

Motivation[edit]

When two or more objects are initialized in a constructor's initialization list and all of them can throw the same exception (std::exception), tracking which one of them failed become a tricky issue as there can be only one try block surrounding the initialization list. Such a try block has a special name called 'constructor try block', which is nothing but a 'function-try block'.

Solution and Sample Code[edit]

Construction Tracker idiom uses a simple technique to track successful construction on objects in the initialization list. A counter is simply incremented as constructors of objects finish successfully one-by-one. It cleverly uses bracket operator to inject the counter increments in between calls to the constructors all being invisible to the user of the class.

#include <iostream>
#include <stdexcept>
#include <cassert>
 
struct B {
    B (char const *) { throw std::runtime_error("B Error"); }
};
struct C {
    C (char const *) { throw std::runtime_error("C Error"); }
};
class A {
   B b_;
   C c_;
   enum TrackerType { NONE, ONE, TWO };
public:
   A( TrackerType tracker = NONE)
   try    // A constructor try block.
     : b_((tracker = ONE, "hello")) // Can throw std::exception
     , c_((tracker = TWO, "world")) // Can throw std::exception
     {
        assert(tracker == TWO);
        // ... constructor body ...
     }
   catch (std::exception const & e)
     {
        if (tracker == ONE) {
           std::cout << "B threw: " << e.what() << std::endl;
        }
        else if (tracker == TWO) {
           std::cout << "C threw: " << e.what() << std::endl;
        }
        throw;
     }
};
 
int main (void) 
{
    try {
        A a;
    }
    catch (std::exception const & e) {
          std::cout << "Caught: " << e.what() << std::endl;
    }
    return 0;       
}

The double parentheses is how the bracket operator is used to place in the assignment to the tracker. This idiom critically depends upon the constructor of B and C taking at least one parameter. If class B and C does not take parameters, then an adapter class needs to be written such that it the adapter class will accept a dummy parameter and calling the default parameters of B and C. Such an adapter can be written using More C++ Idioms/Parameterized Base Class idiom using mixin-from-below technique. The adapter class can also be completely encapsulated inside class A. In the consturctor of class A, the tracker parameter has a default value and therefore it does no bother the user.

Known Uses[edit]

Related Idioms[edit]

References[edit]