cs11 – c++ dgc - california institute of...

35
CS11 – C++ DGC Spring 2006-2007 Lecture 2

Upload: vuongdan

Post on 16-Mar-2018

214 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

CS11 – C++ DGC

Spring 2006-2007Lecture 2

Page 2: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Today’s Topics

C++ Standard ExceptionsException propagation and cleanup“Resource Allocation Is Initialization” pattern

Page 3: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

C++ Exceptions

Exceptions are nice for reporting many errorsCode throwing the exception can detect the problem, but doesn’t know how to handle it.Code that catches the exception knows what to do about the problem.With careful implementation of constructors and destructors, resources get cleaned up, too.

C++ Standard Library provides a number of standard exception classes

Page 4: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Simple Example

void calculate(float x) {if (x < 0)throw domain_error("x is negative");

// Do our calculation....

}

int main() {float x;cin >> x;try {calculate(x);

} catch (domain_error) {cout << "Caught a domain error!" << endl;

}}

Page 5: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

What Happened?

void calculate(float x) {if (x < 0)throw domain_error("x is negative");

// Do our calculation....

}

int main() {float x;cin >> x;try {calculate(x);

} catch (domain_error) {cout << "Caught a domain error!" << endl;cout << de.what() << endl;

}}

C++ Standard Exceptions provide a what() function to

retrieve error details.

Page 6: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

C++ Standard Exceptions

exception#include <exception>

logic_error#include <stdexcept>

runtime_error#include <stdexcept>

bad_alloc#include <new>

bad_exception#include <exception>

bad_cast#include <typeinfo>

bad_typeid#include <typeinfo>

ios_base::failure#include <ios>

length_error#include <stdexcept>

domain_error#include <stdexcept>

out_of_range#include <stdexcept>

invalid_argument#include <stdexcept>

range_error#include <stdexcept>

overflow_error#include <stdexcept>

underflow_error#include <stdexcept>

Thrown by theC++ language

Page 7: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Standard Exception Hierarchy

Two major kinds of standard exceptionslogic_error

Intended for “preventable” errors, a.k.a. bugsInvalid function arguments, violated invariants, etc.A potential alternative to using assert()Steer clear of these for reporting runtime errors!

runtime_error“All other errors.”Errors that can only be caught as the program executes

Page 8: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Using the Standard Exceptions

These are available for use in your programs.Use them as-is, subclass them, or ignore them and make your own!You will probably have to handle them, at least…

“Some people view this as a useful framework for all errors and exceptions; I don’t.”

- Bjarne StroustrupThe C++ Programming Language §14.10

Page 9: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Local Variables and Destructors

Normally, destructors are called when variables go out of scope

void myFunction() {Class1 var1;Class2 var2("out.txt");

var1.doStuff(var2);}

Compiler inserts destructor calls into the appropriate placesYour code doesn’t manually call destructors, ever.

var2.~Class2();var1.~Class1();

Compiler adds:

Page 10: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Destructors and Exceptions

void myFunction() {Class1 var1;Class2 var2("out.txt");

var1.doStuff(var2);

}

What happens if var2 constructor throws?Only var1 was constructed, so only var1 destructor gets called

var2.~Class2();var1.~Class1();

THROW!

propagateexception

cleanup(stack unwinding)

Page 11: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Destructors and Exceptions (2)

void myFunction() {Class1 var1;Class2 var2("out.txt");

var1.doStuff(var2);

}

What happens if var1 constructor throws?Nothing was constructed, so no destructors get called

var2.~Class2();var1.~Class1(); propagate

exception

no cleanup neededTHROW!

Page 12: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Destructors and Exceptions (3)

void myFunction() {Class1 var1;Class2 var2("out.txt");

var1.doStuff(var2);

}

What happens if var1.doStuff(var2) throws?Both var1 and var2 were constructed, so both destructors get called (in reverse order of construction)

var2.~Class2();var1.~Class1();

