Jump to content

Talk:Resource acquisition is initialization: Difference between revisions

Page contents not supported in other languages.
From Wikipedia, the free encyclopedia
Content deleted Content added
→‎C# has had destructors since 1.1: C# has the same type of RAII as C++...
Line 694: Line 694:


See [http://msdn.microsoft.com/en-us/library/66x5fx1b(VS.71).aspx]. So one could use the exact same form of the c++ RAII pattern with c#. [[Special:Contributions/64.234.67.2|64.234.67.2]] ([[User talk:64.234.67.2|talk]]) 05:53, 29 December 2008 (UTC)
See [http://msdn.microsoft.com/en-us/library/66x5fx1b(VS.71).aspx]. So one could use the exact same form of the c++ RAII pattern with c#. [[Special:Contributions/64.234.67.2|64.234.67.2]] ([[User talk:64.234.67.2|talk]]) 05:53, 29 December 2008 (UTC)

:Furthermore, any class implementing the IDisposable interface, has an automatic, deterministic destruction when it leaves a ''using'' scope (IDisposable.Dispose is called). So using the C++ example in the article, the C# would look something like this:

<blockquote><source lang="csharp">
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!");
}
}
}
</source></blockquote>

:Why is this not RAII? [[Special:Contributions/24.243.3.27|24.243.3.27]] ([[User talk:24.243.3.27|talk]]) 02:48, 7 January 2009 (UTC)

Revision as of 02:48, 7 January 2009

WikiProject iconC/C++ Unassessed
WikiProject iconThis article is within the scope of WikiProject C/C++, a collaborative effort to improve the coverage of C and C++ topics on Wikipedia. If you would like to participate, please visit the project page, where you can join the discussion and see a list of open tasks.
???This article has not yet received a rating on Wikipedia's content assessment scale.
???This article has not yet received a rating on the importance scale.

RAII in Ruby/Smalltalk/Python

The Ruby/Python examples aren't RIAA. 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)[reply]

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)[reply]

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)[reply]

Not really true. Regardless of implementation details (e.g., a destructor wasn't automatically called that released the resource when the object went out of scope, the block manually released the resource, just before it went out of scope, or the caller released it just afterward), the usage fits the RAII pattern. I suppose something similar can be said for python's with clause, or Haskell's where clause, &c. It's still RAII, just implemented differently (because they are different languages).
Let's take a C++ example from wikibooks:C++_Programming/RAII:
 #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 & ) ;
 } ;
Now in ruby (using generic exception handling):
 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
All read / write operations are atomic to the block, and the resource is released when the block scope closes (i.e., when control is returned to the calling context). So, in essence, the resource allocated in the constructor, and passed to the block object (yes, it is an object of type Proc), is released when the block object goes out of scope. Rather than an explicit destructor, the constructor acquires the resource, passes control of the resource to the block, and destroys the resource when control is returned. That's the main idea behind RAII, as I understand it.
Ps. In ruby, you could implement an explicit destructor with Object#finalize, but it is not guaranteed to run--best practice dictates using a block form for RAII. You can also implement a destructor in python with the __del__ hook. 64.234.67.2 (talk) 05:50, 29 December 2008 (UTC)[reply]

RAII in Java

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.

I know Java doesn't allow it the way C++ does, that's why I was asking the question :-) Wouter Lievens 19:42, 20 September 2005 (UTC)[reply]

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)[reply]

Remove "stub"?

What about removing the stub template? I found this article most helpfull.

Yes, I think it deserves that removal (I'll do it right now) – Adrian | Talk 23:36, 17 November 2005 (UTC)[reply]

RIIA?

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)[reply]

Agreed, I've never heard it used in that order either. The instances that you can find on Google [1] are Wikipedia "mirrors" and most likely mistakes otherwise. I'll remove the acronym from the page as per WP:BB for now. -- intgr 11:21, 2 July 2006 (UTC)[reply]
That was my typo. It's been fixed. Sory about that. —Ben FrantzDale 02:25, 3 July 2006 (UTC)[reply]

Bad example code

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).

