“RESOURCE ACQUISITION IS INITIALIZATION”
Andrey Dankevich April, 2013
4/18/2013 1
• Memory • Handles • Locks • Sockets • Threads • Temp files • …
4/18/2013 2
What is a resource?
4/18/2013 3
What is a resource?
“Resource” is anything that follows this pattern!
4/18/2013 4
So what’s the problem?
Real code might have different points at which the resource should be released:
void f(const char* p) { FILE* f = fopen(p,"r"); // acquire // use f fclose(f); // release }
4/18/2013 5
Resources and errors
// naïve fix: void f(const char* p) {
FILE* f = 0; try {
f = fopen(p, "r"); // use f
} catch (…) { // handle every exception
if (f) fclose(f); throw;
} if (f) fclose(f);
}
4/18/2013 6
Resources and errors
Is this code really safer?
// use an object to represent a resource
class File_handle { // belongs in some support library FILE* p; public:
File_handle(const char* pp, const char* r) { p = fopen(pp,r); if (p==0) throw File_error(pp,r); } File_handle(const string& s, const char* r) { p = fopen(s.c_str(),r); if (p==0) throw File_error(s,r); } ~File_handle() { fclose(p); }
}; 4/18/2013 7
RAII
In C++ the only code that can be guaranteed to be executed after an exception is thrown are the destructors of objects residing on the stack.
Deterministic Exception-safe
void f() {
A a; throw 42;
} // ~A() is called
4/18/2013 8
Stack lifetime semantics
• Java, C# and Python, support various forms of the dispose pattern to simplify cleanup of resources.
• Custom classes in C# and Java have to explicitly implement the Dispose method.
4/18/2013 10
.NET
using (Font font1 = new Font("Arial", 10.0f)) { byte charset = font1.GdiCharSet; }
• Java, C# and Python, support various forms of the dispose pattern to simplify cleanup of resources.
• Custom classes in C# and Java have to explicitly implement the Dispose method.
4/18/2013 11
.NET
{ Font font1 = new Font("Arial", 10.0f); try { byte charset = font1.GdiCharSet; } finally { if (font1 != null) ((IDisposable)font1).Dispose(); } }
Working with temp files:
class TempFile : IDisposable { public TempFile(string filePath) { if (String.IsNullOrWhiteSpace(filePath)) throw new ArgumentNullException(); this.Path = filePath; } public string Path { get; private set; } public void Dispose() { if (!String.IsNullOrWhiteSpace(this.Path)) { try { System.IO.File.Delete(this.Path); } catch { } this.Path = null; } } } 4/18/2013 12
.NET - Examples
4/18/2013 13
.NET - Examples
Working with temp settings values: Check the implementation for AutoCAD settings
http://www.theswamp.org/index.php?topic=31897.msg474083#msg474083
Use this approach if you need to temporarily change the settings in application.
Many STL classes use RAII: • std::basic_ifstream, std::basic_ofstream and
std::basic_fstream will close the file stream on
destruction if close() hasn’t yet been called.
• smart pointer classes std::unique_ptr for single-owned
objects and std::shared_ptr for objects with shared
ownership.
• Memory: std::string, std::vector…
• Locks: std::unique_lock
4/18/2013 14
Back to C++
SCOPE GUARD
4/18/2013 15
• Original article: http://drdobbs.com/184403758
• Improved implementation: http://www.zete.org/people/jlehrer/scopeguard.html
• C++ and Beyond 2012: Andrei Alexandrescu - Systematic
Error Handling in C++:
video (from 01:05:11) - http://j.mp/XAbBWe
slides ( from slide #31) - http://sdrv.ms/RXjNPR
• C++11 implementation: https://github.com/facebook/folly/blob/master/folly/ScopeGuard.h
4/18/2013
SCOPE GUARD
Real-world example with PTC Creo API:
• Resource type: ProReference In fact it’s mere a typedef void* ProReference;
• Resource acquire: // Gets and allocates a ProReference containing a // representation for this selection. ProError ProSelectionToReference (ProSelection selection, ProReference* reference);
• Resource release: ProError ProReferenceFree (ProReference reference);
4/18/2013 17
SCOPE GUARD vs unique_ptr
Using std::unique_ptr<>: auto ref_allocator = []( const ProSelection selection ) -> ProReference { ProReference reference; ProError status = ProSelectionToReference(selection, &reference); return reference; }; auto ref_deleter = [](ProReference& ref) { ProReferenceFree (ref); }; std::unique_ptr <std::remove_pointer<ProReference>::type, decltype(ref_deleter)> fixed_surf_ref ( ref_allocator(sels[0]), ref_deleter ); // ref_deleter is guaranteed to be called in destructor
4/18/2013 18
SCOPE GUARD vs unique_ptr
Using ScopeGuard: ProReference fixed_surf_ref = nullptr; status = ProSelectionToReference ( sels[0], &fixed_surf_ref ); SCOPE_EXIT { ProReferenceFree ( fixed_surf_ref ); };
4/18/2013 19
SCOPE GUARD vs unique_ptr
I LOVE IT!!!
Questions?
4/18/2013 21