THROW!

propagateexception

cleanup(stack unwinding)

Page 13: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Classes and Exceptions

Similar model used when constructors throwclass Logger {LogConfig config;RotatingFile outputFile;

public:Logger(const string &configFile) {... // initialize logger

}...

};

What happens if the constructor body throws?The new Logger instance failed to be constructedconfig and outputFile have already been initialized, so their destructors are automatically called

THROW!

Page 14: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Classes and Exceptions (2)

Member initialization might also throwclass Logger {LogConfig config;RotatingFile outputFile;

public:Logger(const string &configFile) :config(configFile), outputFile(config)

{... // initialize logger

}...

};What happens if outputFile constructor throws?

The new Logger instance failed to be constructed (again)config was already initialized, so its destructor gets called

THROW!

Page 15: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Classes and Exceptions (3)

Another member constructor throwsclass Logger {LogConfig config;RotatingFile outputFile;

public:Logger(const string &configFile) :config(configFile), outputFile(config)

{... // initialize logger

}...

};What happens if config constructor throws?

The new Logger instance failed to be constructed (yet again)Nothing was initialized, so no member destructors are called

THROW!

Page 16: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Safe Dynamic-Resource Management

Problem:Dynamic allocation of resources, plus exception-handling, is a potentially dangerous mix!

Memory allocated with newOpening files, pipes, etc.Threads, mutexes, condition variables, semaphores, …

Just catching exceptions isn’t enough!Also need to release any resources that were allocated before exception was thrown.

Page 17: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Allocation and Exceptions

Example:Simulator::Simulator(SimConfig *pConf) {entityData = new Entity[pConf->maxEntities];playerData = new Player[pConf->maxPlayers];

}

What happens if second allocation throws bad_alloc?

Simple: entityData doesn’t get cleaned up

Page 18: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

A Safer Constructor

Can fix the problem by doing this:Simulator::Simulator(SimConfig *pConf) :entityData(0), playerData(0)

{try {

entityData = new Entity[pConf->maxEntities];playerData = new Player[pConf->maxPlayers];

}catch (bad_alloc &ba) {

delete[] entityData;delete[] playerData;throw; // Don’t forget to propagate this!

}}

Not the prettiest code, but at least it’s safe.

Page 19: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Again and Again!

This pattern gets old fast:void initSimulation() {SimConfig *pConf = new SimConfig("sim.conf");Simulator *pSim = new Simulator(pConf);...

}

What if Simulator constructor throws?(sigh)

This approach to leak-free, exception-safe code is a pain!

Page 20: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Typical Resource Allocation Model

General form of the problem:void doStuff() {// acquire resource 1// ...// acquire resource N

// use the resources

// release resource N// ...// release resource 1

}

Resources usually released in opposite order of allocationHey, C++ constructors and destructors do this!

Local variables are created and destroyed this way

Page 21: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Exceptions and Local Variables

Example with objects:void doStuff() {Resource1 r1;

// This could throw exceptionsriskyBusiness(r1);

Resource2 r2;doSomethingElse(r1, r2);

}

Local variables are only destructed if they were constructed before an exception is thrown

If riskyBusiness throws, only r1 needs to be cleaned upLocal variables are destructed in opposite order of construction

r2 is destructed (if necessary), then r1 is destructed

Page 22: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Easier Leak-Proofing Approach!

Make a wrapper class for managing a dynamic resource

Constructor allocates the dynamic resourceDestructor frees the resourceUse the wrapper class for local variables

(Otherwise, you’re back to the old problems again…)

“Clean up” exception-handlers become unnecessary

When exception is thrown, C++ will call wrapper-class destructor automatically, since it’s local.

Page 23: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

“Resource Allocation Is Initialization”

This pattern is called “Resource allocation is initialization.”

A local variable’s constructor immediately assumes ownership of the dynamic resourceC++ will call the destructor at the Right Time.