If CloseFile can throw an exception (or even fail at all), there are more serious problems to handle. :) Usually those functions wrapped on RAII classes aren't even exception-aware (like a C API), so you don't need a try-catch for them. I think that adding more lines for a try-catch in this simple example (thus making it even less clear) doesn't add as much value as adding a foot note about the dangers of exceptions escaping from destructors. Feel free to add this note in the article. --DanielKO 00:48, 4 June 2006 (UTC)[reply]
CloseFile can fail. In most cases WriteFill will not actually write data, just append to the buffer, and CloseFile will flush the buffer, and that can fail (not enough disk space, etc). It's very important that write a catch in the destructor when the resource release can throw exception:
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).[reply]

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)[reply]

Bad C example

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)[reply]

RAII != Abstraction

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)[reply]

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 (talkcontribs)

First of all, thanks for catching the fopen() mistake; of course you're right that it doesn't matter whether fopen() throws or not.
By class invariant I simply meant the most basic invariant of every class: "this is a valid object". Once you close the file, you have an invalid object: all subsequent operations on the object will fail. That situation is entirely avoidable.
Since I began my previous comment with, "Your example here doesn't make sense to me," it shouldn't surprise you that I "completely misunderstood" what you originally wrote. Thank you for clarifying. However I would suggest that you could be a more effective communicator if you polished up your etiquette.
As I understand it, you are saying that the non-RAII example should use a C++ class, because "RAII != abstraction". If we go back to the original OpenFile() and CloseFile() during the time of your original comment in this thread, the C code is as abstracted as the non-RAII C++ code you propose. You don't get more abstraction by using a non-RAII C++ class --- it's the same code but with a different syntax. Instead of CloseFile(file), it's file.close().
Since the current example illustrates the common and useful practice of wrapping a C API with C++, since we gain nothing (no abstraction) from a non-RAII class, and since we may possibly introduce confusion by showing a "bad example", it is my suggestion that we keep the C code. Xerxesnine 21:48, 12 May 2007 (UTC)[reply]
Ok, sorry for being so hotheaded in my previous message. But I still disagree with you saying that we gain no abstraction from a non-RAII class. The article's file class has a write method which hides 2-4 lines of low-level code. Similarly the constructor hides both opening the file and throwing in case of an error. No sane programmer would write this code every time she tried to open a file:
   std::FILE* file_handle = std::fopen("logfile.txt", "w+") ;
   if( file_handle == NULL )
   {
       throw open_error() ;
   }
At the very least, she'd write a function to either open or throw.
   std::FILE* file_handle = open_or_throw("logfile.txt", "w+") ;
I maintain my stand that failing to use these abstractions in the non-RAII code is a dishonest presentation of the benefits of RAII. And the current article puts even more code in the class when compared to a year ago so all the more reason for change. 80.221.24.224 07:29, 22 June 2007 (UTC)[reply]

Is it RAII if it's not automatic?

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)[reply]

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)[reply]

Perl

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)[reply]

This is not RAII. The file object will be closed and destroyed by the garbage collector, not explicitly when the object goes out of scope of the function. -- intgr 07:45, 22 November 2006 (UTC)[reply]
Exactly. The RAII C++ version looks like this:
   std::auto_ptr<std::ofstream> open_it() {
     return std::auto_ptr<std::ofstream>(new std::ofstream("it.dat"));
   }
Of course, if this article gave one person the wrong impression, could probably be improved. —Ben FrantzDale 01:41, 12 February 2007 (UTC)[reply]
You speak authoritatively while having no idea what you're talking about; Perl doesn't have a garbage collector, it has reference counts. The file is closed -- deterministically -- when the last reference to the IO::File is dropped. -- 98.108.208.207 (talk) 13:15, 15 July 2008 (UTC)[reply]

stlsoft::scoped_handle

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)[reply]

Agreed, one example is enough and the new one is rather nonobvious, providing little encyclopedic value. -- intgr 22:36, 31 March 2007 (UTC)[reply]

Failure inside the file destructor

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)[reply]

