This is the
talk page for discussing improvements to the
Resource acquisition is initialization article. This is not a forum for general discussion of the article's subject. |
Article policies
|
Find sources: Google ( books · news · scholar · free images · WP refs) · FENS · JSTOR · TWL |
This article is rated C-class on Wikipedia's
content assessment scale. It is of interest to the following WikiProjects: | |||||||||||||||||||||||||||
|
This article links to one or more target anchors that no longer exist.
Please help fix the broken anchors. You can remove this template after fixing the problems. |
Reporting errors |
The C++ Programming Language (Bjarne Stroustrup) (2nd edition) ISBN 0-201-53992-6 : Ch9 "Exception handling" 9.4.1 "Constructors and Destructors" "This technique for managing resources is usually referred to as 'resource acquisition is initialization'. "
This hints at an earlier usage. 51.9.104.223 ( talk) 11:44, 20 May 2021 (UTC)
The Ruby/Python examples aren't RAII. The only difference between the python/ruby versions and the Java try/finally approach is that the cleanup method is not shown. Correct usage of a file object (or whatever other resource) still requires that you always use the object within a 'with' block (or whatever the special block is called).
216.39.239.100 21:10, 17 July 2007 (UTC)
And the correct (or rather, RAII enabled) usage of a file object in C++ requires that you always use a scoped object instead of allocating it with new. Stroustrup says: "The key idea behind the ‘‘resource acquisition is initialization’’ technique/pattern (sometimes abbreviated to RAII) is that ownership of a resource is given to a scoped object." This is precicely what Python and Ruby's blocks do. RAII can be used in Java as well by using a callback object, but since defining an anonymous class is so complex (and they have some limitations), everyone just uses try/finally instead. 80.221.27.192 11:25, 1 December 2007 (UTC)
In C++, in any case the lifetime of the variable defines the lifetime of the resource. This is because the variable is the object, and the object's destructor is called when the variable goes out of scope. Using "new" does not change this, but you now have a variable of a pointer type, so the pointer's RAII is not buying you anything unless you use a magical smart pointer of some sort. The object is not tied to the pointer in any way, so the pointer's lifetime does not influence the object and the pointer's destruction does not cause the object to be destructed.
The same thing happens in Ruby. If you just said foo = File.open('foo.txt'), the variable is scoped, but if it goes out of scope, the object it refers to lives on. In the File.open('foo.txt') do |foo| end example, the block is merely a callback and the open method explicitly has to take care of the resource cleanup. The scoped variable's lifetime actually ends before that, but that is not what causes the cleanup, so no RAII is taking place.
129.13.186.1
21:32, 2 December 2007 (UTC)
A line like foo = File.open('foo.txt') may give you a nice scoped autodestructing variable, but it doesn't tell you *anything* about what was done to make sure that the object foo refers to was carefully initialized. Basically this hides all the joy of RAII in a library function, and then asks the reader to imagine that true RAII must have happened inside of that black box. The example where that File.open() is wrapped in the creation of a larger object, on the other hand, *is* more demonstrative of RAII, I just wish the examples actually *on* on the article page were as relevant (though showing the class in use would be good). -- C. Alex. North-Keys ( talk) 09:46, 6 October 2015 (UTC)
#include <cstdio> // exceptions class file_error { } ; class open_error : public file_error { } ; class close_error : public file_error { } ; class write_error : public file_error { } ; class file { public: file( const char* filename ) : m_file_handle(std::fopen(filename, "w+")) { if( m_file_handle == NULL ) { throw open_error() ; } } ~file() { std::fclose(m_file_handle) ; } void write( const char* str ) { if( std::fputs(str, m_file_handle) == EOF ) { throw write_error() ; } } void write( const char* buffer, std::size_t num_chars ) { if( num_chars != 0 && std::fwrite(buffer, num_chars, 1, m_file_handle) == 0 ) { throw write_error() ; } } private: std::FILE* m_file_handle ; // copy and assignment not implemented; prevent their use by // declaring private. file( const file & ) ; file & operator=( const file & ) ; } ;
class MyFile def initialize(filename, mode, &block) begin handle = File.open(filename, mode) if block_given? block.call(handle) else puts "No block given for RAII" end rescue IOError => ex puts "Got a #{ex.exception} error!" raise #re-raise the exception up the call stack ensure handle.close unless handle.closed? end end end # ... irb(main):018:0> MyFile.new("foo.bar.baz.txt","r") {|f| f.write "foo" } Got a not opened for writing error! IOError: not opened for writing from (irb):18:in `write' from (irb):18 from (irb):6:in `call' from (irb):6:in `initialize' from (irb):18:in `new' from (irb):18 from :0
This is ridiculous nonsense. RAII is a specific way to allocate and release resources ... some totally other way to do it (such as using "ensure") is not RAII. " Rather than an explicit destructor" -- if there is no explicit destructor then it's not RAII. Period. -- 71.102.128.42 ( talk) 02:27, 17 November 2014 (UTC)
Any tips for RAII in Java? Wouter Lievens 12:39, 11 Mar 2005 (UTC)
RAII is not applicable to Java because Java lacks determinstic destruction. In C++ a major concern and use of RAII is memory management. In Java, garbage collection handles memory but there are other types of resources that would benefit from the RAII pattern. Unfortunately the closest thing you can get in Java is the dispose pattern.
This is probably about as close as you can come to the idiom:
public interface Factory<T,E extends Throwable> {
public T create() throws E;
}
import java.io.*;
public abstract class IoRAIIBlock<T extends Closeable> {
private final Factory<T,IOException> factory;
public IoRAIIBlock(Factory<T,IOException> factory) {
this.factory = factory;
}
public final void go() throws IOException {
T obj = factory.create();
try {
run(obj);
} finally {
obj.close();
}
}
public abstract void run(T arg1) throws IOException;
}
public abstract class FileWriterRAIIBlock extends IoRAIIBlock<FileWriter> {
public FileWriterRAIIBlock(final String filename) throws IOException {
super(new Factory<FileWriter,IOException>() {
public FileWriter create() throws IOException {
return new FileWriter(filename);
}
});
}
// more constructors if you want...
}
public class Example {
public static void main(String[] args) throws IOException {
new FileWriterRAIIBlock("logfile.txt") {
public void run(FileWriter logfile) throws IOException {
logfile.append("hello logfile!");
}
}.go();
}
}
I guess if you were feeling really evil, though, you might be able to use AspectJ to insert implicit resource disposal. -- pfunk42 ( talk) 06:30, 17 May 2008 (UTC)
Since version 1.7 (2011), Java has supported the ' try-with-resources' statement, which I often use for RAII. It is not memory-based release, but the guarantees of calling the 'close()' method upon exiting the block provide the same abilities as stack-unwinding destruction. Pedorro ( talk) 19:22, 23 February 2022 (UTC)
What about removing the stub template? I found this article most helpfull.
Who uses RIIA? Googling for "Resource Initialization Is Acquisition" only returns 75 results, most are from mailing lists. DanielKO 00:20, 16 March 2006 (UTC)
Part of the point of RAII is that the code be exception-safe. The example is not necessarily exception safe because it can throw from a destructor (CloseFile can fail). Throwing from a destructor is poor coding practice in C++ (see Exceptional C++ by Herb Sutter).
class RAII { public: RAII () { res.Acquire (); } void ExplicitRelease() { res.Release (); } ~RAII() { try { res.Release (); } catch (...) {} } };
—The preceding unsigned comment was added by 83.216.62.233 ( talk) 10:49, 11 February 2007 (UTC).
I'm with 83.216.62.233. This requires some change to the article. When reading a file, it's probably ok to do it like this:
{ File f("foo.txt"); cout << f.readLine(); }
..since I can't think of a case when the destructor could throw after a successful read. Many resources can be released without the possibility of failure, so this is how simple RAII is usually. But the example writes to a file, so it should be written like this:
{ File f("out.txt"); f.write("hello"); f.close(); }
In this case, Python and Ruby blocks are in fact simpler because they handle release failure correctly and do not require an explicit .close() call.
So, what should be done to the article? This issue needs to be covered better, that much is certain. It may not apply to D (maybe D's destructors can throw without causing abort when unwinding -- I don't know). Should the example use a scoped_ptr, or read from a file, to demonstrate the simple RAII case that requires no explicit close()? I'll probably change the article at some point.. (edit:) A further "issue" with files is that after closing them explicitely, all the operations must throw an exception. This requires some code bloat (I'd rather see an even shorter example than the current one), and the resource is no longer bound to the object's lifetime, which is supposed to be essential to RAII. 80.221.27.192 11:47, 1 December 2007 (UTC)
This article as a whole has been pretty fluffy for a long time, with C++ shoed in (for example) despite hiding most of the useful aspects that relate to resource acquisition == initialization, and instead highlighting irrelevant sidetracks like mutexes, exceptions, and so forth with examples that utterly lack an explicit A==I part. Unless an example *explicitly* shows something like the code that only returns a created object IFF it's initialized (and not by something buried in a library call used in the example, but actual code), then it has no reason to be in this article. That includes code that only talks about how to get rid of objects -- this isn't an article on resource Deletion, but Acquisition. It's been about 3 years since RAII was demonstrated in the examples, with the article as a whole becoming steadily less useful, largely due to massive deletions by Forderud in 2012 who seemed to think that demonstrating approaches to implementing RAII was outside of the scope of an article *about* RAII. -- C. Alex. North-Keys ( talk) 09:42, 6 October 2015 (UTC)
The C example is looks much more complicated than necessary. What about this, that is much better suited for maintenance:
int c_example() {
int retval = -1; // assume error
FILE *f = NULL;
do {
f = fopen("logfile.txt", "w+");
if (f == NULL) {
retval = -1;
break;
}
if (fputs("hello logfile!", f) == EOF) {
retval = -2;
break;
}
// continue using the file resource
retval = 0; // return value 0 is success
} while (0);
// Releasing resources (in reverse order)
if (f != NULL && EOF == fclose(f)) {
retval = -3;
}
return retval;
}
-- 87.163.200.60 ( talk) 11:38, 25 September 2008 (UTC)
The c_example() above, like the one that used to be in the article, only demonstrates the difficulty of dealing with exceptions, not RAII itself. Although talking about both together makes a lot of sense, the topic of exceptions in isolation would be seen in a Wiki article specific to exception handling.
The example on the page now shows where ad-hoc mechanisms in C to emulate RAII tend to lead, with non-local exception handling implemented in conjunction with RAII, and the resulting problems that underscore how having RAII as a language builtin are beneficial. C. Alex. North-Keys ( talk) 06:35, 18 October 2010 (UTC)
Why is this code "much better suited for maintenance"? I think it's just longer and using a loop to simulate gotos is bad form. Also the goto idiom is popular; see for example Linux source code. Matonen ( talk) 13:57, 15 April 2009 (UTC)
The second C isn't particularly useful: there are plenty of things RAII isn't, but this article should focus on things it is. It also reads like an advocacy, rather than an explanation. I intend to replace it with an example of a goto-based RAII, because it is an exceedingly common idiom, as evidenced by its usage in the Linux kernel. I will also include a small blurb about the obvious tradeoffs between the current example and the goto one. 71.188.88.67 ( talk) 06:24, 31 May 2012 (UTC)
Now the article seems to suggest that managing the resource FILE_HANDLE with a class is RAII. No, that's just basic abstraction and clearly quite possible in Java as well. Without RAII you'd have almost the same LogFile class but you'd have to manually call "close". The code would be more like this:
bool functionB() { LogFile log("Output log of functionB"); //if this throws, no closing is needed since nothing was opened try { log.write("fun Step5 succeeded"); // if the function needs to check for an error and abort accordingly: if( ... ) throw FunctionAFailure(); // EXTRA CODE: the resource needs to be closed explicitly log.close(); // explicitly release the resource return true; } catch (...) { // An exception was thrown during the execution of the function // Time to cleanup the resources log.close(); throw; } }
I think this is more accurate than the current article's functionB.
Your example here doesn't make sense to me. A LogFile class with close() would be called a poor design which fails to use constructors and destructors correctly. Namely, the class invariant is violated once you call close(). It would be a textbook case of misuse/misunderstanding of C++, that is, a failure to understand RAII. Xerxesnine 07:24, 23 February 2007 (UTC)
What class invariant would that be? Did I tell you what the class invariants are? No, you arbitrarily decided that the class should have invariant X and saw that the class doesn't conform to X. How silly. On top of that fallacy, you completely misunderstood what I wrote. I did not suggest this as a good way to write C++, but as a change to the currently presented example to better highlight the differences between RAII and non-RAII approaches. Do you think the current non-RAII example code shows a good understanding of RAII? Of course it doesn't because that's the whole point of the example. That's the whole point of my code as well, since it's meant to replace the one presented in the article. Currently the article shows the difference between non-abstracted non-RAII code and abstracted RAII code. When we want to highlight the benefits that RAII gives you, the examples should obviously be of abstracted non-RAII (my code above) and abstracted RAII -- With only the RAII part being different. —Preceding unsigned comment added by 80.221.24.224 ( talk • contribs)
std::FILE* file_handle = std::fopen("logfile.txt", "w+") ; if( file_handle == NULL ) { throw open_error() ; }
std::FILE* file_handle = open_or_throw("logfile.txt", "w+") ;
I feel parts of this article contradicts itself.
To me, RAII means resource management through statically predictable destruction — in C++ by auto variables or std::auto_ptr, or by the lifetime of some larger object having the resource-owning one as a member.
The part that speaks about std::auto_ptr seems to agree with that definition. Another part that speaks of "languages that do not support RAII, such as Java" seems to imply that try ... finally isn't RAII. A third part states that manually calling a finalize() method in languages like Java is RAII.
I suppose you can say you use RAII if you consider your resource FOO as owned by object FOO in general ... but isn't that a more general idea, a basic tool of any object-oriented programming? JöG 20:35, 3 September 2006 (UTC)
Agreed. The final word in the acronym, initialization, refers to the construction of an object where resources are acquired. If you're not doing that, then it's not RAII. Java does not support RAII, but instead has the try-finally facility to guide resource management. I just changed the "Limitations" section to remove this confusion. Xerxesnine 12:49, 23 February 2007 (UTC)
The RAII pattern is used ubiquitously in object-oriented Perl code. In fact, Perl programmers don't even call it RAII, or anything else, since it is simply normal behaviour. Perl's dynamic nature makes it more flexible than C++, so you can safely write:
sub open_it { return new IO::File("it.dat"); }
in Perl, and the file will be automatically closed when you were done with it, whereas in C++ if you did
std::ofstream open_it() { return std::ofstream("it.dat"); }
it would fail to compile, and if you did
std::ofstream *open_it() { return new std::ofstream("it.dat"); }
it would compile but you'd need to explicitly delete the pointer to close the file.
Unfortunately, I can't find a way to cram this vital information into the article. Perhaps someone could add a section for languages that use RAII as standard practice (assuming there are any others)?
-- 61.214.155.14 05:31, 22 November 2006 (UTC)
std::auto_ptr<std::ofstream> open_it() { return std::auto_ptr<std::ofstream>(new std::ofstream("it.dat")); }
My first reaction is that this new section is ugly and superfluous. A simple class wrapper for OpenSocket() and CloseSocket() is just the right thing to do. In contrast, the scoped_handle<> template obscures the programmer's intention.
My inclination is to remove this section entirely, as it contributes nothing to the concept of RAII and appears to be an exercise in unwarranted cleverness. Xerxesnine 22:27, 31 March 2007 (UTC)
Release failure is a complex situation with RAII. The various approaches have been discussed at length on comp.lang.c++ and comp.lang.c++.moderated, all of which are probably inappropriate for an introduction to RAII. That's why I made a simple assert in the destructor; it seems better to do something than to simply leave it unchecked as Intgr wished. Think of it as a placeholder for user-defined functionality. Xerxesnine 14:19, 15 April 2007 (UTC)
So a cryptic error message stating "whatever.cpp:553: file::~file(): Assertion `!"file failed to close"' failed." is somehow considered more meaningful to the user than a pop-up stating "error writing file, your changes may be lost"?
Please read the comp.lang.c++.moderated link I gave before continuing down this line of thought. That's why I gave it to you. You are also missing the context of this simple example; we obviously can't include pop-up dialogue box code.
Assertions are useful for terminating when we detect an undefined state, at which time continuing execution may do real damage. The loss of a file handle is one such case, which may indicate a hardware failure.
You cannot conditionally choose which asserts are included and which are not, unless you are using nonstandard assert macros. Assertions are also useful in inner loops, especially when thrown out of release builds.
Yes, you can make conditional use of asserts within the same build, with the standard NDEBUG macro if desired. That's the whole point of using assertions. That you don't understand this is another indication of the fact that you rarely write C++. Your confusion over assert(!"bad state") is yet another indication, among others.
You continue to provide needless informational comments about how assert() works. Your first post on this thread consisted of entirely of such comments. Be assured that I understand. In light of your inexperience with C++, you might be more productive if you reconsidered your role as "educator" here, engaging with others as peers instead.
The overall issue is a practical one. How are we to handle the release error? There is no good answer because the usual solutions are complex and therefore inappropriate for this introductory example. My suggestion was a simple assert. Your first suggestion was to ignore the error. That is universally unacceptable. Your second suggestion was a std::cerr notice, but as I said that is almost the same thing but worse: it lacks the file, line number, and hardware breakpoint which assert() normally entails.
So, what do you suggest as an alternative to the assert()?
Xerxesnine 16:04, 16 April 2007 (UTC)
I wish to completely demolish all of these arguments so I don't have to deal with them again.
Xerxesnine 03:18, 17 April 2007 (UTC)
Response to Intgr:
As if this steady stream of misinformation coming from Intgr was not enough, I have counted three outright troll remarks from him. This, together with his two reverts on the fclose() checks, is an firm indication of bad faith.
Xerxesnine 15:37, 17 April 2007 (UTC)
It turns out I misread fclose() even after re-checking it. At http://www.die.net/doc/linux/man/man3/fclose.3.html for example, it lists EBADF, which seems to imply that it's the only relevant error code. The next sentence is "The fclose() function may also fail and set errno...", but I read that as "The fclose() function may fail to set errno...", which would explain why only EBADF was listed, in my mind. I put these together and thought the ISO C standard only set a single failure flag for fclose(), leaving the rest to the particular platform API.
So it turns out the fclose() indeed sets useful errnos. I can now imagine legitimate code which checks standard ISO errons before the assert() happens, whereas before I believed one could only use the platform API before the assert(). While I would still put an assert() when all other checks fail or are bogus, that assert() seems much farther away now, and this changed my mind about it.
I apologize to Intgr for wasting his time on this. Mea culpa.
Xerxesnine 01:29, 18 April 2007 (UTC)
Boy this Xerxeresnine character was a jerk, and incompetent too. You should never ever ever use the assert macro for anything other than logic errors. -- 98.108.208.207 ( talk) 13:25, 15 July 2008 (UTC)
Indeed. That exchange shows him to be an incompetent moron. Let's hope none of his code has killed anyone. -- 71.102.128.42 ( talk) 02:57, 17 November 2014 (UTC)
I don’t know who invented the term, but it’s highly misleading: RAII deals neither with resource acquisition nor with initialization. RAII is automatic resource releasing upon leaving scope, whether via throw, break, continue, return or by control reaching the end of block. As in the following code:
{ std::auto_ptr<X> x; // initialization doSomething(); // If this throws, auto_ptr::~auto_ptr does nothing dangerous. x.reset(new X); // Resource acquisition. Done by programmer and not by RAII class! doSomethingElse(); // If this throws, the X object is released. // If nothing throws, ‘x’ is released here. }
Roman V. Odaisky 13:02, 6 June 2007 (UTC)
Since your example does not acquire resources during initialization, it does not demonstrate "resource acquisition is initialization", or RAII. I believe Stroustrup introduced the term in his 3rd ed. book, where he gives (something similar to) this example:
class log { database_lock lock ; file output ; public: log( database_handle db ) : lock(db), output("output.txt") { } } ;
If the lock fails, an exception is thrown and the opening of "output.txt" is never attempted. If the lock succeeds but the output file fails to open, then an exception is thrown which results in the the release of the lock. If both succeed, and an exception is thrown at some point during the scope of the log instance, then both resources are released.
The strategy guarantees that resources are acquired and released in the correct order (FILO/LIFO) and that objects are always in a valid state. If an object cannot be created in a valid state, that is, if the object cannot acquire the needed resources, then we cease the current execution by throwing.
In your example, you have elected to construct an invalid object (any use of auto_ptr is undefined until it is given a valid pointer). This positively demonstrates what RAII seeks to remedy. Xerxesnine 17:43, 21 June 2007 (UTC)
std::auto_ptr<int> a ; *a = 1 ; // crash
The tr1::shared_ptr (or if you prefer boost::shared_ptr), taken as an example, is an example of RAII for which resource acquisition doesn't necessarily occur when the constructor is called, and resource relinquishment doesn't necessarily occur when the destructor is called. That RAII is about acquiring a resource in the constructor and releasing it in the destructor is merely a lie to children. What RAII is really about is that the object is responsible for the management of a resource, and its destruction should guarantee its cleanup, which is quite distinct. —The preceding unsigned comment was added by 75.130.108.174 ( talk • contribs) 15:10, 8 August 2007.
The technique for managing resources using local objects is usually referred to as ''resource acquisition is initialization.'' This is a general technique that relies on the properties of constructors and destructors and their interaction with exception handling.
auto_ptr<int> x(new int(5));
The basic tools for writing exception-safe code are: ... (2) the support for the ''resource acquisition is initialization'' technique. The key idea behind the ''resource acquisition is initialization'' technique/pattern (sometimes abbreviated RAII) is that ownership of a resource is given to a scoped object. Typically, that object acquires (opens, allocates, etc.) the resource in its constructor. ...
PHP5 introduces destructors. Does anyone know if they are guaranteed to run in a timely fashion, for example, when a static object passes out of scope? If so, then this technique could be used in PHP5. Either way, the article should mention whether or not the technique is applicable to that language. - Jmabel | Talk 17:37, 21 February 2008 (UTC)
GNU C adds a cleanup attribute that helps plain C code in exceptional cases. —Preceding unsigned comment added by 17.201.20.87 ( talk) 19:24, 27 February 2008 (UTC)
I believe better wording to replace "the automatic, deterministic destruction" would be "the implicit, synchronous destruction". I believe "automatic" is not as informative as "implicit" and "deterministic" is not as informative as "synchronous". In fact, I think "deterministic" can be misleading because the destructor could be implemented with non-deterministic characteristics (such as calling fclose, which does not have a deterministic result). But I get the impression someone loves the existing wording, since it also shows up in the Java vs. C++ article, so I would like your comments. -- pfunk42 ( talk) 05:25, 17 May 2008 (UTC)
See [4]. So one could use the exact same form of the c++ RAII pattern with c#. 64.234.67.2 ( talk) 05:53, 29 December 2008 (UTC)
public class MyFileWriter : IDisposable { private StreamWriter _swriter; private StreamWriter Writer { get { return _swriter; } set { _swriter = value; } } public MyFileWriter (string filename) { try { Writer = new StreamWriter (filename); } catch (Exception ex) { throw new ApplicationException ("file open failure", ex); } } public void Dispose () { try { Writer.Close (); } catch (Exception ex) { // handle it } } public void Write (string str) { try { Writer.Write ("{0}\n", str); } catch (Exception ex) { throw new ApplicationException ("file write failure", ex); } } } class TestMyFile { public static void Main() { using (MyFileWriter logfile = new MyFileWriter ("logfile.txt")) { logfile.Write ("hello logfile!"); } } }
Googling "mutable RAII" gives 7 hits, many of which are derivatives from this Wikipedia article. "Immutable RAII" fares a little better, giving 171 hits. Some of the hits are for Wikipedia article, some are to discussions where Matthew Wilson, author of the term, is using it. Or to excerpts from Matthew's books. Clearly the term has not caught on, and should not be in Wikipedia. I'll go ahead and remove the mutability section if there's no objection. Matonen ( talk) 13:45, 15 April 2009 (UTC)
Do you guys think the acronym ```SBRM``` (scope-bound resource management) deserves to be included here too? It seems to be more descriptive of what happens here. I can't seem to track down the origin with quick googling though - just various blogs and forums, starting about 2007 and getting more common by 2010. -- Cubbi ( talk) 18:06, 25 August 2010 (UTC)
The RAII concept can be understood with just the C++ example given. "Resource management without RAII" is an ever-growing section with everyone adding a remark or example about their favourite language, and the new "Ad-Hoc Mechanisms" section is longer than the C++ example itself (not to mention original research). -- Matonen ( talk) 14:20, 20 October 2010 (UTC)
This article, especially the comparison to other forms of resource management is incomplete without a section that delves into the subject of compositional properties of the different resource management technologies. The single most compelling property of RAII when compared to try/finally lies in how RAII works with 'deep' resources. In a pure GC+try/finally world, 'being a resource' is transitive when used in composition. In a RAII world it isn't. If in a GC+try/finally world an object contains a 'deep' resource, than this object requires manual resource management at the level of the user of the composite object. With RAII the resource (and thus the resource management) is fully captured at the deeper levels of composition and neither the higher levels of composition nor the user of the composite object are exposed to the fact that the there is a deep resource in there. That is, RAII provides the possibility of encapsulation of resources, while for the alternative, composition will brake encapsulation.
One thing that aggravates the problem for alternative resource management technologies (GC+try/finally) is polymorphism. Combined with polymorphism, not only 'holding' a deep resource makes a composed object require manual resource management, but also composition using any polymorphic component will turn a composite object into an object needing manual resource management. As RAII doesn't have the encapsulation problems it also does not have the polymorphism problems that GC+try/finaly has. —Preceding unsigned comment added by Pibara ( talk • contribs) 09:22, 11 May 2011 (UTC)
Given that there was absolutely no response to the above, I'vv added a section "Disadvantages of scope bound resource management alternatives" to the page, hope this is OK. Pibara ( talk) 07:40, 5 July 2011 (UTC)
decltype
(
talk)
10:17, 5 July 2011 (UTC)The article seems to assume that RAII is only about scope-based automatic construction and destruction of objects. It omits an extremely important part of RAII, namely copying and assignment (which in C++ is implemented in the form of copy constructors and copy assignment operators). This is quite obvious as the article gives as examples of "RAII" the gcc extension "cleanup" and the Java "finally" block. That's only part of RAII. Those do not handle copying and assignment properly.
Copy semantics is what allows, for example, returning objects from functions by value, giving objects as value-based function parameters, copying objects (obviously), creating entire arrays of objects (each element being default-initialized or initialized as a copy of an existing object) and so on, while still fully adhering to the RAII mechanism. This is most certainly not covered by "finally" blocks or a "cleanup" attribute.
Another aspect that's missing is that RAII works in C++ even on the heap: As long as the container structure is allocated and deallocated properly, the contained objects will follow the RAII rules appropriately. (This is the reason why, for example, the standard library data structures do not need to know how their elements are copied and moved around, as RAII takes care of that.)
Comparing RAII to Java's "finally" blocks is, at the very least, misleading. — Preceding unsigned comment added by 84.249.92.156 ( talk) 06:25, 12 April 2012 (UTC)
The article seems to imply that there is no RAII in C#, but this is not true: it merely works in a different way, with more explicit syntax, as already described in this talk page (see C# example above).
This has nothing to do with C# not having deterministic memory deallocation, as RAII is not fundamentally about memory allocation. For example, this C# example is RAII, since reader and writer objects are automatically disposed (i.e. closed) upon exit from the using scope. 212.92.211.195 ( talk) 14:32, 30 January 2013 (UTC)
Through 2012 September, The RAII entry spent more text on usefully showing the bonding together of creating a data object and acquiring auxiliary resources, and showing how falling out of scope could release those auxiliary resources. It had examples in C++, Java, Ruby, Python, and Smalltalk which all essentially showed RAII support (despite the section being conflictingly titled "Resource Management without RAII"), using library calls and closure blocks which all essentially show that the languages can trigger the destructor when needed.
The fact that the first mentioned use for RAII in the article was given as mutex locks is fairly esoteric for some programmers, but the C++ example does show two implicit examples of RAII through C++ features, one for the mutex and one for the file.
The "GCC extensions for C" subsection, about autodeallocation, which is related to RAII despite being in the wrong section, only covers deallocation on falling out of scope. Although useful, it would be more edifying in a complete example showing the allocation part as well, as the old, long C example did (up to 2012 September or so) with the RAII macro automatically calling the destructor for the C object at the end of the scope. Extract:
#define RAII(t, v, a,...) \
for(int _outer_once = 1, _inner_once = 1, _jumped = 0 ; _outer_once-- ; \
_jumped && (Throw(_jumped), 1), 1) \
for(_jumped = setjmp(_jmp_bufs[_jmp_i++]) ; _inner_once-- ; ) \
for( t *v = t ## New(a, __VA_ARGS__) ; v ; t ## Delete(&v))
[...]
RAII(File, from, "file-from", "r+") { /* r+ to ban opening dirs */
RAII(File, to, "file-to", "w") {
int c;
while(EOF != (c = FileGetc(from)))
FilePutc(to, c);
puts("copy complete");
}
}
Note that although that code does use variadic macros and some recent standardized C features, it's not strictly confined to GCC, whereas the RAII_VARIABLE macros appears to be far less portable.
That "Ad-hoc mechanisms" section was in one key way one of the most interesting (alert: I'm biased, I wrote it, and I agree shorter would be nice, but it's not easy to accomplish) because instead of just using a language feature that implements key RAII components implicitly, it instead had to *explicitly* show how the initialization was part of the declaration, and how the destructor was called even in the face of errors.
Renaming that section from "Ad-hoc mechanisms" to "Implementing RAII in non-RAII Languages" would really have been a more reasonable choice than simply claiming "ad-hoc work-arounds are in general of little encyclopedic value" and tossing it.
Further, it also addressed a lot of the tradeoffs involved in retrofitting RAII to languages without direct support, the benefits achieved with RAII, and so on. That part of the article is still available in its original form at http://www.talisman.org/~erlkonig/documents/ad-hoc-exceptions-in-c/
(Aside; the the second C example was inferior, essentially being an example of a way to avoid having deeply-nested blocks to handle failures. While common in kernel code, it was not RAII - the uninitialized FILE pointers and reliance on specific resource-release calls have nothing to do with RAII. Dropping that one made sense.)
So I'm not very happy with what's left of the RAII entry, but order to work around what bias I might have, I'd like to leave it up others going forward.
C. Alex. North-Keys ( talk) 21:52, 5 August 2013 (UTC)
This, like the cleanup extension in gcc, seem to address only automatic destruction of local-scope resources. Although that's one part of RAII, at least I consider smartpointers as another equally important part, allowing scopes to give out allocated resources, but still have them subject to automatic destruction. The only "real" RAII I have seen done in C is my implementation of smartpointers ( https://github.com/psevon/exceptions-and-raii-in-c). It provides functionality similar to unique_ptr and shared_ptr in C++ (not weak_ptr at the moment, but it can be easily added if necessary), allowing shared or unique ownersip to the constructed resources, and transfer of ownership to outside the scope, even to another thread. It keeps track of things and initiates cleanup by macros replacing the braces, and macro replacement for return. It works by keeping a cleanup stack in the heap memory, which makes it possible to use setjmp/longjmp based exception mechanism, or exit a scope without calling the correct macro and still have all the resources from the unwound scopes implicitly destructed, in the catch block or when doing the next clean exit from a scope. — Preceding unsigned comment added by Psevon ( talk • contribs) 19:22, 10 April 2014 (UTC)
In the sentence "In RAII, holding a resource is tied to object lifetime: resource allocation (acquisition) is done during object creation...", should the colon after "lifetime" be a period or a semicolon? — Preceding unsigned comment added by 188.126.200.132 ( talk) 19:31, 11 July 2014 (UTC)
The line
"Ownership of dynamically allocated objects (memory allocated with new in C++) can also be controlled with RAII, such that the object is released when the RAII (stack-based) object is destroyed."
is incorrect as written because of the "stack-based" modifier. It could be made "more correct" by simply removing the modifier, but it still misses the point because it is not just "dynamically allocated objects" that can be managed, and you typically do not want dynamic allocation unless you are explicitly not stack-based. The only time you use dynamic allocation with scope lifetime is when you need more memory than the stack can allow or need special memory permissions, both fairly rare.
When you have an interface that can receive asynchronous events, though, and you need something to have a lifetime that lasts longer than the first event scope, that's when you typically need dynamic allocation. This is what State Machines were created for. State Machines are RAII for asynchronous events. A State is what manages the automated lifetime between the two events. Everything the State holds (everything contained in the object) is automatically created when the State is created and destroyed when the State is destroyed. This solves exactly the same problems that scope based RAII solves:
In fact, there are actually four lifetimes, all of which are managed by RAII:
In all these cases, referentiality equals lifetime and management is automated by the simple declaration. That is RAII, and a good article should mention these things. Ex0du5 5utu7e ( talk) 22:23, 3 December 2014 (UTC)
Perhaps I'm missing something, but there appears to be a serious error in the C++ coding sample: the destructor of the static mutex instance will not be called until the program exits. The static variable may not be visible outside the scope of the function in which it resides, but it is nevertheless global. So if we rely on the destructor to unlock the code, then the mutex will remain locked after the first call.
It looks to me that to make this function work would remove most of the RAII characteristics that it is trying to demonstrate.
Maybe a better example of RAII would be a function that returns an instance of an object? I.e., the called method allocates the instance on the stack, automatically copies the instance to the return value, which is deleted when it goes out of scope in the calling routine.
Rstinejr ( talk) 13:39, 29 December 2014 (UTC)
Yes, why is the mutex static? It is my believe that that will give the mutex a life-time longer than the procedure's stack-frame. Thus, the destructor will not execute when the file no longer needs exclusive access. I'm going to change the example, and see if anyone complains. -- 129.33.132.9 ( talk) 18:30, 13 May 2015 (UTC)
The mutex need to be static to prevent multiple threads from calling the function simultaneously. That would lead to concurrent access to the same file. RAII still applies to the lock object, which is not static. Fredrik Orderud ( talk) 19:40, 13 May 2015 (UTC)
Thanks Fredrik Orderud! 😃 172.56.39.30 ( talk) 16:34, 23 May 2015 (UTC)
There is problem with the static mutex. The static mutex is being constructed when the function is invoked the first time. This is fine as long as it is single threaded. I don't know if there is anything in the C++ standard, which guaranties that such a function can be invoked from multiple threads at the same time. Moving the mutex into the global scope would solve this. See also https://stackoverflow.com/questions/6915/thread-safe-lazy-construction-of-a-singleton-in-c PF~~ — Preceding unsigned comment added by 139.181.28.34 ( talk) 21:18, 9 October 2017 (UTC)
C++11 and newer guarantees that static variables are initialized in a thread-safe manner ( http://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables). Therefore, the example is safe & correct. Fredrik Orderud ( talk) 16:03, 10 October 2017 (UTC)
Does anyone have WP:V sources that can confirm this? Ushkin N ( talk) 17:26, 30 May 2016 (UTC)
I haven't found the cppcon talk calling it DRES (Destruct Resources on Exit Scope) yet but here is one reference in the meantime: C++ mingw g++ and Notepad++ — Preceding unsigned comment added by JeffyTheDragonSlayer ( talk • contribs) 09:07, 9 January 2020 (UTC)
This is the
talk page for discussing improvements to the
Resource acquisition is initialization article. This is not a forum for general discussion of the article's subject. |
Article policies
|
Find sources: Google ( books · news · scholar · free images · WP refs) · FENS · JSTOR · TWL |
This article is rated C-class on Wikipedia's
content assessment scale. It is of interest to the following WikiProjects: | |||||||||||||||||||||||||||
|
This article links to one or more target anchors that no longer exist.
Please help fix the broken anchors. You can remove this template after fixing the problems. |
Reporting errors |
The C++ Programming Language (Bjarne Stroustrup) (2nd edition) ISBN 0-201-53992-6 : Ch9 "Exception handling" 9.4.1 "Constructors and Destructors" "This technique for managing resources is usually referred to as 'resource acquisition is initialization'. "
This hints at an earlier usage. 51.9.104.223 ( talk) 11:44, 20 May 2021 (UTC)
The Ruby/Python examples aren't RAII. The only difference between the python/ruby versions and the Java try/finally approach is that the cleanup method is not shown. Correct usage of a file object (or whatever other resource) still requires that you always use the object within a 'with' block (or whatever the special block is called).
216.39.239.100 21:10, 17 July 2007 (UTC)
And the correct (or rather, RAII enabled) usage of a file object in C++ requires that you always use a scoped object instead of allocating it with new. Stroustrup says: "The key idea behind the ‘‘resource acquisition is initialization’’ technique/pattern (sometimes abbreviated to RAII) is that ownership of a resource is given to a scoped object." This is precicely what Python and Ruby's blocks do. RAII can be used in Java as well by using a callback object, but since defining an anonymous class is so complex (and they have some limitations), everyone just uses try/finally instead. 80.221.27.192 11:25, 1 December 2007 (UTC)
In C++, in any case the lifetime of the variable defines the lifetime of the resource. This is because the variable is the object, and the object's destructor is called when the variable goes out of scope. Using "new" does not change this, but you now have a variable of a pointer type, so the pointer's RAII is not buying you anything unless you use a magical smart pointer of some sort. The object is not tied to the pointer in any way, so the pointer's lifetime does not influence the object and the pointer's destruction does not cause the object to be destructed.
The same thing happens in Ruby. If you just said foo = File.open('foo.txt'), the variable is scoped, but if it goes out of scope, the object it refers to lives on. In the File.open('foo.txt') do |foo| end example, the block is merely a callback and the open method explicitly has to take care of the resource cleanup. The scoped variable's lifetime actually ends before that, but that is not what causes the cleanup, so no RAII is taking place.
129.13.186.1
21:32, 2 December 2007 (UTC)
A line like foo = File.open('foo.txt') may give you a nice scoped autodestructing variable, but it doesn't tell you *anything* about what was done to make sure that the object foo refers to was carefully initialized. Basically this hides all the joy of RAII in a library function, and then asks the reader to imagine that true RAII must have happened inside of that black box. The example where that File.open() is wrapped in the creation of a larger object, on the other hand, *is* more demonstrative of RAII, I just wish the examples actually *on* on the article page were as relevant (though showing the class in use would be good). -- C. Alex. North-Keys ( talk) 09:46, 6 October 2015 (UTC)
#include <cstdio> // exceptions class file_error { } ; class open_error : public file_error { } ; class close_error : public file_error { } ; class write_error : public file_error { } ; class file { public: file( const char* filename ) : m_file_handle(std::fopen(filename, "w+")) { if( m_file_handle == NULL ) { throw open_error() ; } } ~file() { std::fclose(m_file_handle) ; } void write( const char* str ) { if( std::fputs(str, m_file_handle) == EOF ) { throw write_error() ; } } void write( const char* buffer, std::size_t num_chars ) { if( num_chars != 0 && std::fwrite(buffer, num_chars, 1, m_file_handle) == 0 ) { throw write_error() ; } } private: std::FILE* m_file_handle ; // copy and assignment not implemented; prevent their use by // declaring private. file( const file & ) ; file & operator=( const file & ) ; } ;
class MyFile def initialize(filename, mode, &block) begin handle = File.open(filename, mode) if block_given? block.call(handle) else puts "No block given for RAII" end rescue IOError => ex puts "Got a #{ex.exception} error!" raise #re-raise the exception up the call stack ensure handle.close unless handle.closed? end end end # ... irb(main):018:0> MyFile.new("foo.bar.baz.txt","r") {|f| f.write "foo" } Got a not opened for writing error! IOError: not opened for writing from (irb):18:in `write' from (irb):18 from (irb):6:in `call' from (irb):6:in `initialize' from (irb):18:in `new' from (irb):18 from :0
This is ridiculous nonsense. RAII is a specific way to allocate and release resources ... some totally other way to do it (such as using "ensure") is not RAII. " Rather than an explicit destructor" -- if there is no explicit destructor then it's not RAII. Period. -- 71.102.128.42 ( talk) 02:27, 17 November 2014 (UTC)
Any tips for RAII in Java? Wouter Lievens 12:39, 11 Mar 2005 (UTC)
RAII is not applicable to Java because Java lacks determinstic destruction. In C++ a major concern and use of RAII is memory management. In Java, garbage collection handles memory but there are other types of resources that would benefit from the RAII pattern. Unfortunately the closest thing you can get in Java is the dispose pattern.
This is probably about as close as you can come to the idiom:
public interface Factory<T,E extends Throwable> {
public T create() throws E;
}
import java.io.*;
public abstract class IoRAIIBlock<T extends Closeable> {
private final Factory<T,IOException> factory;
public IoRAIIBlock(Factory<T,IOException> factory) {
this.factory = factory;
}
public final void go() throws IOException {
T obj = factory.create();
try {
run(obj);
} finally {
obj.close();
}
}
public abstract void run(T arg1) throws IOException;
}
public abstract class FileWriterRAIIBlock extends IoRAIIBlock<FileWriter> {
public FileWriterRAIIBlock(final String filename) throws IOException {
super(new Factory<FileWriter,IOException>() {
public FileWriter create() throws IOException {
return new FileWriter(filename);
}
});
}
// more constructors if you want...
}
public class Example {
public static void main(String[] args) throws IOException {
new FileWriterRAIIBlock("logfile.txt") {
public void run(FileWriter logfile) throws IOException {
logfile.append("hello logfile!");
}
}.go();
}
}
I guess if you were feeling really evil, though, you might be able to use AspectJ to insert implicit resource disposal. -- pfunk42 ( talk) 06:30, 17 May 2008 (UTC)
Since version 1.7 (2011), Java has supported the ' try-with-resources' statement, which I often use for RAII. It is not memory-based release, but the guarantees of calling the 'close()' method upon exiting the block provide the same abilities as stack-unwinding destruction. Pedorro ( talk) 19:22, 23 February 2022 (UTC)
What about removing the stub template? I found this article most helpfull.
Who uses RIIA? Googling for "Resource Initialization Is Acquisition" only returns 75 results, most are from mailing lists. DanielKO 00:20, 16 March 2006 (UTC)
Part of the point of RAII is that the code be exception-safe. The example is not necessarily exception safe because it can throw from a destructor (CloseFile can fail). Throwing from a destructor is poor coding practice in C++ (see Exceptional C++ by Herb Sutter).
class RAII { public: RAII () { res.Acquire (); } void ExplicitRelease() { res.Release (); } ~RAII() { try { res.Release (); } catch (...) {} } };
—The preceding unsigned comment was added by 83.216.62.233 ( talk) 10:49, 11 February 2007 (UTC).
I'm with 83.216.62.233. This requires some change to the article. When reading a file, it's probably ok to do it like this:
{ File f("foo.txt"); cout << f.readLine(); }
..since I can't think of a case when the destructor could throw after a successful read. Many resources can be released without the possibility of failure, so this is how simple RAII is usually. But the example writes to a file, so it should be written like this:
{ File f("out.txt"); f.write("hello"); f.close(); }
In this case, Python and Ruby blocks are in fact simpler because they handle release failure correctly and do not require an explicit .close() call.
So, what should be done to the article? This issue needs to be covered better, that much is certain. It may not apply to D (maybe D's destructors can throw without causing abort when unwinding -- I don't know). Should the example use a scoped_ptr, or read from a file, to demonstrate the simple RAII case that requires no explicit close()? I'll probably change the article at some point.. (edit:) A further "issue" with files is that after closing them explicitely, all the operations must throw an exception. This requires some code bloat (I'd rather see an even shorter example than the current one), and the resource is no longer bound to the object's lifetime, which is supposed to be essential to RAII. 80.221.27.192 11:47, 1 December 2007 (UTC)
This article as a whole has been pretty fluffy for a long time, with C++ shoed in (for example) despite hiding most of the useful aspects that relate to resource acquisition == initialization, and instead highlighting irrelevant sidetracks like mutexes, exceptions, and so forth with examples that utterly lack an explicit A==I part. Unless an example *explicitly* shows something like the code that only returns a created object IFF it's initialized (and not by something buried in a library call used in the example, but actual code), then it has no reason to be in this article. That includes code that only talks about how to get rid of objects -- this isn't an article on resource Deletion, but Acquisition. It's been about 3 years since RAII was demonstrated in the examples, with the article as a whole becoming steadily less useful, largely due to massive deletions by Forderud in 2012 who seemed to think that demonstrating approaches to implementing RAII was outside of the scope of an article *about* RAII. -- C. Alex. North-Keys ( talk) 09:42, 6 October 2015 (UTC)
The C example is looks much more complicated than necessary. What about this, that is much better suited for maintenance:
int c_example() {
int retval = -1; // assume error
FILE *f = NULL;
do {
f = fopen("logfile.txt", "w+");
if (f == NULL) {
retval = -1;
break;
}
if (fputs("hello logfile!", f) == EOF) {
retval = -2;
break;
}
// continue using the file resource
retval = 0; // return value 0 is success
} while (0);
// Releasing resources (in reverse order)
if (f != NULL && EOF == fclose(f)) {
retval = -3;
}
return retval;
}
-- 87.163.200.60 ( talk) 11:38, 25 September 2008 (UTC)
The c_example() above, like the one that used to be in the article, only demonstrates the difficulty of dealing with exceptions, not RAII itself. Although talking about both together makes a lot of sense, the topic of exceptions in isolation would be seen in a Wiki article specific to exception handling.
The example on the page now shows where ad-hoc mechanisms in C to emulate RAII tend to lead, with non-local exception handling implemented in conjunction with RAII, and the resulting problems that underscore how having RAII as a language builtin are beneficial. C. Alex. North-Keys ( talk) 06:35, 18 October 2010 (UTC)
Why is this code "much better suited for maintenance"? I think it's just longer and using a loop to simulate gotos is bad form. Also the goto idiom is popular; see for example Linux source code. Matonen ( talk) 13:57, 15 April 2009 (UTC)
The second C isn't particularly useful: there are plenty of things RAII isn't, but this article should focus on things it is. It also reads like an advocacy, rather than an explanation. I intend to replace it with an example of a goto-based RAII, because it is an exceedingly common idiom, as evidenced by its usage in the Linux kernel. I will also include a small blurb about the obvious tradeoffs between the current example and the goto one. 71.188.88.67 ( talk) 06:24, 31 May 2012 (UTC)
Now the article seems to suggest that managing the resource FILE_HANDLE with a class is RAII. No, that's just basic abstraction and clearly quite possible in Java as well. Without RAII you'd have almost the same LogFile class but you'd have to manually call "close". The code would be more like this:
bool functionB() { LogFile log("Output log of functionB"); //if this throws, no closing is needed since nothing was opened try { log.write("fun Step5 succeeded"); // if the function needs to check for an error and abort accordingly: if( ... ) throw FunctionAFailure(); // EXTRA CODE: the resource needs to be closed explicitly log.close(); // explicitly release the resource return true; } catch (...) { // An exception was thrown during the execution of the function // Time to cleanup the resources log.close(); throw; } }
I think this is more accurate than the current article's functionB.
Your example here doesn't make sense to me. A LogFile class with close() would be called a poor design which fails to use constructors and destructors correctly. Namely, the class invariant is violated once you call close(). It would be a textbook case of misuse/misunderstanding of C++, that is, a failure to understand RAII. Xerxesnine 07:24, 23 February 2007 (UTC)
What class invariant would that be? Did I tell you what the class invariants are? No, you arbitrarily decided that the class should have invariant X and saw that the class doesn't conform to X. How silly. On top of that fallacy, you completely misunderstood what I wrote. I did not suggest this as a good way to write C++, but as a change to the currently presented example to better highlight the differences between RAII and non-RAII approaches. Do you think the current non-RAII example code shows a good understanding of RAII? Of course it doesn't because that's the whole point of the example. That's the whole point of my code as well, since it's meant to replace the one presented in the article. Currently the article shows the difference between non-abstracted non-RAII code and abstracted RAII code. When we want to highlight the benefits that RAII gives you, the examples should obviously be of abstracted non-RAII (my code above) and abstracted RAII -- With only the RAII part being different. —Preceding unsigned comment added by 80.221.24.224 ( talk • contribs)
std::FILE* file_handle = std::fopen("logfile.txt", "w+") ; if( file_handle == NULL ) { throw open_error() ; }
std::FILE* file_handle = open_or_throw("logfile.txt", "w+") ;
I feel parts of this article contradicts itself.
To me, RAII means resource management through statically predictable destruction — in C++ by auto variables or std::auto_ptr, or by the lifetime of some larger object having the resource-owning one as a member.
The part that speaks about std::auto_ptr seems to agree with that definition. Another part that speaks of "languages that do not support RAII, such as Java" seems to imply that try ... finally isn't RAII. A third part states that manually calling a finalize() method in languages like Java is RAII.
I suppose you can say you use RAII if you consider your resource FOO as owned by object FOO in general ... but isn't that a more general idea, a basic tool of any object-oriented programming? JöG 20:35, 3 September 2006 (UTC)
Agreed. The final word in the acronym, initialization, refers to the construction of an object where resources are acquired. If you're not doing that, then it's not RAII. Java does not support RAII, but instead has the try-finally facility to guide resource management. I just changed the "Limitations" section to remove this confusion. Xerxesnine 12:49, 23 February 2007 (UTC)
The RAII pattern is used ubiquitously in object-oriented Perl code. In fact, Perl programmers don't even call it RAII, or anything else, since it is simply normal behaviour. Perl's dynamic nature makes it more flexible than C++, so you can safely write:
sub open_it { return new IO::File("it.dat"); }
in Perl, and the file will be automatically closed when you were done with it, whereas in C++ if you did
std::ofstream open_it() { return std::ofstream("it.dat"); }
it would fail to compile, and if you did
std::ofstream *open_it() { return new std::ofstream("it.dat"); }
it would compile but you'd need to explicitly delete the pointer to close the file.
Unfortunately, I can't find a way to cram this vital information into the article. Perhaps someone could add a section for languages that use RAII as standard practice (assuming there are any others)?
-- 61.214.155.14 05:31, 22 November 2006 (UTC)
std::auto_ptr<std::ofstream> open_it() { return std::auto_ptr<std::ofstream>(new std::ofstream("it.dat")); }
My first reaction is that this new section is ugly and superfluous. A simple class wrapper for OpenSocket() and CloseSocket() is just the right thing to do. In contrast, the scoped_handle<> template obscures the programmer's intention.
My inclination is to remove this section entirely, as it contributes nothing to the concept of RAII and appears to be an exercise in unwarranted cleverness. Xerxesnine 22:27, 31 March 2007 (UTC)
Release failure is a complex situation with RAII. The various approaches have been discussed at length on comp.lang.c++ and comp.lang.c++.moderated, all of which are probably inappropriate for an introduction to RAII. That's why I made a simple assert in the destructor; it seems better to do something than to simply leave it unchecked as Intgr wished. Think of it as a placeholder for user-defined functionality. Xerxesnine 14:19, 15 April 2007 (UTC)
So a cryptic error message stating "whatever.cpp:553: file::~file(): Assertion `!"file failed to close"' failed." is somehow considered more meaningful to the user than a pop-up stating "error writing file, your changes may be lost"?
Please read the comp.lang.c++.moderated link I gave before continuing down this line of thought. That's why I gave it to you. You are also missing the context of this simple example; we obviously can't include pop-up dialogue box code.
Assertions are useful for terminating when we detect an undefined state, at which time continuing execution may do real damage. The loss of a file handle is one such case, which may indicate a hardware failure.
You cannot conditionally choose which asserts are included and which are not, unless you are using nonstandard assert macros. Assertions are also useful in inner loops, especially when thrown out of release builds.
Yes, you can make conditional use of asserts within the same build, with the standard NDEBUG macro if desired. That's the whole point of using assertions. That you don't understand this is another indication of the fact that you rarely write C++. Your confusion over assert(!"bad state") is yet another indication, among others.
You continue to provide needless informational comments about how assert() works. Your first post on this thread consisted of entirely of such comments. Be assured that I understand. In light of your inexperience with C++, you might be more productive if you reconsidered your role as "educator" here, engaging with others as peers instead.
The overall issue is a practical one. How are we to handle the release error? There is no good answer because the usual solutions are complex and therefore inappropriate for this introductory example. My suggestion was a simple assert. Your first suggestion was to ignore the error. That is universally unacceptable. Your second suggestion was a std::cerr notice, but as I said that is almost the same thing but worse: it lacks the file, line number, and hardware breakpoint which assert() normally entails.
So, what do you suggest as an alternative to the assert()?
Xerxesnine 16:04, 16 April 2007 (UTC)
I wish to completely demolish all of these arguments so I don't have to deal with them again.
Xerxesnine 03:18, 17 April 2007 (UTC)
Response to Intgr:
As if this steady stream of misinformation coming from Intgr was not enough, I have counted three outright troll remarks from him. This, together with his two reverts on the fclose() checks, is an firm indication of bad faith.
Xerxesnine 15:37, 17 April 2007 (UTC)
It turns out I misread fclose() even after re-checking it. At http://www.die.net/doc/linux/man/man3/fclose.3.html for example, it lists EBADF, which seems to imply that it's the only relevant error code. The next sentence is "The fclose() function may also fail and set errno...", but I read that as "The fclose() function may fail to set errno...", which would explain why only EBADF was listed, in my mind. I put these together and thought the ISO C standard only set a single failure flag for fclose(), leaving the rest to the particular platform API.
So it turns out the fclose() indeed sets useful errnos. I can now imagine legitimate code which checks standard ISO errons before the assert() happens, whereas before I believed one could only use the platform API before the assert(). While I would still put an assert() when all other checks fail or are bogus, that assert() seems much farther away now, and this changed my mind about it.
I apologize to Intgr for wasting his time on this. Mea culpa.
Xerxesnine 01:29, 18 April 2007 (UTC)
Boy this Xerxeresnine character was a jerk, and incompetent too. You should never ever ever use the assert macro for anything other than logic errors. -- 98.108.208.207 ( talk) 13:25, 15 July 2008 (UTC)
Indeed. That exchange shows him to be an incompetent moron. Let's hope none of his code has killed anyone. -- 71.102.128.42 ( talk) 02:57, 17 November 2014 (UTC)
I don’t know who invented the term, but it’s highly misleading: RAII deals neither with resource acquisition nor with initialization. RAII is automatic resource releasing upon leaving scope, whether via throw, break, continue, return or by control reaching the end of block. As in the following code:
{ std::auto_ptr<X> x; // initialization doSomething(); // If this throws, auto_ptr::~auto_ptr does nothing dangerous. x.reset(new X); // Resource acquisition. Done by programmer and not by RAII class! doSomethingElse(); // If this throws, the X object is released. // If nothing throws, ‘x’ is released here. }
Roman V. Odaisky 13:02, 6 June 2007 (UTC)
Since your example does not acquire resources during initialization, it does not demonstrate "resource acquisition is initialization", or RAII. I believe Stroustrup introduced the term in his 3rd ed. book, where he gives (something similar to) this example:
class log { database_lock lock ; file output ; public: log( database_handle db ) : lock(db), output("output.txt") { } } ;
If the lock fails, an exception is thrown and the opening of "output.txt" is never attempted. If the lock succeeds but the output file fails to open, then an exception is thrown which results in the the release of the lock. If both succeed, and an exception is thrown at some point during the scope of the log instance, then both resources are released.
The strategy guarantees that resources are acquired and released in the correct order (FILO/LIFO) and that objects are always in a valid state. If an object cannot be created in a valid state, that is, if the object cannot acquire the needed resources, then we cease the current execution by throwing.
In your example, you have elected to construct an invalid object (any use of auto_ptr is undefined until it is given a valid pointer). This positively demonstrates what RAII seeks to remedy. Xerxesnine 17:43, 21 June 2007 (UTC)
std::auto_ptr<int> a ; *a = 1 ; // crash
The tr1::shared_ptr (or if you prefer boost::shared_ptr), taken as an example, is an example of RAII for which resource acquisition doesn't necessarily occur when the constructor is called, and resource relinquishment doesn't necessarily occur when the destructor is called. That RAII is about acquiring a resource in the constructor and releasing it in the destructor is merely a lie to children. What RAII is really about is that the object is responsible for the management of a resource, and its destruction should guarantee its cleanup, which is quite distinct. —The preceding unsigned comment was added by 75.130.108.174 ( talk • contribs) 15:10, 8 August 2007.
The technique for managing resources using local objects is usually referred to as ''resource acquisition is initialization.'' This is a general technique that relies on the properties of constructors and destructors and their interaction with exception handling.
auto_ptr<int> x(new int(5));
The basic tools for writing exception-safe code are: ... (2) the support for the ''resource acquisition is initialization'' technique. The key idea behind the ''resource acquisition is initialization'' technique/pattern (sometimes abbreviated RAII) is that ownership of a resource is given to a scoped object. Typically, that object acquires (opens, allocates, etc.) the resource in its constructor. ...
PHP5 introduces destructors. Does anyone know if they are guaranteed to run in a timely fashion, for example, when a static object passes out of scope? If so, then this technique could be used in PHP5. Either way, the article should mention whether or not the technique is applicable to that language. - Jmabel | Talk 17:37, 21 February 2008 (UTC)
GNU C adds a cleanup attribute that helps plain C code in exceptional cases. —Preceding unsigned comment added by 17.201.20.87 ( talk) 19:24, 27 February 2008 (UTC)
I believe better wording to replace "the automatic, deterministic destruction" would be "the implicit, synchronous destruction". I believe "automatic" is not as informative as "implicit" and "deterministic" is not as informative as "synchronous". In fact, I think "deterministic" can be misleading because the destructor could be implemented with non-deterministic characteristics (such as calling fclose, which does not have a deterministic result). But I get the impression someone loves the existing wording, since it also shows up in the Java vs. C++ article, so I would like your comments. -- pfunk42 ( talk) 05:25, 17 May 2008 (UTC)
See [4]. So one could use the exact same form of the c++ RAII pattern with c#. 64.234.67.2 ( talk) 05:53, 29 December 2008 (UTC)
public class MyFileWriter : IDisposable { private StreamWriter _swriter; private StreamWriter Writer { get { return _swriter; } set { _swriter = value; } } public MyFileWriter (string filename) { try { Writer = new StreamWriter (filename); } catch (Exception ex) { throw new ApplicationException ("file open failure", ex); } } public void Dispose () { try { Writer.Close (); } catch (Exception ex) { // handle it } } public void Write (string str) { try { Writer.Write ("{0}\n", str); } catch (Exception ex) { throw new ApplicationException ("file write failure", ex); } } } class TestMyFile { public static void Main() { using (MyFileWriter logfile = new MyFileWriter ("logfile.txt")) { logfile.Write ("hello logfile!"); } } }
Googling "mutable RAII" gives 7 hits, many of which are derivatives from this Wikipedia article. "Immutable RAII" fares a little better, giving 171 hits. Some of the hits are for Wikipedia article, some are to discussions where Matthew Wilson, author of the term, is using it. Or to excerpts from Matthew's books. Clearly the term has not caught on, and should not be in Wikipedia. I'll go ahead and remove the mutability section if there's no objection. Matonen ( talk) 13:45, 15 April 2009 (UTC)
Do you guys think the acronym ```SBRM``` (scope-bound resource management) deserves to be included here too? It seems to be more descriptive of what happens here. I can't seem to track down the origin with quick googling though - just various blogs and forums, starting about 2007 and getting more common by 2010. -- Cubbi ( talk) 18:06, 25 August 2010 (UTC)
The RAII concept can be understood with just the C++ example given. "Resource management without RAII" is an ever-growing section with everyone adding a remark or example about their favourite language, and the new "Ad-Hoc Mechanisms" section is longer than the C++ example itself (not to mention original research). -- Matonen ( talk) 14:20, 20 October 2010 (UTC)
This article, especially the comparison to other forms of resource management is incomplete without a section that delves into the subject of compositional properties of the different resource management technologies. The single most compelling property of RAII when compared to try/finally lies in how RAII works with 'deep' resources. In a pure GC+try/finally world, 'being a resource' is transitive when used in composition. In a RAII world it isn't. If in a GC+try/finally world an object contains a 'deep' resource, than this object requires manual resource management at the level of the user of the composite object. With RAII the resource (and thus the resource management) is fully captured at the deeper levels of composition and neither the higher levels of composition nor the user of the composite object are exposed to the fact that the there is a deep resource in there. That is, RAII provides the possibility of encapsulation of resources, while for the alternative, composition will brake encapsulation.
One thing that aggravates the problem for alternative resource management technologies (GC+try/finally) is polymorphism. Combined with polymorphism, not only 'holding' a deep resource makes a composed object require manual resource management, but also composition using any polymorphic component will turn a composite object into an object needing manual resource management. As RAII doesn't have the encapsulation problems it also does not have the polymorphism problems that GC+try/finaly has. —Preceding unsigned comment added by Pibara ( talk • contribs) 09:22, 11 May 2011 (UTC)
Given that there was absolutely no response to the above, I'vv added a section "Disadvantages of scope bound resource management alternatives" to the page, hope this is OK. Pibara ( talk) 07:40, 5 July 2011 (UTC)
decltype
(
talk)
10:17, 5 July 2011 (UTC)The article seems to assume that RAII is only about scope-based automatic construction and destruction of objects. It omits an extremely important part of RAII, namely copying and assignment (which in C++ is implemented in the form of copy constructors and copy assignment operators). This is quite obvious as the article gives as examples of "RAII" the gcc extension "cleanup" and the Java "finally" block. That's only part of RAII. Those do not handle copying and assignment properly.
Copy semantics is what allows, for example, returning objects from functions by value, giving objects as value-based function parameters, copying objects (obviously), creating entire arrays of objects (each element being default-initialized or initialized as a copy of an existing object) and so on, while still fully adhering to the RAII mechanism. This is most certainly not covered by "finally" blocks or a "cleanup" attribute.
Another aspect that's missing is that RAII works in C++ even on the heap: As long as the container structure is allocated and deallocated properly, the contained objects will follow the RAII rules appropriately. (This is the reason why, for example, the standard library data structures do not need to know how their elements are copied and moved around, as RAII takes care of that.)
Comparing RAII to Java's "finally" blocks is, at the very least, misleading. — Preceding unsigned comment added by 84.249.92.156 ( talk) 06:25, 12 April 2012 (UTC)
The article seems to imply that there is no RAII in C#, but this is not true: it merely works in a different way, with more explicit syntax, as already described in this talk page (see C# example above).
This has nothing to do with C# not having deterministic memory deallocation, as RAII is not fundamentally about memory allocation. For example, this C# example is RAII, since reader and writer objects are automatically disposed (i.e. closed) upon exit from the using scope. 212.92.211.195 ( talk) 14:32, 30 January 2013 (UTC)
Through 2012 September, The RAII entry spent more text on usefully showing the bonding together of creating a data object and acquiring auxiliary resources, and showing how falling out of scope could release those auxiliary resources. It had examples in C++, Java, Ruby, Python, and Smalltalk which all essentially showed RAII support (despite the section being conflictingly titled "Resource Management without RAII"), using library calls and closure blocks which all essentially show that the languages can trigger the destructor when needed.
The fact that the first mentioned use for RAII in the article was given as mutex locks is fairly esoteric for some programmers, but the C++ example does show two implicit examples of RAII through C++ features, one for the mutex and one for the file.
The "GCC extensions for C" subsection, about autodeallocation, which is related to RAII despite being in the wrong section, only covers deallocation on falling out of scope. Although useful, it would be more edifying in a complete example showing the allocation part as well, as the old, long C example did (up to 2012 September or so) with the RAII macro automatically calling the destructor for the C object at the end of the scope. Extract:
#define RAII(t, v, a,...) \
for(int _outer_once = 1, _inner_once = 1, _jumped = 0 ; _outer_once-- ; \
_jumped && (Throw(_jumped), 1), 1) \
for(_jumped = setjmp(_jmp_bufs[_jmp_i++]) ; _inner_once-- ; ) \
for( t *v = t ## New(a, __VA_ARGS__) ; v ; t ## Delete(&v))
[...]
RAII(File, from, "file-from", "r+") { /* r+ to ban opening dirs */
RAII(File, to, "file-to", "w") {
int c;
while(EOF != (c = FileGetc(from)))
FilePutc(to, c);
puts("copy complete");
}
}
Note that although that code does use variadic macros and some recent standardized C features, it's not strictly confined to GCC, whereas the RAII_VARIABLE macros appears to be far less portable.
That "Ad-hoc mechanisms" section was in one key way one of the most interesting (alert: I'm biased, I wrote it, and I agree shorter would be nice, but it's not easy to accomplish) because instead of just using a language feature that implements key RAII components implicitly, it instead had to *explicitly* show how the initialization was part of the declaration, and how the destructor was called even in the face of errors.
Renaming that section from "Ad-hoc mechanisms" to "Implementing RAII in non-RAII Languages" would really have been a more reasonable choice than simply claiming "ad-hoc work-arounds are in general of little encyclopedic value" and tossing it.
Further, it also addressed a lot of the tradeoffs involved in retrofitting RAII to languages without direct support, the benefits achieved with RAII, and so on. That part of the article is still available in its original form at http://www.talisman.org/~erlkonig/documents/ad-hoc-exceptions-in-c/
(Aside; the the second C example was inferior, essentially being an example of a way to avoid having deeply-nested blocks to handle failures. While common in kernel code, it was not RAII - the uninitialized FILE pointers and reliance on specific resource-release calls have nothing to do with RAII. Dropping that one made sense.)
So I'm not very happy with what's left of the RAII entry, but order to work around what bias I might have, I'd like to leave it up others going forward.
C. Alex. North-Keys ( talk) 21:52, 5 August 2013 (UTC)
This, like the cleanup extension in gcc, seem to address only automatic destruction of local-scope resources. Although that's one part of RAII, at least I consider smartpointers as another equally important part, allowing scopes to give out allocated resources, but still have them subject to automatic destruction. The only "real" RAII I have seen done in C is my implementation of smartpointers ( https://github.com/psevon/exceptions-and-raii-in-c). It provides functionality similar to unique_ptr and shared_ptr in C++ (not weak_ptr at the moment, but it can be easily added if necessary), allowing shared or unique ownersip to the constructed resources, and transfer of ownership to outside the scope, even to another thread. It keeps track of things and initiates cleanup by macros replacing the braces, and macro replacement for return. It works by keeping a cleanup stack in the heap memory, which makes it possible to use setjmp/longjmp based exception mechanism, or exit a scope without calling the correct macro and still have all the resources from the unwound scopes implicitly destructed, in the catch block or when doing the next clean exit from a scope. — Preceding unsigned comment added by Psevon ( talk • contribs) 19:22, 10 April 2014 (UTC)
In the sentence "In RAII, holding a resource is tied to object lifetime: resource allocation (acquisition) is done during object creation...", should the colon after "lifetime" be a period or a semicolon? — Preceding unsigned comment added by 188.126.200.132 ( talk) 19:31, 11 July 2014 (UTC)
The line
"Ownership of dynamically allocated objects (memory allocated with new in C++) can also be controlled with RAII, such that the object is released when the RAII (stack-based) object is destroyed."
is incorrect as written because of the "stack-based" modifier. It could be made "more correct" by simply removing the modifier, but it still misses the point because it is not just "dynamically allocated objects" that can be managed, and you typically do not want dynamic allocation unless you are explicitly not stack-based. The only time you use dynamic allocation with scope lifetime is when you need more memory than the stack can allow or need special memory permissions, both fairly rare.
When you have an interface that can receive asynchronous events, though, and you need something to have a lifetime that lasts longer than the first event scope, that's when you typically need dynamic allocation. This is what State Machines were created for. State Machines are RAII for asynchronous events. A State is what manages the automated lifetime between the two events. Everything the State holds (everything contained in the object) is automatically created when the State is created and destroyed when the State is destroyed. This solves exactly the same problems that scope based RAII solves:
In fact, there are actually four lifetimes, all of which are managed by RAII:
In all these cases, referentiality equals lifetime and management is automated by the simple declaration. That is RAII, and a good article should mention these things. Ex0du5 5utu7e ( talk) 22:23, 3 December 2014 (UTC)
Perhaps I'm missing something, but there appears to be a serious error in the C++ coding sample: the destructor of the static mutex instance will not be called until the program exits. The static variable may not be visible outside the scope of the function in which it resides, but it is nevertheless global. So if we rely on the destructor to unlock the code, then the mutex will remain locked after the first call.
It looks to me that to make this function work would remove most of the RAII characteristics that it is trying to demonstrate.
Maybe a better example of RAII would be a function that returns an instance of an object? I.e., the called method allocates the instance on the stack, automatically copies the instance to the return value, which is deleted when it goes out of scope in the calling routine.
Rstinejr ( talk) 13:39, 29 December 2014 (UTC)
Yes, why is the mutex static? It is my believe that that will give the mutex a life-time longer than the procedure's stack-frame. Thus, the destructor will not execute when the file no longer needs exclusive access. I'm going to change the example, and see if anyone complains. -- 129.33.132.9 ( talk) 18:30, 13 May 2015 (UTC)
The mutex need to be static to prevent multiple threads from calling the function simultaneously. That would lead to concurrent access to the same file. RAII still applies to the lock object, which is not static. Fredrik Orderud ( talk) 19:40, 13 May 2015 (UTC)
Thanks Fredrik Orderud! 😃 172.56.39.30 ( talk) 16:34, 23 May 2015 (UTC)
There is problem with the static mutex. The static mutex is being constructed when the function is invoked the first time. This is fine as long as it is single threaded. I don't know if there is anything in the C++ standard, which guaranties that such a function can be invoked from multiple threads at the same time. Moving the mutex into the global scope would solve this. See also https://stackoverflow.com/questions/6915/thread-safe-lazy-construction-of-a-singleton-in-c PF~~ — Preceding unsigned comment added by 139.181.28.34 ( talk) 21:18, 9 October 2017 (UTC)
C++11 and newer guarantees that static variables are initialized in a thread-safe manner ( http://en.cppreference.com/w/cpp/language/storage_duration#Static_local_variables). Therefore, the example is safe & correct. Fredrik Orderud ( talk) 16:03, 10 October 2017 (UTC)
Does anyone have WP:V sources that can confirm this? Ushkin N ( talk) 17:26, 30 May 2016 (UTC)
I haven't found the cppcon talk calling it DRES (Destruct Resources on Exit Scope) yet but here is one reference in the meantime: C++ mingw g++ and Notepad++ — Preceding unsigned comment added by JeffyTheDragonSlayer ( talk • contribs) 09:07, 9 January 2020 (UTC)