SlideShare a Scribd company logo
N
O MATTER HOW GOOD OUR INTENTIONS
are, bad things happen. This is as true of
programs at runtime as it is in everyday life.
The reality differentiates industrial-strength
code from simple, toy code examples. But handling
the bad along with the good is where things can get
ugly.There’s always more that can go wrong than can
go right, so traditional error handling often chops up
and obscures normal logic, making it hard to trace the
original intent.
The intricacy of error handling has sometimes
driven programmers blindly into the arms of the
goto, which has an effect on code similar to that of the
mile-a-minute plant on gardens.To those who are not
forewarned, it initially looks harmless enough, but before
long it’s completely taken over. And it takes more than
a pair of garden shears to disentangle either your
code or your garden.
This is where exceptions fit in. C++’s exception
handling mechanism is a marriage between its object
model and its control-flow features. It supports a
separation between the normal flow of control (the
try block) and the handling of the abnormal and
abominable (the catch handlers). It also simplifies and
broadens the communication of the bad news (the
throw), just as a goto does not. It’s true that exceptions
inflict a certain amount of violence on the normal,
gentle flow of control, but they do so sympathetically.
Exceptions support rather than obstruct the block
structure and modularity of your code, whereas “a goto
completely invalidates the high-level structure of the
code”1
, making it difficult to read, refactor or rescue.
Exceptions also have a dark side. If code isn’t
written to be exception-safe, an exception will lead
to graceless rather than graceful failure – the act of deliv-
ering bad news creates worse news. Code that releas-
es a previously acquired resource, for example, should
be executed regardless of the route out of a function.
Code not written with exceptions in mind normal-
ly fails to do the right thing when they happen.
An object finale
The common focus of exception handling is on sep-
arating the normal and exceptional paths. In contrast,
exception safety focuses on identifying code that’s com-
mon to both paths. In the case of paired actions, such
as acquire–release or open–close, an exception-safe
approach ensures the bracketing action is always exe-
cuted. Consider the following code:
{
resource_type resource = acquire();
.... // use resource, exceptions possible
release(resource);
}
There are two things to notice about this. It’s brief
and clear, and it’s unsafe, with the release action
bypassed if an exception is thrown in the body of the
block. The first instinct of many programmers is to
make the code exception-aware in order to make it safe:
{
resource_type resource = acquire();
try
{
.... // use resource, exceptions possible
}
catch(...)
{
release(resource);
58 APPLICATION DEVELOPMENT ADVISOR q www.appdevadvisor.com
Kevlin Henney is an
independent software
development consultant
and trainer.He can be
reached at
www.curbralan.com
EFFECTIVE C++
q C++’s exception handling mechanism
respects a program’s modularity when it
transfers control
q Exceptions can cause problems if code is
not exception safe
q Some languages use a finally block to
allow clean up of resources on block exit
q The C++ idiom is to use objects to
encapsulate actions rather than extend
the language itself
q A simple, extensible class template can
be written to handle many common
clean-up tasks
FACTS AT A GLANCE
Exception handling in C++ can save your program from digital death, but
it must be treated with care. Kevlin Henney explains how to make your
programs exception-safe
Making an exception
60 APPLICATION DEVELOPMENT ADVISOR q www.appdevadvisor.com
EFFECTIVE C++
throw; // rethrow so caller is aware of failure
}
release(resource);
}
There are three things to notice about this code: it’s verbose,
exception-safe and, because of its verbosity, it’s not immediately obvious
that it’s exception-safe. It’s not enough that code should be
exception-safe, either – it should also be clear that it’s exception-
safe. The code above demonstrates a simple scenario with a single
resource and single release action. Include more of either and it looks
like the mile-a-minute has returned to take over your code.
And finally...
Some languages that adopt an exception handling mechanism like
C++’s also introduce a finally block. For example, Java and C# work
this way. A finally block groups code common to both execution
paths into a single block. C++ doesn’t have finally, but if it did, it
would look something like the following:
{
resource_type resource = acquire();
try
{
.... // use resource, exceptions possible
}
finally // not legal C++
{
release(resource);
}
}
This is clearly briefer and briefly clearer than the previous,
tortured code fragment. Is it an oversight that C++ doesn’t support
such a construct? On the contrary, it’s by design2
. Although finally
reduces the amount of code duplication within a single function,
it still bloats the source of the function and often leads to code
duplication when the resource type is used in many places.The same
finally and release code are repeated everywhere the resource is used.
C++ in addition has a feature that neither Java nor C# supports
– deterministic object lifetime and destruction. An ordinary
local variable will have its destructor called when it goes out of
scope, both for normal exit and as the stack unwinds from an excep-
tion. Destructors are not just for cleaning up the resources used by
the destructing object, they can be used to clean up other objects’
resources as well.
Thanks for the memory
The most common example of this idiom is in managing
memory that is allocated only for a block, although it can also
be used to automate lifetime management for nested objects.
This is one of the roles that the standard auto_ptr class template is
designed to cover:
{
std::auto_ptr<type> ptr(new type);
.... // use ptr, exceptions possible
}
As opposed to:
{
type *ptr = new type;
try
{
.... // use ptr, exceptions possible
}
catch(...)
{
delete ptr;
throw;
}
delete ptr;
}
auto_ptr is clearly a winner in this department. However, beyond
this simple scenario, it’s let down by some rather messy semantics.
auto_ptr’s specification passes all understanding and defies imple-
mentation. It’s clear from the scenario already outlined that such
acquisition objects should not be copyable, as this would lead to
aliasing and attempted multiple deletion of a single object. How-
ever, after a few rounds with standardisation politics, what was once
a simple class that could be implemented by a C++ novice grew into
a monster that supported move rather than copy semantics for its
copy operations – a handful of C++ experts, and not everyone involved
with the standardisation, can implement it. If you want surprising
semantics, consider the following:
{
std::auto_ptr<type> ptr(new type), qtr;
qtr = ptr;
....
}
There is an expectation that assignment leaves the right-hand side
of the assignment unaffected. Alas, auto_ptr fails to live up to expec-
tations: the right-hand side is set to null. While such transfer
functionality is sometimes useful, it isn’t copy assignment. Sadly,
this is the tip of the iceberg with this class template’s problems.
Let’s go back to first principles, see what’s involved in designing
such a class for its original use in simplifying block-scoped memory
management, and then generalise it to other resources while
retaining its essential simplicity. To bind object deletion to block
scope, we need a constructor to be told about the object, a destruc-
tor to destroy it and no copying operations (because that way lies
madness, darkness and the shadow of std::auto_ptr):
template<typename resource_type>
class scoped
{
public: // structors
explicit scoped(resource_type resourced)
: resource(resourced)
{
}
~scoped()
{
delete resource;
}
The solution to the exception-safety problem is to give custody of the
resource to a locally scoped object which then performs the clean-up on
destruction.This solution is a more object-oriented one: the control flow
is abstracted into an object whose responsibility is clear and whose
action is reusable.The resulting code is exception-neutral, meaning that
it’s safe, but not cluttered with try blocks of any kind, and requires no
extension to the language.The technique is well known and well
documented in a number of forms. It is commonly known as the
RESOURCE ACQUISITION IS INITIALIZATION idiom3
, although this name is
something of a misnomer: the idiom has nothing to do with initialisation
and everything to do with finalisation.
MAY 2001 61
EFFECTIVE C++
private: // prevention
scoped(const scoped &);
scoped &operator=(const scoped &);
private: // representation
resource_type resource;
};
scoped is simple in both implementation and use:
{
scoped<type *> ptr(new type);
....
}
A change of policy
The precondition to using scoped as it currently stands is that the
pointer must either be null or have been allocated using new. If you’re
using a different allocator, such as malloc or new[], then delete is the
wrong deallocation. Instead, you need free and delete[] respectively.
Rather than write a separate class template for each possibility, it
would be simpler to allow scoped to be parameterised by a destruc-
tion policy. A policy class describes a STRATEGY4
that can be used
as a template parameter to control some aspect of the template’s
behaviour. By default, we tend to work with single objects rather
than arrays, which suggests the following usage:
{
scoped<type *> ptr(new type);
scoped<type *, array_deleter> array(new type[size]);
....
}
Treating the policy as a function object type means we get the
desired flexibility, a simple syntax to use inside scoped, and the pos-
sibility of passing in a policy object that has its own state. Here is
the revised scoped template:
template<
typename resource_type, typename destructor = deleter>
class scoped
{
public: // structors
explicit scoped(
resource_type resourced,
destructor on_destruct = destructor())
: resource(resourced), destruct(on_destruct)
{
}
~scoped()
{
destruct(resource);
}
private: // prevention
scoped(const scoped &);
scoped &operator=(const scoped &);
private: // representation
resource_type resource;
destructor destruct;
};
The default object deletion policy and function object type are
simple:
struct deleter
{
template<typename deleted_type>
void operator()(deleted_type to_delete)
{
delete to_delete;
}
};
A member template is used so that this simple class will work with
any pointer type.The alternative would be to make the whole class
a template, which would make its use verbose: to use it, you
would have to spell out the class template name and its parame-
ter types, rather than just use the class name and let the compiler
deduce the type from the argument of the function call operator
– deleter<type *> as opposed to deleter.
As a function-object type, deleter can be reused in contexts
other than scoped. For instance, consider the common need to delete
each object pointed to by a container:
std::list<type *> objects;
objects.push_back(new type);
....
Rather than the following, slightly long-winded loop:
for(std::list<type *>::iterator at = objects.begin();
at != objects.end();
++at)
{
delete *at;
}
You can write the following:
std::for_each(objects.begin(), objects.end(), deleter());
Given deleter, array_deleter is quite straightforward:
struct array_deleter
{
template<typename deleted_type>
void operator()(deleted_type to_delete)
{
delete[] to_delete;
}
};
A sense of closure
Not all pointers want deleting. Some have more complex finalisation.
If, for compatibility reasons, you end up using C-style I/O, you may
be using FILE * with fopen and fclose from the C standard library.
In this case, the pointer is ‘allocated’ using fopen and ‘deallocated’
using fclose. We can provide a simple closer function object type
to handle this:
struct closer
{
void operator()(FILE *to_close)
{
fclose(to_close);
}
};
And to use it, scoped needs no change:
{
scoped<FILE *, closer> file(fopen(name, mode));
....
}
62 APPLICATION DEVELOPMENT ADVISOR q www.appdevadvisor.com
EFFECTIVE C++
Now, the way that I have written the scoped template allows any
type to be used as a resource, not just pointers. For instance, you
can use scoped with an int. An int? Why would an int need tidy-
ing up in the event of an exception? In the POSIX API, int is the
type used for file handles. So, although the int itself does not need
any extra management, an open needs to be matched with a close.
Given that ‘close’ is the most common name for any close action,
we can generalise the closer function object type beyond just FILE
* and int:
struct closer
{
template<typename closed_type>
void operator()(closed_type to_close)
{
close(to_close);
}
void operator()(FILE *to_close)
{
fclose(to_close);
}
};
This now takes the appropriate action depending on the type of
the resource:
{
scoped<FILE *, closer> file(fopen(name, mode));
....
} // fclose called
{
scoped<int, closer> file(open(name, flags));
....
} // close called
Getting a result
As it stands, the scoped template is simple, flexible – and almost
useless.The only public interface it currently sports is a constructor
and a destructor. This is something of a write-only interface:
there’s no way to get your hands on the actual resource to use it.
We can provide a simple outward conversion with a user-
defined conversion operator:
template<
typename resource_type, typename destructor = deleter>
class scoped
{
....
operator resource_type() const
{
return resource;
}
....
};
For the common case of just getting your hands on the value of
the resource, the implicit conversion makes the use of scoped
practically transparent:
{
scoped<FILE *, closer> file(fopen(name, mode));
....
fflush(file); // implicit conversion to FILE *
....
}
Except for pointers. For instance, auto_ptr, like most other
smart pointers, supports operator* and operator-> for dereferencing.
The arrow operator is easy enough:
template<
typename resource_type, typename destructor = deleter>
class scoped
{
....
resource_type operator->() const
{
return resource;
}
....
};
If resource_type is a pointer, or another class that supports its own
operator->, the operator-> just defined can be used. If not, any attempt
to use it will result in a compile time error. So the code is valid when
it’s supposed to be, and isn’t when it isn’t, which is just the level of
type checking we want.
operator* presents us with a bit more of a challenge: what’s the
return type in the case of a pointer-based resource, and how do we
prevent it being used with non-pointer resources? How do we get
from resource_type to the type of *resource? C++ doesn’t support
a typeof operator, which would make this trivial, so we resolve the
situation using theTRAITS idiom. A trait class allows compile-time
deduction of features of another type based on template speciali-
sation. We are going to define a simple, single-feature trait that allows
us to deduce a legal result type for operator*:
template<
typename resource_type, typename destructor = deleter>
class scoped
{
....
dereferenced<resource_type>::type operator*() const
{
return *resource;
}
....
};
The dereferenced trait class has a nested typedef that resolves to
the right type. For non-pointer types, operator* is unusable because
the *resource expression isn’t legal. Template functions aren’t fully
checked and compiled unless they are used, but they still need to
have legal signatures. In this case, a return type of void will do the
trick. So, the default implementation for dereferenced does just that:
template<typename value_type>
struct dereferenced
{
typedef void type;
};
For pointer types, we can use partial specialisation to offer the
dereferenced result type we are expecting:
template<typename element_type>
struct dereferenced<element_type *>
{
typedef element_type &type;
};
This specialisation effectively overrides the primary template for
all types that match form T *, and which dereferenced would there-
MAY 2001
fore be T &. The only other case we need to specialise for is that
of void *. There’s no such thing, however, as a reference to void.
In the event that we are working at a low level, we may find our-
selves working with raw memory, but we still want the safety and
convenience of scoped at our disposal. At the moment, the com-
piler will reject any attempt to use scoped<void *>, as indeed it
does for auto_ptr<void>, because the type void & isn’t well formed.
We can add a full specialisation for this case, silencing the com-
piler and disabling use of operator* while still offering the core
scoped features:
template<>
struct dereferenced<void *>
{
typedef void type;
};
There are many benefits to the traits-based approach, not
least of which is that it makes the scoped template’s operator* work!
Another is that traits are open to further specialisation by other
users. For instance, if you have your own smart pointer class that
you would like to use with scoped, you could specialise derefer-
erenced accordingly.
Exceptions are a simple mechanism that can be used to send
bad news from one part of a program to another. They can also
be used to make bad news if the programmer isn’t aware of a hand-
ful of simple exception-safe practices. Rather than introducing
a finally block to guarantee resource release, C++ encourages a more
object-oriented approach that abstracts control flow and reduces
code duplication. Exception-neutral code that uses this technique
is safer and more concise than exception-aware code that labo-
riously addresses each outcome in turn. The simple principles and
techniques described in this article can be taken to quite an
advanced level5,6
.
auto_ptr is useful for the simple task of scope-managed mem-
ory, but it doesn’t generalise beyond delete of new-allocated
objects. It is further complicated for both the user and the stan-
dard library implementor by its move semantics for copying. So,
if nothing else is available, use auto_ptr. However, you might want
to experiment with other libraries, such as Boost7
, or the scoped
template presented here. The only feature currently missing
from scoped is the ability to transfer ownership of a resource: this
is left as an exercise for the reader. s
References
1. Taligent’s Guide to Designing Programs: Well-Mannered
Object-Oriented Design in C++, Addison-Wesley, 1994
http://pcroot.cern.ch/TaligentDocs/TaligentOnline/Docu-
mentRoot/1.0/Docs/books/WM/WM_1.html.
2. Bjarne Stroustrup, The Design and Evolution of C++,
Addison-Wesley, 1994
3. Bjarne Stroustrup, The C++ Programming Language, Third
edition, Addison-Wesley, 1997
4. Erich Gamma, Richard Helm, Ralph Johnson and John
Vlissides, Design Patterns: Elements of Reusable Object-
Oriented Software, Addison-Wesley, 1995
5. Kevlin Henney, “C++ Patterns: Executing Around
Sequences”, EuroPLoP 2000, July 2000,
www.curbralan.com.
6. Andrei Alexandrescu and Petru Marginean, “Generic<Pro-
gramming>: Simplifying Your Exception-Safe Code”,
C/C++ Users Journal web site, December 2000,
www.cuj.com/experts/1812/alexandr.htm.
7. Boost library web site, www.boost.org.