No code is better than misleading code. This:
if(std::fclose(m_file_handle) != 0) assert(!"file failed to close");
is definitely the worst example of using assertions that I've ever seen; assertions are for verifying certain conditions and catching developer errors during the debugging phase, and NEVER for catching/handling runtime errors, or simply emitting error messages.
Runtime errors are errors that may occur even when the code does not contain any bugs, e.g., hardware errors, user errors, whatnot. An I/O error is such a runtime error.
Typically, assertions are not even compiled into release builds of C/C++ programs, so the body of this if() condition would expand into a no-op and your "error check" is not actually checking anything in cases where it matters.
-- intgr 19:43, 15 April 2007 (UTC)[reply]
We all know what assertions are, so most of your comment is unnecessary. There is an error condition for fclose(). Not checking that error is more misleading than anything you could possibly imagine. We must check the error. Given the complexity of RAII release errors, I went with a simple assertion flag; as I said, it's a placeholder. Remember this is supposed to be a simple example.
Now, how do you propose that we check the error? If you don't like the assertion, then please suggest an alternative. Leaving it unchecked is not acceptable. Xerxesnine 20:08, 15 April 2007 (UTC)[reply]
Adding an assert there does not constitute "checking for the error". Repeating myself: assertions should never be used for checking runtime errors, since they are left out of release builds, where runtime errors matter the most. Assertions catch developer errors (bugs), not runtime errors.
Thus, the example was more misleading than useful — leaving the impression that the error was checked, while it really wasn't. In addition to that, it was a misuse of assertions: the example also included an awkward "!" hack to evaluate a string to false (WTF?!), and assert()ed to a constant inside an if() condition, instead of actually asserting a condition.
I do not have a good alternative, as I rarely write C++, and avoid overuse of patterns such as RAII. I do, however, use assertions. Even a cout << "error closing file\n"; exit(1); would be better in this case.
-- intgr 22:29, 15 April 2007 (UTC)[reply]
The assert(!"something bad") idiom is common. You'll find it in books by Herb Sutter and Andrei Alexandrescu (at least one of those). It's a good technique.
Since you rarely write C++, I advise you to look through all of this thread and spend some quality time googling on RAII before writing anything else on this page. Assertions are in fact a common method of dealing with release failure, even preferred by some. After all, when A B C is done, this forces the undo action to be C^(-1) B^(-1) A^(-1). If the latter fails, we must assume in general the state is undefined --- which is an assertion (yes, even in production code).
As I hoped to convey in my last comment, your preaching on assert() completely misses the point. You also missed the point that the assert is essentially "placeholder" for user-defined code. It is better to notify the reader that this condition exists than to keep silent (remember the audience). I'm willing to bet 100% of experienced programmers would agree.
Your suggestion to use std::cout for error reporting (actually it should be std::cerr) is similar to what assert(!"file failed to close") does, only the latter does it better. You seem to be fixated on the assumption that all assertions are removed from release builds, which is most certainly not the case. Indeed, in practice many are retained --- especially those such as file release checks which are unlikely to ever become a bottleneck. Xerxesnine 05:22, 16 April 2007 (UTC)[reply]
"Assertions are in fact a common method of dealing with release failure, even preferred by some"
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"? That is certainly one of the anti-patterns of using assert — assertion failures are only useful to the developer.
"the assumption that all assertions are removed from release builds"
Yes, because this is the default behavior — assertions using the standard assert() macro are stripped when the NDEBUG macro is defined, and most IDEs define that by default for release builds. Look it up in your favourite C++ language reference.
"that the assert is essentially "placeholder" for user-defined code"
Then why not just make that clear with a comment?
"especially those such as file release checks which are unlikely to ever become a bottleneck"
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.
"better to notify the reader that this condition exists than to keep silent"
Certainly, but misleading (tricking) the reader is not the right way to do it.
"spend some quality time googling on RAII before writing anything else on this page"
I am not planning to edit anything RAII-related on this article, so I'll pass. I am only complaining about your use of assertions.
-- intgr 10:28, 16 April 2007 (UTC)[reply]

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)[reply]

