More C++ Idioms/Construction Tracker

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

Construction Tracker

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 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

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 comma 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 comma 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

References

edit