More C++ Idioms/Construction Tracker

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

Construction Tracker
[edit | edit source]

Intent[edit | edit source]

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 | edit source]

Construction Tracker

Motivation[edit | edit source]

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 becomes 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 | edit source]

Construction Tracker idiom uses a simple technique to track successful construction of objects in the initialization list. A counter is simply incremented as constructors of objects finish successfully one-by-one. It cleverly uses the 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 or C does not take parameters, then an adapter class needs to be written, such that the adapter class accepts a dummy parameter and calls the default constructors of B and C. Such an adapter can be written using More C++ Idioms/Parameterized Base Class idiom using the mixin-from-below technique. The adapter class can also be completely encapsulated inside class A. In the conconstructor class A, the tracker parameter has a default value, and therefore it does not bother the user.

Known Uses[edit | edit source]

Related Idioms[edit | edit source]

References[edit | edit source]