"Yes, you can make conditional use of asserts within the same build, with the standard NDEBUG macro if desired."
Technically, yes; however, redefining and re-including the assert header is exactly the kind of misleading behavior that should not be relied on in real code, let alone examples, given that it is the only header defined by C99/C++98 that changes its behaviour with multiple inclusions.
Nor does this address my concern at all, since you did not override NDEBUG in your example.
"Your confusion over assert(!"bad state") is yet another indication, among others."
Where did you get the impression that I was confused? I said that it was "an awkward "!" hack to evaluate a string to false". Are you saying that my analysis is incorrect on the purpose? I already made a point on how assertions should be used.
Anyway, back to relevant discussion.
"Assertions are useful for terminating when we detect an undefined state"
Correct. However, I/O errors are not an "undefined state"; as noted twice above, they are legitimate runtime errors. I would expect a program to handle them and inform the user, instead of crashing (which an assertion failure or "undefined state" effectively is).
The loss of a file handle is by far not the only error that fclose() can return: fclose() is also responsible for flushing the latest changes to the file, which may fail, for example, when the user has pulled out their USB stick too early, or when the HD is full — conditions that are bound to happen in practice, but rarely do during development.
While crashing means that the user has lost their work, a gracefully handled error would presumably allow the user to save their work on a different medium. I assumed that you would understand this, seeing you introducing a check for that error in the first place. Hence I started explaining why an assertion was inappropriate for handling this case. Given that you do understand assertions, I have to conclude now that you are simply negligent of handling such runtime errors.
"Your first suggestion was to ignore the error."
That was not my suggestion at all — I suggested not to include a piece of code that I considered misleading and a cookbook example of an assert anti-pattern. My previous post did propose a real suggestion — if the line was supposed to be a "placeholder", then a comment explaining what should be placed there would certainly be warranted, preferably replacing the assertion.
"you might be more productive if you reconsidered your role as "educator" here, engaging with others as peers instead."
I explained above why I considered it necessary to explain the purpose of assertions, and I contend that my argument for runtime errors still stands. I did not consider myself an "educator" to begin with; I apologise if I came off to you that way.
-- intgr 17:48, 16 April 2007 (UTC)[reply]
Yes, you can make conditional use of asserts within the same build, with the standard NDEBUG macro if desired.
Technically, yes; however, redefining and re-including the assert header is exactly the kind of misleading behavior that should not be relied on in real code, let alone examples, given that it is the only header defined by C99/C++98 that changes its behaviour with multiple inclusions.
Nor does this address my concern at all, since you did not override NDEBUG in your example.
There is no "redefining and re-including" other than the manner in which C++ has been designed. Namely, C++ supports the use of multiple translation units. In some translation units you define NDEBUG, in others you don't. This is how assertions have been used since the dawn of C. It is entirely in the hands of the developer where to keep assertions and where to remove them. This is entirely understood by all. Honestly, have you ever used C++ for serious development?
I have to conclude now that you are simply negligent of handling such runtime errors.
A surprising statement, given that you twice removed the check on fclose(), and I twice restored it. Again, my concern here is practical. What are we to do about this example code? On the other hand, your concerns are impractical, as you keep moving the goalpost. You keep taking my statements about the example code and construe them as applying to large-scale software development. You want to make dialogue boxes, handle the possible removal of USB sticks, full hard drives, and God knows what else. It's all out of proportion. The quoted sentence above is ridiculous, and you know it. You are quickly becoming a troll.
Your confusion over assert(!"bad state") is yet another indication, among others.
Where did you get the impression that I was confused? I said that it was "an awkward "!" hack to evaluate a string to false". Are you saying that my analysis is incorrect on the purpose? I already made a point on how assertions should be used.
I got that impression because of your various comments such as "awkward hack", "WTF?", "braindead usage", and "unintelligible". You appear to be confused or surprised by the use of assert(!"foo"), which is a common, useful, and intelligible idiom.
Your first suggestion was to ignore the error.
That was not my suggestion at all [...]
Yes, that was your suggestion, because twice you reverted the code back to ignoring the check.
My previous post did propose a real suggestion — if the line was supposed to be a "placeholder", then a comment explaining what should be placed there would certainly be warranted, preferably replacing the assertion.
This is the only useful thing you have said in this whole commotion. To me, it was clear that assert(!"file failed to close") was the placeholder. But to the general audience, an additional comment of "// placeholder -- handle failure" would probably be helpful. Good point.
Now, why didn't you just put that comment in yourself? Why did you keep removing the check on fclose()? This whole discussion was needless. It appears to revolve around the your ideological position on assertions, an unshakable belief which is impervious to my matter-of-fact statements about how assertions are actually used in real life. Oh well.
Xerxesnine 19:32, 16 April 2007 (UTC)[reply]
Well, this discussion certainly was needless. As for your comments, I have my reasons and justifications, but it's better just to end this. -- intgr 20:15, 16 April 2007 (UTC)[reply]