More Related Content

What's hot

PVS-Studio: analyzing ReactOS's code
PVS-Studio: analyzing ReactOS's codePVS-Studio: analyzing ReactOS's code
PVS-Studio: analyzing ReactOS's code
Andrey Karpov
 
Insecure coding in C (and C++)
Insecure coding in C (and C++)Insecure coding in C (and C++)
Insecure coding in C (and C++)
Olve Maudal
 
Deep C
Deep CDeep C
Deep C
Olve Maudal
 
Detecting aspect-specific code smells using Ekeko for AspectJ
Detecting aspect-specific code smells using Ekeko for AspectJDetecting aspect-specific code smells using Ekeko for AspectJ
Detecting aspect-specific code smells using Ekeko for AspectJ
Coen De Roover
 
Intro Oop
Intro OopIntro Oop
Intro Oop
Kamatchi Selvam
 
Ekeko Technology Showdown at SoTeSoLa 2012
Ekeko Technology Showdown at SoTeSoLa 2012Ekeko Technology Showdown at SoTeSoLa 2012
Ekeko Technology Showdown at SoTeSoLa 2012Coen De Roover
 
A Logic Meta-Programming Foundation for Example-Driven Pattern Detection in O...
A Logic Meta-Programming Foundation for Example-Driven Pattern Detection in O...A Logic Meta-Programming Foundation for Example-Driven Pattern Detection in O...
A Logic Meta-Programming Foundation for Example-Driven Pattern Detection in O...
Coen De Roover
 
