written by michael lowell roberts.
in C++, exception handling provides a mechanism that makes error reporting and handling more convenient for the programmer than simple return codes allow. a catch block is a control structure, which unfortunately forces the programmer to resort to copypasta when describing common reactions to anticipated exceptions. this practice is unmanageable. furthermore, it discourages the placement of try
...catch
blocks where they would be appropriate.
failure policies are a means by which the programmer can define a vocabulary for how to react to an exception anticipated in a given situation.
failure policies, as implemented in the nyan library, require the boost metaprogramming library to compile.
a failure policy is represented as a class with the following members defined:
- a protocol declaration.
- a set of response methods.
the protocol
member is a typelist (boost::mpl::vector
) that describes which exception types the policy understands. the types are expected to be listed in order from most specific to least specific.
for example, the following definition of protocol
first considers exceptions of type nyan::fail
, then of type std::exception
:
typedef boost::mpl::vector< nyan::fail, std::exception > protocol;
this is equivalent to specifying the following series of catch
statements:
try
{
...
}
catch (const nyan::fail &e)
{
...
}
catch (const std::exception &e)
{
...
}
no equivalent to catch (...)
is supported.
the failure policy class should include a response method for each type declared in the protocol
typelist. each response method is represented with an overload of operator()
that conforms to the following signature:
void operator()(const T &fail_arg) const;
wherever the policy is applied to a function call, if exception T is caught, it is dispatched to the corresponding response method.
the following is an example of a failure policy declaration that understands how to respond to nyan::fail
and std::exception
exceptions:
class fail_policy
{
public:
typedef boost::mpl::vector< nyan::fail, std::exception > protocol;
void operator()(const std::exception &fail_arg) const;
void operator()(const nyan::fail &fail_arg) const;
};
failure policies, in the nyan library, are applied using the nyan::apply_fail_policy()
method.
the following code will invoke the response method defined in the example associated with std::exception
:
#include <nyan/fail.hpp>
#include <stdexcept>
void foo(int x)
{
if (0 == x)
throw std::runtime_error("oops.");
}
int main()
{
if (nyan::apply_fail_policy(nyan::fail_policy(), foo, 0))
return 1;
else
return 0;
}
nyan::apply_fail_policy()
returns true
if an exception was caught. this gives an enclosing loop the ability to terminate, should it see fit to do so.
the advantage of this approach is that a programmer can define a policy once and apply it throughout the source code simply by referring to it by name. the result is reasonably maintainable, since a change in policy propagates throughout the rest of the source code effortlessly.
consider the case of a destructor, which is not permitted to throw exceptions:
void object::finalize()
{
// finalization code.
}
object::~object()
{
(void)nyan::apply_fail_policy(destructor_fail_policy(),
std::mem_fun(&object::finalize), this);
}
ordinarily, checking for anticipated exceptions within each destructor would require considerable copy-and-paste coding. most programmers, understandably, would not bother with such an endeavor. additionally, the code would not be adaptable to the needs of new situations without a continuous effort. this is a reasonable undertaking, however, when using the failure policy abstraction.
failure-policies.markdown.
copyright (c) 2011, michael lowell roberts.
licensed under the creative commons attribution 3.0 unported license.
all rights reserved.
##fmrl