Summary

I wish to completely demolish all of these arguments so I don't have to deal with them again.

Well, if you insist on continuing the "needless" discussion, I'll bite.
  • It is unacceptable to ignore system errors.
    • I never disagreed with this one, although you keep on putting your words into my mouth on this one.
  • Assertions can be used to check runtime errors. The cases entail hardware faults which (for example) may be associated with spontaneously lost file handles. In these cases, an immediate abort is the safest action, otherwise the application can do real damage.
    • See the next response.
  • I/O errors are not, in themselves, an undefined state, but unusual I/O errors such as lost file handles can indicate an undefined state because they are symptomatic of hardware errors, which impose an undefined state.
    • I already thoroughly addressed this. Quoting myself: "The loss of a file handle is by far not the only error that fclose() can return: fclose() is also responsible for flushing the latest changes to the file, which may fail, for example, when the user has pulled out their USB stick too early, or when the HD is full — conditions that are bound to happen in practice, but rarely do during development. [...]" This is my primary concern why assertions inadequately address the situation; I assumed that you would be aware of this, and I have been insisting that fclose returns legitimate runtime errors all along. If you're going to respond at all, I would expect you to pay more attention to what I am saying.
  • Assertions can and should be left in certain parts of production/release code.
    • Yes; the standard assert() macro does not, however, communicate that when leaving NDEBUG in an uncertain state, like the example code does. So I will take it as just an afterthought/rationalization.
  • Assertions can and should be used conditionally within the same build. Parts of the code have assertions enabled, other parts do not. This is how C++ developers use assertions.
    • Yes, however, I disagree with the behavior of conditionally defining NDEBUG on a per source-file basis — obviously the same source file may contain inner loops as well as non-bottleneck code; I would use two assertion macros, explicitly making it clear in the source which assertions are compiled into release builds. However, given that this is irrelevant to the question and a matter of preference, I do not think we should debate this.
  • C++ is designed to support multiple translation units. In most cases, a translation unit is an invocation of the compiler with a source file as input. Each translation unit is free to define or undefine macros (such as NDEBUG) in addition to any other action as long as the ODR is not violated. This should not be construed as "redefining and re-including", nor is it "misleading behavior that should not be relied on in real code". On the contrary, this is how C++ is designed to work.
    • I assumed that you meant conditionally including different assertions in a single source file (which is a real use case per comment above). Naturally, you can have different definitions in different source files. However, this, yet again, is irrelevant; quoting myself: "Nor does this address my concern at all, since you did not override NDEBUG in your example."
  • assert(!"something bad") is a common and useful idiom in C and C++.
    • A matter of preference. I dislike using practices in examples that are discouraged in real code, but perhaps, with an explanatory comment. I would not expect a reader to be aware of it, given that this is the first time I came across it. I still find it a hackish work-around over the fact that assert() only emits a message when the argument evaluates to false.
  • We cannot anticipate how the reader will handle release errors, especially in this simple example. We obviously can't provide code for dialogue boxes, for instance. The current strategy is to provide a placeholder assertion to alert the reader of the release error condition.
    • And this is the exact reason we also shouldn't rely on whether the reader's NDEBUG macro is set — we cannot predict their environment or coding habits. An exit() call, given as my first example, would be straightforward and to the point, without relying on the user's environment. Nor would a plain comment, although that simply wouldn't "handle" the error. -- intgr 08:13, 17 April 2007 (UTC)[reply]