Cursus phpunit
Cursus phpunitCursus phpunit
Cursus phpunit
Nick Belhomme
 
Java căn bản - Chapter8
Java căn bản - Chapter8Java căn bản - Chapter8
Java căn bản - Chapter8Vince Vo
 
Java - Concurrent programming - Thread's advanced concepts
Java - Concurrent programming - Thread's advanced conceptsJava - Concurrent programming - Thread's advanced concepts
Java - Concurrent programming - Thread's advanced concepts
Riccardo Cardin
 
Chapter 2.4
Chapter 2.4Chapter 2.4
Chapter 2.4sotlsoc
 
A Recommender System for Refining Ekeko/X Transformation
A Recommender System for Refining Ekeko/X TransformationA Recommender System for Refining Ekeko/X Transformation
A Recommender System for Refining Ekeko/X Transformation
Coen De Roover
 
Advanced programming topics asma
Advanced programming topics asmaAdvanced programming topics asma
Advanced programming topics asma
AbdullahJana
 
Unit testing with PHPUnit - there's life outside of TDD
Unit testing with PHPUnit - there's life outside of TDDUnit testing with PHPUnit - there's life outside of TDD
Unit testing with PHPUnit - there's life outside of TDD
Paweł Michalik
 
The SOUL Tool Suite for Querying Programs in Symbiosis with Eclipse
The SOUL Tool Suite for Querying Programs in Symbiosis with EclipseThe SOUL Tool Suite for Querying Programs in Symbiosis with Eclipse
The SOUL Tool Suite for Querying Programs in Symbiosis with Eclipse
Coen De Roover
 
javasebeyondbasics
javasebeyondbasicsjavasebeyondbasics
javasebeyondbasicswebuploader
 
Java Faqs useful for freshers and experienced
Java Faqs useful for freshers and experiencedJava Faqs useful for freshers and experienced
Java Faqs useful for freshers and experiencedyearninginjava
 
Eclipse Code Recommenders @ MAJUG 2011
Eclipse Code Recommenders @ MAJUG 2011Eclipse Code Recommenders @ MAJUG 2011
Eclipse Code Recommenders @ MAJUG 2011Marcel Bruch
 
Clean code and Code Smells
Clean code and Code SmellsClean code and Code Smells
Clean code and Code Smells
Mario Sangiorgio
 

What's hot (20)

PVS-Studio: analyzing ReactOS's code
PVS-Studio: analyzing ReactOS's codePVS-Studio: analyzing ReactOS's code
PVS-Studio: analyzing ReactOS's code
 
Insecure coding in C (and C++)
Insecure coding in C (and C++)Insecure coding in C (and C++)
Insecure coding in C (and C++)
 
Deep C
Deep CDeep C
Deep C
 
Detecting aspect-specific code smells using Ekeko for AspectJ
Detecting aspect-specific code smells using Ekeko for AspectJDetecting aspect-specific code smells using Ekeko for AspectJ
Detecting aspect-specific code smells using Ekeko for AspectJ
 
Intro Oop
Intro OopIntro Oop
Intro Oop
 
Ekeko Technology Showdown at SoTeSoLa 2012
Ekeko Technology Showdown at SoTeSoLa 2012Ekeko Technology Showdown at SoTeSoLa 2012
Ekeko Technology Showdown at SoTeSoLa 2012
 
A Logic Meta-Programming Foundation for Example-Driven Pattern Detection in O...
A Logic Meta-Programming Foundation for Example-Driven Pattern Detection in O...A Logic Meta-Programming Foundation for Example-Driven Pattern Detection in O...
A Logic Meta-Programming Foundation for Example-Driven Pattern Detection in O...
 
Cursus phpunit
Cursus phpunitCursus phpunit
Cursus phpunit
 
Java căn bản - Chapter8
Java căn bản - Chapter8Java căn bản - Chapter8
Java căn bản - Chapter8
 
Java - Concurrent programming - Thread's advanced concepts
Java - Concurrent programming - Thread's advanced conceptsJava - Concurrent programming - Thread's advanced concepts
Java - Concurrent programming - Thread's advanced concepts
 
Chapter 2.4
Chapter 2.4Chapter 2.4
Chapter 2.4
 