Typically realized as “smart pointers”They follow this model for heap-allocated memory

This can be applied to any dynamic resourceFiles! Semaphores! Mutexes! …

Page 24: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

This Week’s Lab

Implement “Resource Allocation Is Initialization” pattern for POSIX threading constructs

A wrapper class to manage a POSIX threading primitiveConstructor can allocate the primitiveDestructor can clean up the primitiveWrapper class automates cleanup operations when exceptions occurCan also indicate errors with exceptions

Page 25: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

POSIX Mutex Wrapper

Mutex operations:Allocated with pthread_mutex_init()Destroyed with pthread_mutex_destroy()Lock, unlock, try-lock, etc.

POSIX functions can return error codesCreate a Mutex class that wraps a POSIX mutex

Constructor initializes mutex variableDestructor cleans up mutex

Page 26: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Mutex Errors

If mutex can’t be created:Wrapper class constructor can throw an exception

When mutex lock/unlock operation fails:Throw an exception to indicate the issue

When mutex destructor operation fails:Just log an error to cerr; don’t throw!

RAII Pattern:Constructors should throw when construction failsDestructors should never ever throw!!

Page 27: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Destructors and Exceptions

Destructors should never ever throw.Breaks process of cleaning up collections of objects

Example:Widget *batch = new Widget[20];Widget default constructor called on each widget in the batch

What does this do?delete[] batch;Widget destructor called on each widget in the batchThen, memory for array is reclaimed

What if batch[3] destructor throws an exception?Rest of objects are leaked!

Page 28: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

More Mutex Mangling

Create a mutex variable:Mutex m;

Can lock and unlock it, etc.Internal POSIX pthread_mutex_t is cleaned up automatically (Yay RAII)

What does this mean?Mutex m2(m);

Or this?Mutex m3;m3 = m;

Page 29: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Copying Resources

Basic question:What are the semantics of copying a resource?If multiple objects share a particular resource, which one should free it?

Often, easiest solution is to disallow copyingMutex is a good example

Approach:Declare copy-constructor and assignment operator to be privateImplementation can assert(false) too!

Page 30: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Uncopyable Widgets

To disallow copying and assignment:class Widget {double weight;

// Disallow copy construction.Widget(const Widget &) { assert(false); }

// Disallow assignment. Return void to simplify.void operator=(const Widget &) { assert(false); }

public:Widget(double w) : weight(w) { ... }...

};

Document these situations in your code!

Page 31: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Another Useful Trick

Locks on mutexes are also resourcesWould like locks to be released automatically when an exception is thrown

Example:void doStuff() {

// Lock mutex.m.lock();// This could throw!riskyBusiness();// Finally, unlock the mutex.m.unlock();

}If function throws, mutex will remain locked!

Page 32: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Lock Objects

Create a Lock class that manages a lock on a Mutex objectLock constructor acquires a lock on the mutexLock destructor releases the lockIf error during lock operation, Lock constructor should throwLock destructor should never throw

Need to catch Mutex::unlock() exceptions…The Lock class has no other member functions!

Lock should disallow copying too

Page 33: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Exception-Safe Locking

Now our locking code is exception-safe:void doStuff() {

// Lock mutex.Lock lock(m);// This could throw!riskyBusiness();

}

If function throws, mutex is automatically unlockedOur code is slightly simpler, too

Page 34: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Lock Blocks

Can use blocks to temporarily acquire locks:void doMoreStuff() {{Lock lock(m);riskyBusiness1();

}...{Lock lock(m);riskyBusiness2();

}}

Scope of each lock variable is inside blockAutomatically released at end of blockStill exception-safe, like before

Page 35: CS11 – C++ DGC - California Institute of Technologycourses.cms.caltech.edu/cs11/material/dgc/lectures/cs11-cppdgc-lec... · Errors that can only be caught as the program executes

Next Time

No class next weekResume class in two weeks

Next time:Signaling between threadsBuilding larger-scale multi-threading components