Xerxesnine 03:18, 17 April 2007 (UTC)[reply]

To summarize the points I have made:
  • It is unacceptable to ignore system errors. (ditto)
  • fclose() returns important and common runtime errors, even if hardware is not malfunctioning, since it is responsible for flushing dirty buffers.
  • Hence, an assertion is inappropriate for addressing fclose() — errors such as "out of space" should never produce a crash, or be considered an "undefined state".
  • I believe that example code should not contain patterns that are discouraged in real code, unless they are explicitly explained (those C++ books probably did explain the given assert pattern).
  • The standard assert() macro is inadequate for expressing release build assertions. Even if you did specify the state of NDEBUG, it could still be misleading to readers who are not aware of the meaning of NDEBUG.
-- intgr 08:13, 17 April 2007 (UTC)[reply]

Response to Intgr:

  • Since it is agreed that ignoring system errors is unacceptable, it is also unacceptable to twice revert the example code back to ignoring system errors.
  • fclose() errors are not exclusively due to hardware failure, but can indicate hardware failure.
  • fclose() does not return important and common runtime errors. It returns 0 on success, EOF on error. The corresponding errno is EBADF (bad file descriptor). That's all the information that fclose() provides.
  • fclose() does not give (nor could it possibly give) a way to distinguish between hardware failures and USB-drive-pulled-out errors. The best thing to do is assume the worst: hardware failure.
  • We obviously cannot know how the developer will handle filesystem errors which aren't "hard errors". That is why there is a comment which says, "handle filesystem errors here", the only constructive outcome of this discussion. The worst case scenario is a hardware failure, which is why the assert() is there.
  • assert() is exactly appropriate for a hardware failure, since a hardware failure requires immediate termination.
  • assert(!"bad state") is not "discouraged in real code". On the contrary, the best experts encourage it, for obvious reasons. It (normally) gives you the file, line number, and the user-defined message; it calls abort(); it produces a hardware breakpoint inside debuggers. Who could ask for anything more?
  • exit() should not be called in place of assert(). If we are talking about hardware failure, exit() is a bad idea because it calls the destructors of static objects; it does not halt execution as required.
  • The use of assert() entirely adequate; it used frequently, even in release builds. Any developer who is tasked with handling fclose() errors would, by definition, understand how assert() works.

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)[reply]

(digging up old comments) "comments such as "awkward hack", "WTF?", "braindead usage", and "unintelligible"."
For these comments, I apologise; I realise that I was being uncivil.
But speaking of fclose errors: In fact, the fclose() implementation in the standard C libraries of both Linux and Windows (these I tested, but very likely other popular OSes as well) returns EOF and sets errno appropriately in case of failures. The MSDN article on fclose is unhelpfully vague, but the Linux man page clearly states "The fclose() function may also fail and set errno for any of the errors specified for the routines close(2), write(2) or fflush(3)."[2], as does BSD's.[3]
"assert() is exactly appropriate for a hardware failure, since a hardware failure requires immediate termination."
I do not buy your reasons for assuming the worst case hardware failure.
If the problem is not fatal to the kernel (e.g., HD failure, some bus failures, connectivity outages and whatnot), then the kernel is fully capable of handling such cases for you, and passing a safe error code to the user space. If the problem is in a component that is fundamentally relied on, e.g., the CPU, RAM, chipset, etc, then it doesn't matter much at all what happens, since you cannot really guarantee anything at that point — and it is much more likely that your application or the OS crashes at a random pointer reference, or corrupts the data in some other way, than be caught in this assertion.
However, if you assume that it's a runtime error, then you might rescue the user's unsaved work.
"assert(!"bad state") is not "discouraged in real code"."
Okay, I do not like it, but I suppose it's just a matter of taste; I will give in.
"We obviously cannot know how the developer will handle filesystem errors which aren't "hard errors". [...]"
But we can guide him by pointing out that it's worth handling, rather than asserting that it is not supposed to happen. While having the comment is better than not, the assert still implies to me that it is not a practical error to bother with, and needless to say, I find that implication very misleading.
"twice revert the example code back to ignoring system errors."
This is another point that I've addressed beyond absurdity. I am not asking you to embrace or agree with my reasons, I am asking you to acknowledge them. Let me quote my previous responses:
  • "Thus, the example was more misleading than useful — leaving the impression that the error was checked, while it really wasn't"
  • "Certainly, but misleading (tricking) the reader is not the right way to do it."
  • "I suggested not to include a piece of code that I considered misleading and a cookbook example of an assert anti-pattern."