A Recommender System for Refining Ekeko/X Transformation
A Recommender System for Refining Ekeko/X TransformationA Recommender System for Refining Ekeko/X Transformation
A Recommender System for Refining Ekeko/X Transformation
 
Advanced programming topics asma
Advanced programming topics asmaAdvanced programming topics asma
Advanced programming topics asma
 
Unit testing with PHPUnit - there's life outside of TDD
Unit testing with PHPUnit - there's life outside of TDDUnit testing with PHPUnit - there's life outside of TDD
Unit testing with PHPUnit - there's life outside of TDD
 
The SOUL Tool Suite for Querying Programs in Symbiosis with Eclipse
The SOUL Tool Suite for Querying Programs in Symbiosis with EclipseThe SOUL Tool Suite for Querying Programs in Symbiosis with Eclipse
The SOUL Tool Suite for Querying Programs in Symbiosis with Eclipse
 
Gdd pydp
Gdd pydpGdd pydp
Gdd pydp
 
javasebeyondbasics
javasebeyondbasicsjavasebeyondbasics
javasebeyondbasics
 
Java Faqs useful for freshers and experienced
Java Faqs useful for freshers and experiencedJava Faqs useful for freshers and experienced
Java Faqs useful for freshers and experienced
 
Eclipse Code Recommenders @ MAJUG 2011
Eclipse Code Recommenders @ MAJUG 2011Eclipse Code Recommenders @ MAJUG 2011
Eclipse Code Recommenders @ MAJUG 2011
 
Clean code and Code Smells
Clean code and Code SmellsClean code and Code Smells
Clean code and Code Smells
 

Similar to Making an Exception

The Rest of the Best
The Rest of the BestThe Rest of the Best
The Rest of the Best
Kevlin Henney
 
What
WhatWhat
Whatanity
 
Best Coding Practices For Android Application Development
Best Coding Practices For Android Application DevelopmentBest Coding Practices For Android Application Development
Best Coding Practices For Android Application Development
Ketan Raval
 
Lecture 1 Try Throw Catch.pptx
Lecture 1 Try Throw Catch.pptxLecture 1 Try Throw Catch.pptx
Lecture 1 Try Throw Catch.pptx
VishuSaini22
 
Oops And C++ Fundamentals
Oops And C++ FundamentalsOops And C++ Fundamentals
Oops And C++ Fundamentals
Subhasis Nayak
 
4 pillars of OOPS CONCEPT
4 pillars of OOPS CONCEPT4 pillars of OOPS CONCEPT
4 pillars of OOPS CONCEPT
Ajay Chimmani
 
【Unite 2017 Tokyo】パフォーマンス向上のためのスクリプトのベストプラクティス(note付き)
【Unite 2017 Tokyo】パフォーマンス向上のためのスクリプトのベストプラクティス(note付き)【Unite 2017 Tokyo】パフォーマンス向上のためのスクリプトのベストプラクティス(note付き)
【Unite 2017 Tokyo】パフォーマンス向上のためのスクリプトのベストプラクティス(note付き)
Unity Technologies Japan K.K.
 
Lecture 3.1.1 Try Throw Catch.pptx
Lecture 3.1.1 Try Throw Catch.pptxLecture 3.1.1 Try Throw Catch.pptx
Lecture 3.1.1 Try Throw Catch.pptx
sunilsoni446112
 
02 c++g3 d
02 c++g3 d02 c++g3 d
02 c++g3 d
mahago
 
If I Had a Hammer...
If I Had a Hammer...If I Had a Hammer...
If I Had a Hammer...
Kevlin Henney
 
6-Error Handling.pptx
6-Error Handling.pptx6-Error Handling.pptx
6-Error Handling.pptx
amiralicomsats3
 
Binary code obfuscation through c++ template meta programming
Binary code obfuscation through c++ template meta programmingBinary code obfuscation through c++ template meta programming
Binary code obfuscation through c++ template meta programmingnong_dan
 
Templates and Exception Handling in C++
Templates and Exception Handling in C++Templates and Exception Handling in C++
Templates and Exception Handling in C++
Nimrita Koul
 
Legacy of Void*
Legacy of Void*Legacy of Void*
Legacy of Void*Adam Crain
 
Extension methods, nulls, namespaces and precedence in c#
Extension methods, nulls, namespaces and precedence in c#Extension methods, nulls, namespaces and precedence in c#
Extension methods, nulls, namespaces and precedence in c#
Paul Houle
 
Extension methods, nulls, namespaces and precedence in c#
Extension methods, nulls, namespaces and precedence in c#Extension methods, nulls, namespaces and precedence in c#
Extension methods, nulls, namespaces and precedence in c#
Paul Houle
 
Yeahhhh the final requirement!!!
Yeahhhh the final requirement!!!Yeahhhh the final requirement!!!
Yeahhhh the final requirement!!!olracoatalub
 
Mark asoi ppt
Mark asoi pptMark asoi ppt
Mark asoi pptmark-asoi
 

Similar to Making an Exception (20)

The Rest of the Best
The Rest of the BestThe Rest of the Best
The Rest of the Best
 
$Cash
$Cash$Cash
$Cash
 
$Cash
$Cash$Cash
$Cash
 
What
WhatWhat
What
 
Best Coding Practices For Android Application Development
Best Coding Practices For Android Application DevelopmentBest Coding Practices For Android Application Development
Best Coding Practices For Android Application Development
 
Lecture 1 Try Throw Catch.pptx
Lecture 1 Try Throw Catch.pptxLecture 1 Try Throw Catch.pptx
Lecture 1 Try Throw Catch.pptx
 
Oops And C++ Fundamentals
Oops And C++ FundamentalsOops And C++ Fundamentals
Oops And C++ Fundamentals
 
4 pillars of OOPS CONCEPT
4 pillars of OOPS CONCEPT4 pillars of OOPS CONCEPT
4 pillars of OOPS CONCEPT
 
【Unite 2017 Tokyo】パフォーマンス向上のためのスクリプトのベストプラクティス(note付き)
【Unite 2017 Tokyo】パフォーマンス向上のためのスクリプトのベストプラクティス(note付き)【Unite 2017 Tokyo】パフォーマンス向上のためのスクリプトのベストプラクティス(note付き)
【Unite 2017 Tokyo】パフォーマンス向上のためのスクリプトのベストプラクティス(note付き)
 
Lecture 3.1.1 Try Throw Catch.pptx
Lecture 3.1.1 Try Throw Catch.pptxLecture 3.1.1 Try Throw Catch.pptx
Lecture 3.1.1 Try Throw Catch.pptx
 
02 c++g3 d
02 c++g3 d02 c++g3 d
02 c++g3 d
 
If I Had a Hammer...
If I Had a Hammer...If I Had a Hammer...
If I Had a Hammer...
 
6-Error Handling.pptx
6-Error Handling.pptx6-Error Handling.pptx
6-Error Handling.pptx
 
Binary code obfuscation through c++ template meta programming
Binary code obfuscation through c++ template meta programmingBinary code obfuscation through c++ template meta programming
Binary code obfuscation through c++ template meta programming
 
Templates and Exception Handling in C++
Templates and Exception Handling in C++Templates and Exception Handling in C++
Templates and Exception Handling in C++
 
Legacy of Void*
Legacy of Void*Legacy of Void*
Legacy of Void*
 