"As if this steady stream of misinformation coming from Intgr was not enough, I have counted three outright troll remarks from him."
If I'm a troll then why do you keep biting? If you want to withdraw, I won't mind, and I agree that having this argument is a waste of time. I even tried to end this argument by swallowing my pride and not responding to your comment, which I nevertheless disagreed with. But you insisted on pulling me back in, I will not let you kick me around without defending myself.
As for "misinformation", please point out where I have lied to you, or misinformed you? Sounds like a resort to ad hominem.
-- intgr 20:02, 17 April 2007 (UTC)[reply]

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)[reply]

Thanks! Let's forget about this and have a a nice cup of tea and a sit down. :) -- intgr 20:49, 18 April 2007 (UTC)[reply]

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)[reply]

A very misleading name

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)[reply]

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)[reply]

I can't agree with you. RAII is all about releasing resources, not acquiring them. Why Java has no RAII? It has constructors, it has exceptions; but it lacks destructors.
As for LIFO order, I consider such a constructor bad style because its behavior depends on the order in which member variables are defined in the class (see 12.6.2/5), someone could break it by merely regrouping members.
The default constructor of std::auto_ptr initializes it with NULL: explicit auto_ptr(X* p = 0) throw();
To repeat, RAII is automatic resource releasing upon leaving scope. Why “RAII is a key concept for writing exception-safe code” (taken from the article)? It is because destructors are guaranteed to run however control exits the scope.
Roman V. Odaisky 18:38, 29 July 2007 (UTC)[reply]
So, Wikipedia being not a forum, how can we best reflect this in the article? Roman V. Odaisky 18:46, 29 July 2007 (UTC)[reply]
Yes, "automatic resource releasing upon leaving scope" is part of RAII. But despite your multiple assertions otherwise, that is not all of it.
Repeating your own definition of RAII will not persuade others to adopt it over Stroustrup's definition. Stroustrup called it RAII for a reason. It is about acquiring resources, hence the name "resource acquisition is initialization". If you redefine "resource acquisition is initialization" as something which "deals neither with resource acquisition nor with initialization", then I can certainly see why you might think the name RAII is misleading.
FILO ordering is an essential part of RAII. Locks, mutexes, and most (all?) resources follow FILO. RAII ensures FILO order automatically; it does not rely upon the diligence of programmers to do it. You cite 12.6.2, which explains this important mechanism with regard to member data. The purpose of RAII is to take advantage of that mechanism. It is not bad style; the style is called RAII. Please see Stroustrup's example in his 3rd ed. book, "The C++ Programming Language". Really, please look at that example and its explanation before responding. It appears that you are simply missing the point.
Of course the default constructor of std::auto_ptr initializes internally to NULL. It is not clear why you believe that is relevant to mention here. Or were you being a stickler for the definition of "invalid object"?
            std::auto_ptr<int> a ;
            *a = 1 ; // crash
That is the behavior I wished to convey. It's not good behavior. Call it what you wish.
You are correct that Wikipedia is not a forum. It is especially not a forum for proselytizing a new definition which is contrary to the canonical definition used by the creator of the language.
Xerxesnine 00:39, 30 July 2007 (UTC)[reply]

Construction, Destruction, and RAII

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 (talkcontribs) 15:10, 8 August 2007.

Please justify your bold statements by responding to the points I made on the subject above. Xerxesnine 19:00, 8 August 2007 (UTC)[reply]
I don't know which of your claims you are referring to, but I don't think it's appropriate to ask you to be more specific, because I disagree with your criteria for conflict resolution. This is an encyclopedic article, not a home page. We shouldn't be deciding what RAII is, we should be documenting what it is. Though I'd imagine both of us are industry professionals, neither of us are authoritative sources.
Therefore, for the article, I claim that we should be referring to facts and citations (subtle difference), where a fact is encyclopedic in nature--that is, if something is claimed to be true by an authoritative source, it should automatically be treated as true (unless redacted). If two authoritative sources disagree--no problem. Citing the controversy is certainly an option.
If this doesn't sound fair to you, please let me know by which criteria we can resolve this dispute.
Following my own rules, here is my defense:
TC++PL3, 14.4.1:
 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.
This is the section most closely matching the example you cited (which is slightly different in TC++PL3--if it helps, I could quote the exact example). Nothing here is being defined, just referred to. Nothing here specifies when resources are acquired or deleted, only that RAII relies on the relationship between constructors, destructors, and exceptions. So, if this is what you were referring to, it's incorrect. Still, since you weren't specific, I leave it open that you may be referring to something else (you're more than welcome to cite what--Stroustrup has a lot to say about RAII throughout 14.4). For the record, various miscellaneous references suggest that the concept of RAII was introduced in "Design and Evolution of C++", which unfortunately I don't have (since I didn't cite which, feel free to treat this as an unverified claim, though I honestly don't see where it would be controversial, especially since you seem to recognize Stroustrup as an authority).
It's a fact that the auto_ptr supports RAII (per 14.4.2 TC++PL3). But auto_ptr's have no constructors that obtain the resources they manage (ISO IEC 14882-2003 20.4.5). Take a look at a typical example:
auto_ptr<int> x(new int(5));
Note that the resource, the heap int object, is allocated before x's constructor is called, not in the constructor.
It's a fact that tr1::shared_ptr is an RAII object (per Meyers, EC++3, Item 13 (p. 66)). It should be fairly obvious that a shared_ptr destructor doesn't necessarily relinquish the resource, but rather, it's just responsible for relinquishing it. In particular, shared_ptr destructors only relinquish the resource when the last shared_ptr is destructed. This implies that ownership is the key concept behind RAII, and indeed...
It's a fact that the key concept behind RAII is assigning ownership of a resource to a scoped object (Stroustrup, http://www.research.att.com/~bs/except.pdf (p. 3)):
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. ...
Note in particular the qualifier typically that Stroustrup uses. Furthermore, note that this is precisely what I said RAII was about earlier.
Is this adequate?
References are intentionally informal (I don't want to add ref tags on a discussion page). TC++PL3 is "The C++ Programming Language, 3d ed", EC++3 is "Effective C++, 3d ed", both are Addison-Wesley. 14882 should be obvious.
75.130.108.174 07:50, 17 August 2007 (UTC)[reply]
Rephrasing definition again... because my changes will probably be controversial, I'll explan. "Variables" changed to "scoped objects" because that's what Stroustrup says in the exception article (see above, and also cited in main body), and not all scoped objects are bound to variables (consider object temporaries). I don't feel the first statement was inaccurate though (except, again, the variables part).
Also, RAII is not merely allocating in the constructor and deallocating in the destructor (see same Stroustrup article again; reread above if you got opinions from TC++PL, which isn't wrong either). It's more comprehensive than this. Also see above--RAII is often used (I say "often" because smart pointers are common) to manage resources created before construction, as opposed to in the constructor.
Still, my wording is probably not perfect, and I'm not quite sure if we care for an encyclopedic reference to be this precise, but the old definition was wrong (where wrong I mean, "officially" wrong, not mere opinion--see above), if only technically, so at least this fix deserves peer review.75.130.108.174 07:45, 2 September 2007 (UTC)[reply]

PHP5

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)[reply]

GNU C

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)[reply]

"automatic, deterministic"

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)[reply]

C# has had destructors since 1.1

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)[reply]

Furthermore, any class implementing the IDisposable interface, has an automatic, deterministic destruction when it leaves a using scope (IDisposable.Dispose is called). So using the C++ example in the article, the C# would look something like this:
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!");
		}
	}
}
Why is this not RAII? 24.243.3.27 (talk) 02:48, 7 January 2009 (UTC)[reply]