Extension methods, nulls, namespaces and precedence in c#
Extension methods, nulls, namespaces and precedence in c#Extension methods, nulls, namespaces and precedence in c#
Extension methods, nulls, namespaces and precedence in c#
 
Extension methods, nulls, namespaces and precedence in c#
Extension methods, nulls, namespaces and precedence in c#Extension methods, nulls, namespaces and precedence in c#
Extension methods, nulls, namespaces and precedence in c#
 
Yeahhhh the final requirement!!!
Yeahhhh the final requirement!!!Yeahhhh the final requirement!!!
Yeahhhh the final requirement!!!
 
Mark asoi ppt
Mark asoi pptMark asoi ppt
Mark asoi ppt
 

More from Kevlin Henney

Program with GUTs
Program with GUTsProgram with GUTs
Program with GUTs
Kevlin Henney
 
The Case for Technical Excellence
The Case for Technical ExcellenceThe Case for Technical Excellence
The Case for Technical Excellence
Kevlin Henney
 
Empirical Development
Empirical DevelopmentEmpirical Development
Empirical Development
Kevlin Henney
 
Lambda? You Keep Using that Letter
Lambda? You Keep Using that LetterLambda? You Keep Using that Letter
Lambda? You Keep Using that Letter
Kevlin Henney
 
Lambda? You Keep Using that Letter
Lambda? You Keep Using that LetterLambda? You Keep Using that Letter
Lambda? You Keep Using that Letter
Kevlin Henney
 
Solid Deconstruction
Solid DeconstructionSolid Deconstruction
Solid Deconstruction
Kevlin Henney
 
Get Kata
Get KataGet Kata
Get Kata
Kevlin Henney
 
Procedural Programming: It’s Back? It Never Went Away
Procedural Programming: It’s Back? It Never Went AwayProcedural Programming: It’s Back? It Never Went Away
Procedural Programming: It’s Back? It Never Went Away
Kevlin Henney
 
Structure and Interpretation of Test Cases
Structure and Interpretation of Test CasesStructure and Interpretation of Test Cases
Structure and Interpretation of Test Cases
Kevlin Henney
 
Agility ≠ Speed
Agility ≠ SpeedAgility ≠ Speed
Agility ≠ Speed
Kevlin Henney
 
Refactoring to Immutability
Refactoring to ImmutabilityRefactoring to Immutability
Refactoring to Immutability
Kevlin Henney
 
Old Is the New New
Old Is the New NewOld Is the New New
Old Is the New New
Kevlin Henney
 
Turning Development Outside-In
Turning Development Outside-InTurning Development Outside-In
Turning Development Outside-In
Kevlin Henney
 
Giving Code a Good Name
Giving Code a Good NameGiving Code a Good Name
Giving Code a Good Name
Kevlin Henney
 
Clean Coders Hate What Happens To Your Code When You Use These Enterprise Pro...
Clean Coders Hate What Happens To Your Code When You Use These Enterprise Pro...Clean Coders Hate What Happens To Your Code When You Use These Enterprise Pro...
Clean Coders Hate What Happens To Your Code When You Use These Enterprise Pro...
Kevlin Henney
 
Thinking Outside the Synchronisation Quadrant
Thinking Outside the Synchronisation QuadrantThinking Outside the Synchronisation Quadrant
Thinking Outside the Synchronisation Quadrant
Kevlin Henney
 
Code as Risk
Code as RiskCode as Risk
Code as Risk
Kevlin Henney
 
Software Is Details
Software Is DetailsSoftware Is Details
Software Is Details
Kevlin Henney
 
Game of Sprints
Game of SprintsGame of Sprints
Game of Sprints
Kevlin Henney
 
Good Code
Good CodeGood Code
Good Code
Kevlin Henney
 

More from Kevlin Henney (20)

Program with GUTs
Program with GUTsProgram with GUTs
Program with GUTs
 
The Case for Technical Excellence
The Case for Technical ExcellenceThe Case for Technical Excellence
The Case for Technical Excellence
 
Empirical Development
Empirical DevelopmentEmpirical Development
Empirical Development
 
Lambda? You Keep Using that Letter
Lambda? You Keep Using that LetterLambda? You Keep Using that Letter
Lambda? You Keep Using that Letter
 
Lambda? You Keep Using that Letter
Lambda? You Keep Using that LetterLambda? You Keep Using that Letter
Lambda? You Keep Using that Letter
 
Solid Deconstruction
Solid DeconstructionSolid Deconstruction
Solid Deconstruction
 
Get Kata
Get KataGet Kata
Get Kata
 
Procedural Programming: It’s Back? It Never Went Away
Procedural Programming: It’s Back? It Never Went AwayProcedural Programming: It’s Back? It Never Went Away
Procedural Programming: It’s Back? It Never Went Away
 
Structure and Interpretation of Test Cases
Structure and Interpretation of Test CasesStructure and Interpretation of Test Cases
Structure and Interpretation of Test Cases
 
Agility ≠ Speed
Agility ≠ SpeedAgility ≠ Speed
Agility ≠ Speed
 
Refactoring to Immutability
Refactoring to ImmutabilityRefactoring to Immutability
Refactoring to Immutability
 
Old Is the New New
Old Is the New NewOld Is the New New
Old Is the New New
 
Turning Development Outside-In
Turning Development Outside-InTurning Development Outside-In
Turning Development Outside-In
 
Giving Code a Good Name
Giving Code a Good NameGiving Code a Good Name
Giving Code a Good Name
 
Clean Coders Hate What Happens To Your Code When You Use These Enterprise Pro...
Clean Coders Hate What Happens To Your Code When You Use These Enterprise Pro...Clean Coders Hate What Happens To Your Code When You Use These Enterprise Pro...
Clean Coders Hate What Happens To Your Code When You Use These Enterprise Pro...
 
Thinking Outside the Synchronisation Quadrant
Thinking Outside the Synchronisation QuadrantThinking Outside the Synchronisation Quadrant
Thinking Outside the Synchronisation Quadrant
 
Code as Risk
Code as RiskCode as Risk
Code as Risk
 
Software Is Details
Software Is DetailsSoftware Is Details
Software Is Details
 
Game of Sprints
Game of SprintsGame of Sprints
Game of Sprints
 
Good Code
Good CodeGood Code
Good Code
 

Recently uploaded

openEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain SecurityopenEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain Security
Shane Coughlan
 
Developing Distributed High-performance Computing Capabilities of an Open Sci...
Developing Distributed High-performance Computing Capabilities of an Open Sci...Developing Distributed High-performance Computing Capabilities of an Open Sci...
Developing Distributed High-performance Computing Capabilities of an Open Sci...
Globus
 
Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024
Paco van Beckhoven
 
Globus Connect Server Deep Dive - GlobusWorld 2024
Globus Connect Server Deep Dive - GlobusWorld 2024Globus Connect Server Deep Dive - GlobusWorld 2024
Globus Connect Server Deep Dive - GlobusWorld 2024
Globus
 
Atelier - Innover avec l’IA Générative et les graphes de connaissances
Atelier - Innover avec l’IA Générative et les graphes de connaissancesAtelier - Innover avec l’IA Générative et les graphes de connaissances
Atelier - Innover avec l’IA Générative et les graphes de connaissances
Neo4j
 
Text-Summarization-of-Breaking-News-Using-Fine-tuning-BART-Model.pptx
Text-Summarization-of-Breaking-News-Using-Fine-tuning-BART-Model.pptxText-Summarization-of-Breaking-News-Using-Fine-tuning-BART-Model.pptx
Text-Summarization-of-Breaking-News-Using-Fine-tuning-BART-Model.pptx
ShamsuddeenMuhammadA
 
Navigating the Metaverse: A Journey into Virtual Evolution"
Navigating the Metaverse: A Journey into Virtual Evolution"Navigating the Metaverse: A Journey into Virtual Evolution"
Navigating the Metaverse: A Journey into Virtual Evolution"
Donna Lenk
 
OpenMetadata Community Meeting - 5th June 2024
OpenMetadata Community Meeting - 5th June 2024OpenMetadata Community Meeting - 5th June 2024
OpenMetadata Community Meeting - 5th June 2024
OpenMetadata
 
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoamOpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
takuyayamamoto1800
 
2024 RoOUG Security model for the cloud.pptx
2024 RoOUG Security model for the cloud.pptx2024 RoOUG Security model for the cloud.pptx
2024 RoOUG Security model for the cloud.pptx
Georgi Kodinov
 
Understanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSageUnderstanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSage
Globus
 
GraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph TechnologyGraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph Technology
Neo4j
 
A Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of PassageA Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of Passage
Philip Schwarz
 
Vitthal Shirke Microservices Resume Montevideo
Vitthal Shirke Microservices Resume MontevideoVitthal Shirke Microservices Resume Montevideo
Vitthal Shirke Microservices Resume Montevideo
Vitthal Shirke
 
Dominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdf
Dominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdfDominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdf
Dominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdf
AMB-Review
 
AI Fusion Buddy Review: Brand New, Groundbreaking Gemini-Powered AI App
AI Fusion Buddy Review: Brand New, Groundbreaking Gemini-Powered AI AppAI Fusion Buddy Review: Brand New, Groundbreaking Gemini-Powered AI App
AI Fusion Buddy Review: Brand New, Groundbreaking Gemini-Powered AI App
Google
 
APIs for Browser Automation (MoT Meetup 2024)
APIs for Browser Automation (MoT Meetup 2024)APIs for Browser Automation (MoT Meetup 2024)
APIs for Browser Automation (MoT Meetup 2024)
Boni García
 
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data AnalysisProviding Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
Globus
 
Prosigns: Transforming Business with Tailored Technology Solutions
Prosigns: Transforming Business with Tailored Technology SolutionsProsigns: Transforming Business with Tailored Technology Solutions
Prosigns: Transforming Business with Tailored Technology Solutions
Prosigns
 
GlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote sessionGlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote session
Globus
 

Recently uploaded (20)

openEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain SecurityopenEuler Case Study - The Journey to Supply Chain Security
openEuler Case Study - The Journey to Supply Chain Security
 
Developing Distributed High-performance Computing Capabilities of an Open Sci...
Developing Distributed High-performance Computing Capabilities of an Open Sci...Developing Distributed High-performance Computing Capabilities of an Open Sci...
Developing Distributed High-performance Computing Capabilities of an Open Sci...
 
Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024
 
Globus Connect Server Deep Dive - GlobusWorld 2024
Globus Connect Server Deep Dive - GlobusWorld 2024Globus Connect Server Deep Dive - GlobusWorld 2024
Globus Connect Server Deep Dive - GlobusWorld 2024
 
Atelier - Innover avec l’IA Générative et les graphes de connaissances
Atelier - Innover avec l’IA Générative et les graphes de connaissancesAtelier - Innover avec l’IA Générative et les graphes de connaissances
Atelier - Innover avec l’IA Générative et les graphes de connaissances
 
Text-Summarization-of-Breaking-News-Using-Fine-tuning-BART-Model.pptx
Text-Summarization-of-Breaking-News-Using-Fine-tuning-BART-Model.pptxText-Summarization-of-Breaking-News-Using-Fine-tuning-BART-Model.pptx
Text-Summarization-of-Breaking-News-Using-Fine-tuning-BART-Model.pptx
 
Navigating the Metaverse: A Journey into Virtual Evolution"
Navigating the Metaverse: A Journey into Virtual Evolution"Navigating the Metaverse: A Journey into Virtual Evolution"
Navigating the Metaverse: A Journey into Virtual Evolution"
 
OpenMetadata Community Meeting - 5th June 2024
OpenMetadata Community Meeting - 5th June 2024OpenMetadata Community Meeting - 5th June 2024
OpenMetadata Community Meeting - 5th June 2024
 
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoamOpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
OpenFOAM solver for Helmholtz equation, helmholtzFoam / helmholtzBubbleFoam
 
2024 RoOUG Security model for the cloud.pptx
2024 RoOUG Security model for the cloud.pptx2024 RoOUG Security model for the cloud.pptx
2024 RoOUG Security model for the cloud.pptx
 
Understanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSageUnderstanding Globus Data Transfers with NetSage
Understanding Globus Data Transfers with NetSage
 
GraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph TechnologyGraphSummit Paris - The art of the possible with Graph Technology
GraphSummit Paris - The art of the possible with Graph Technology
 
A Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of PassageA Sighting of filterA in Typelevel Rite of Passage
A Sighting of filterA in Typelevel Rite of Passage
 
Vitthal Shirke Microservices Resume Montevideo
Vitthal Shirke Microservices Resume MontevideoVitthal Shirke Microservices Resume Montevideo
Vitthal Shirke Microservices Resume Montevideo
 
Dominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdf
Dominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdfDominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdf
Dominate Social Media with TubeTrivia AI’s Addictive Quiz Videos.pdf
 
AI Fusion Buddy Review: Brand New, Groundbreaking Gemini-Powered AI App
AI Fusion Buddy Review: Brand New, Groundbreaking Gemini-Powered AI AppAI Fusion Buddy Review: Brand New, Groundbreaking Gemini-Powered AI App
AI Fusion Buddy Review: Brand New, Groundbreaking Gemini-Powered AI App
 
APIs for Browser Automation (MoT Meetup 2024)
APIs for Browser Automation (MoT Meetup 2024)APIs for Browser Automation (MoT Meetup 2024)
APIs for Browser Automation (MoT Meetup 2024)
 
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data AnalysisProviding Globus Services to Users of JASMIN for Environmental Data Analysis
Providing Globus Services to Users of JASMIN for Environmental Data Analysis
 
Prosigns: Transforming Business with Tailored Technology Solutions
Prosigns: Transforming Business with Tailored Technology SolutionsProsigns: Transforming Business with Tailored Technology Solutions
Prosigns: Transforming Business with Tailored Technology Solutions
 
GlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote sessionGlobusWorld 2024 Opening Keynote session
GlobusWorld 2024 Opening Keynote session
 

Making an Exception

  • 1. N O MATTER HOW GOOD OUR INTENTIONS are, bad things happen. This is as true of programs at runtime as it is in everyday life. The reality differentiates industrial-strength code from simple, toy code examples. But handling the bad along with the good is where things can get ugly.There’s always more that can go wrong than can go right, so traditional error handling often chops up and obscures normal logic, making it hard to trace the original intent. The intricacy of error handling has sometimes driven programmers blindly into the arms of the goto, which has an effect on code similar to that of the mile-a-minute plant on gardens.To those who are not forewarned, it initially looks harmless enough, but before long it’s completely taken over. And it takes more than a pair of garden shears to disentangle either your code or your garden. This is where exceptions fit in. C++’s exception handling mechanism is a marriage between its object model and its control-flow features. It supports a separation between the normal flow of control (the try block) and the handling of the abnormal and abominable (the catch handlers). It also simplifies and broadens the communication of the bad news (the throw), just as a goto does not. It’s true that exceptions inflict a certain amount of violence on the normal, gentle flow of control, but they do so sympathetically. Exceptions support rather than obstruct the block structure and modularity of your code, whereas “a goto completely invalidates the high-level structure of the code”1 , making it difficult to read, refactor or rescue. Exceptions also have a dark side. If code isn’t written to be exception-safe, an exception will lead to graceless rather than graceful failure – the act of deliv- ering bad news creates worse news. Code that releas- es a previously acquired resource, for example, should be executed regardless of the route out of a function. Code not written with exceptions in mind normal- ly fails to do the right thing when they happen. An object finale The common focus of exception handling is on sep- arating the normal and exceptional paths. In contrast, exception safety focuses on identifying code that’s com- mon to both paths. In the case of paired actions, such as acquire–release or open–close, an exception-safe approach ensures the bracketing action is always exe- cuted. Consider the following code: { resource_type resource = acquire(); .... // use resource, exceptions possible release(resource); } There are two things to notice about this. It’s brief and clear, and it’s unsafe, with the release action bypassed if an exception is thrown in the body of the block. The first instinct of many programmers is to make the code exception-aware in order to make it safe: { resource_type resource = acquire(); try { .... // use resource, exceptions possible } catch(...) { release(resource); 58 APPLICATION DEVELOPMENT ADVISOR q www.appdevadvisor.com Kevlin Henney is an independent software development consultant and trainer.He can be reached at www.curbralan.com EFFECTIVE C++ q C++’s exception handling mechanism respects a program’s modularity when it transfers control q Exceptions can cause problems if code is not exception safe q Some languages use a finally block to allow clean up of resources on block exit q The C++ idiom is to use objects to encapsulate actions rather than extend the language itself q A simple, extensible class template can be written to handle many common clean-up tasks FACTS AT A GLANCE Exception handling in C++ can save your program from digital death, but it must be treated with care. Kevlin Henney explains how to make your programs exception-safe Making an exception
  • 2. 60 APPLICATION DEVELOPMENT ADVISOR q www.appdevadvisor.com EFFECTIVE C++ throw; // rethrow so caller is aware of failure } release(resource); } There are three things to notice about this code: it’s verbose, exception-safe and, because of its verbosity, it’s not immediately obvious that it’s exception-safe. It’s not enough that code should be exception-safe, either – it should also be clear that it’s exception- safe. The code above demonstrates a simple scenario with a single resource and single release action. Include more of either and it looks like the mile-a-minute has returned to take over your code. And finally... Some languages that adopt an exception handling mechanism like C++’s also introduce a finally block. For example, Java and C# work this way. A finally block groups code common to both execution paths into a single block. C++ doesn’t have finally, but if it did, it would look something like the following: { resource_type resource = acquire(); try { .... // use resource, exceptions possible } finally // not legal C++ { release(resource); } } This is clearly briefer and briefly clearer than the previous, tortured code fragment. Is it an oversight that C++ doesn’t support such a construct? On the contrary, it’s by design2 . Although finally reduces the amount of code duplication within a single function, it still bloats the source of the function and often leads to code duplication when the resource type is used in many places.The same finally and release code are repeated everywhere the resource is used. C++ in addition has a feature that neither Java nor C# supports – deterministic object lifetime and destruction. An ordinary local variable will have its destructor called when it goes out of scope, both for normal exit and as the stack unwinds from an excep- tion. Destructors are not just for cleaning up the resources used by the destructing object, they can be used to clean up other objects’ resources as well. Thanks for the memory The most common example of this idiom is in managing memory that is allocated only for a block, although it can also be used to automate lifetime management for nested objects. This is one of the roles that the standard auto_ptr class template is designed to cover: { std::auto_ptr<type> ptr(new type); .... // use ptr, exceptions possible } As opposed to: { type *ptr = new type; try { .... // use ptr, exceptions possible } catch(...) { delete ptr; throw; } delete ptr; } auto_ptr is clearly a winner in this department. However, beyond this simple scenario, it’s let down by some rather messy semantics. auto_ptr’s specification passes all understanding and defies imple- mentation. It’s clear from the scenario already outlined that such acquisition objects should not be copyable, as this would lead to aliasing and attempted multiple deletion of a single object. How- ever, after a few rounds with standardisation politics, what was once a simple class that could be implemented by a C++ novice grew into a monster that supported move rather than copy semantics for its copy operations – a handful of C++ experts, and not everyone involved with the standardisation, can implement it. If you want surprising semantics, consider the following: { std::auto_ptr<type> ptr(new type), qtr; qtr = ptr; .... } There is an expectation that assignment leaves the right-hand side of the assignment unaffected. Alas, auto_ptr fails to live up to expec- tations: the right-hand side is set to null. While such transfer functionality is sometimes useful, it isn’t copy assignment. Sadly, this is the tip of the iceberg with this class template’s problems. Let’s go back to first principles, see what’s involved in designing such a class for its original use in simplifying block-scoped memory management, and then generalise it to other resources while retaining its essential simplicity. To bind object deletion to block scope, we need a constructor to be told about the object, a destruc- tor to destroy it and no copying operations (because that way lies madness, darkness and the shadow of std::auto_ptr): template<typename resource_type> class scoped { public: // structors explicit scoped(resource_type resourced) : resource(resourced) { } ~scoped() { delete resource; } The solution to the exception-safety problem is to give custody of the resource to a locally scoped object which then performs the clean-up on destruction.This solution is a more object-oriented one: the control flow is abstracted into an object whose responsibility is clear and whose action is reusable.The resulting code is exception-neutral, meaning that it’s safe, but not cluttered with try blocks of any kind, and requires no extension to the language.The technique is well known and well documented in a number of forms. It is commonly known as the RESOURCE ACQUISITION IS INITIALIZATION idiom3 , although this name is something of a misnomer: the idiom has nothing to do with initialisation and everything to do with finalisation.
  • 3. MAY 2001 61 EFFECTIVE C++ private: // prevention scoped(const scoped &); scoped &operator=(const scoped &); private: // representation resource_type resource; }; scoped is simple in both implementation and use: { scoped<type *> ptr(new type); .... } A change of policy The precondition to using scoped as it currently stands is that the pointer must either be null or have been allocated using new. If you’re using a different allocator, such as malloc or new[], then delete is the wrong deallocation. Instead, you need free and delete[] respectively. Rather than write a separate class template for each possibility, it would be simpler to allow scoped to be parameterised by a destruc- tion policy. A policy class describes a STRATEGY4 that can be used as a template parameter to control some aspect of the template’s behaviour. By default, we tend to work with single objects rather than arrays, which suggests the following usage: { scoped<type *> ptr(new type); scoped<type *, array_deleter> array(new type[size]); .... } Treating the policy as a function object type means we get the desired flexibility, a simple syntax to use inside scoped, and the pos- sibility of passing in a policy object that has its own state. Here is the revised scoped template: template< typename resource_type, typename destructor = deleter> class scoped { public: // structors explicit scoped( resource_type resourced, destructor on_destruct = destructor()) : resource(resourced), destruct(on_destruct) { } ~scoped() { destruct(resource); } private: // prevention scoped(const scoped &); scoped &operator=(const scoped &); private: // representation resource_type resource; destructor destruct; }; The default object deletion policy and function object type are simple: struct deleter { template<typename deleted_type> void operator()(deleted_type to_delete) { delete to_delete; } }; A member template is used so that this simple class will work with any pointer type.The alternative would be to make the whole class a template, which would make its use verbose: to use it, you would have to spell out the class template name and its parame- ter types, rather than just use the class name and let the compiler deduce the type from the argument of the function call operator – deleter<type *> as opposed to deleter. As a function-object type, deleter can be reused in contexts other than scoped. For instance, consider the common need to delete each object pointed to by a container: std::list<type *> objects; objects.push_back(new type); .... Rather than the following, slightly long-winded loop: for(std::list<type *>::iterator at = objects.begin(); at != objects.end(); ++at) { delete *at; } You can write the following: std::for_each(objects.begin(), objects.end(), deleter()); Given deleter, array_deleter is quite straightforward: struct array_deleter { template<typename deleted_type> void operator()(deleted_type to_delete) { delete[] to_delete; } }; A sense of closure Not all pointers want deleting. Some have more complex finalisation. If, for compatibility reasons, you end up using C-style I/O, you may be using FILE * with fopen and fclose from the C standard library. In this case, the pointer is ‘allocated’ using fopen and ‘deallocated’ using fclose. We can provide a simple closer function object type to handle this: struct closer { void operator()(FILE *to_close) { fclose(to_close); } }; And to use it, scoped needs no change: { scoped<FILE *, closer> file(fopen(name, mode)); .... }
  • 4. 62 APPLICATION DEVELOPMENT ADVISOR q www.appdevadvisor.com EFFECTIVE C++ Now, the way that I have written the scoped template allows any type to be used as a resource, not just pointers. For instance, you can use scoped with an int. An int? Why would an int need tidy- ing up in the event of an exception? In the POSIX API, int is the type used for file handles. So, although the int itself does not need any extra management, an open needs to be matched with a close. Given that ‘close’ is the most common name for any close action, we can generalise the closer function object type beyond just FILE * and int: struct closer { template<typename closed_type> void operator()(closed_type to_close) { close(to_close); } void operator()(FILE *to_close) { fclose(to_close); } }; This now takes the appropriate action depending on the type of the resource: { scoped<FILE *, closer> file(fopen(name, mode)); .... } // fclose called { scoped<int, closer> file(open(name, flags)); .... } // close called Getting a result As it stands, the scoped template is simple, flexible – and almost useless.The only public interface it currently sports is a constructor and a destructor. This is something of a write-only interface: there’s no way to get your hands on the actual resource to use it. We can provide a simple outward conversion with a user- defined conversion operator: template< typename resource_type, typename destructor = deleter> class scoped { .... operator resource_type() const { return resource; } .... }; For the common case of just getting your hands on the value of the resource, the implicit conversion makes the use of scoped practically transparent: { scoped<FILE *, closer> file(fopen(name, mode)); .... fflush(file); // implicit conversion to FILE * .... } Except for pointers. For instance, auto_ptr, like most other smart pointers, supports operator* and operator-> for dereferencing. The arrow operator is easy enough: template< typename resource_type, typename destructor = deleter> class scoped { .... resource_type operator->() const { return resource; } .... }; If resource_type is a pointer, or another class that supports its own operator->, the operator-> just defined can be used. If not, any attempt to use it will result in a compile time error. So the code is valid when it’s supposed to be, and isn’t when it isn’t, which is just the level of type checking we want. operator* presents us with a bit more of a challenge: what’s the return type in the case of a pointer-based resource, and how do we prevent it being used with non-pointer resources? How do we get from resource_type to the type of *resource? C++ doesn’t support a typeof operator, which would make this trivial, so we resolve the situation using theTRAITS idiom. A trait class allows compile-time deduction of features of another type based on template speciali- sation. We are going to define a simple, single-feature trait that allows us to deduce a legal result type for operator*: template< typename resource_type, typename destructor = deleter> class scoped { .... dereferenced<resource_type>::type operator*() const { return *resource; } .... }; The dereferenced trait class has a nested typedef that resolves to the right type. For non-pointer types, operator* is unusable because the *resource expression isn’t legal. Template functions aren’t fully checked and compiled unless they are used, but they still need to have legal signatures. In this case, a return type of void will do the trick. So, the default implementation for dereferenced does just that: template<typename value_type> struct dereferenced { typedef void type; }; For pointer types, we can use partial specialisation to offer the dereferenced result type we are expecting: template<typename element_type> struct dereferenced<element_type *> { typedef element_type &type; }; This specialisation effectively overrides the primary template for all types that match form T *, and which dereferenced would there-
  • 5. MAY 2001 fore be T &. The only other case we need to specialise for is that of void *. There’s no such thing, however, as a reference to void. In the event that we are working at a low level, we may find our- selves working with raw memory, but we still want the safety and convenience of scoped at our disposal. At the moment, the com- piler will reject any attempt to use scoped<void *>, as indeed it does for auto_ptr<void>, because the type void & isn’t well formed. We can add a full specialisation for this case, silencing the com- piler and disabling use of operator* while still offering the core scoped features: template<> struct dereferenced<void *> { typedef void type; }; There are many benefits to the traits-based approach, not least of which is that it makes the scoped template’s operator* work! Another is that traits are open to further specialisation by other users. For instance, if you have your own smart pointer class that you would like to use with scoped, you could specialise derefer- erenced accordingly. Exceptions are a simple mechanism that can be used to send bad news from one part of a program to another. They can also be used to make bad news if the programmer isn’t aware of a hand- ful of simple exception-safe practices. Rather than introducing a finally block to guarantee resource release, C++ encourages a more object-oriented approach that abstracts control flow and reduces code duplication. Exception-neutral code that uses this technique is safer and more concise than exception-aware code that labo- riously addresses each outcome in turn. The simple principles and techniques described in this article can be taken to quite an advanced level5,6 . auto_ptr is useful for the simple task of scope-managed mem- ory, but it doesn’t generalise beyond delete of new-allocated objects. It is further complicated for both the user and the stan- dard library implementor by its move semantics for copying. So, if nothing else is available, use auto_ptr. However, you might want to experiment with other libraries, such as Boost7 , or the scoped template presented here. The only feature currently missing from scoped is the ability to transfer ownership of a resource: this is left as an exercise for the reader. s References 1. Taligent’s Guide to Designing Programs: Well-Mannered Object-Oriented Design in C++, Addison-Wesley, 1994 http://pcroot.cern.ch/TaligentDocs/TaligentOnline/Docu- mentRoot/1.0/Docs/books/WM/WM_1.html. 2. Bjarne Stroustrup, The Design and Evolution of C++, Addison-Wesley, 1994 3. Bjarne Stroustrup, The C++ Programming Language, Third edition, Addison-Wesley, 1997 4. Erich Gamma, Richard Helm, Ralph Johnson and John Vlissides, Design Patterns: Elements of Reusable Object- Oriented Software, Addison-Wesley, 1995 5. Kevlin Henney, “C++ Patterns: Executing Around Sequences”, EuroPLoP 2000, July 2000, www.curbralan.com. 6. Andrei Alexandrescu and Petru Marginean, “Generic<Pro- gramming>: Simplifying Your Exception-Safe Code”, C/C++ Users Journal web site, December 2000, www.cuj.com/experts/1812/alexandr.htm. 7. Boost library web site, www.boost.org.