D - C++ destructor [was Properties]
- Patrick Down (127/133) Jan 15 2002 Yes, but this is really using a side effect of a feature of C++ to solve...
- Walter (5/9) Jan 15 2002 they
- Juan Carlos Arevalo Baeza (55/87) Jan 16 2002 In this one case (a very thin wrapper), I'd rather do:
- Walter (12/106) Jan 16 2002 I know this is beside the point of resource deallocation via destructor,...
- Pavel Minayev (9/12) Jan 17 2002 but
"Martin York" <Martin.York veritas.com> wrote in message news:a1vpav$1rgj$1 digitaldaemon.com...Yes, but this is really using a side effect of a feature of C++ to solve a larger problem. How do you encode a partial algorithm where some of the steps are predefined but some of the steps are not. The most common place you see this type of thing is when some object has to be setup, used, and shut down correctly. File handling is an example. You must: A. Open the file B. Read/Write/Process the data C. Close the file Step A is well defined. Step C must always be done. But step B always depends on the particular application. A C++ programmer might attack the problem like this: class FileReader { FILE* fin; FileReader(const char* szName) { fin = fopen(szName,"r"); if(!fin) throw FileError(); } ~FileReader() { if(fin) fclose(fin); } Read(...) Write(...) }; This is over simplified example. The point is this if the object is used as an automatic stack based object the destructor semantics takes care of closing the file. However this is not the only way or even the best way to attack this type problem. Other languages have more elegant ways of handling this. Take Ruby for example. Here is the regular way to write to a file. Despite being written in Ruby we should all recognize the steps. aFile = File.new("test","w") aFile.puts "Hello" aFile.close But we can also write the same program in Ruby like this: File.open("test","w") do |aFile| aFile.puts "Hello" end The function open above is analogous to a static function call on the File class. It opens the file in write mode. What is not obvious from above is that the code block that follows the open is a hidden parameter to the open function. It's like the code block that follows the open was turned into a function and a reference to that function was passed as a parameter to the open function. The open function above is written in Ruby like this. def open(filename,mode) aFile = File.new(filename.mode) yield aFile aFile.close end The yield statement gives control to the code block that was passed as a hidden parameter to the open function. The aFile object is passed as a parameter to the code block. The Ruby open function neatly hides having to close the file. Accessing the elements in a container can be done similarly in Ruby. Compare it to what you would need to do in STL. anArray.each { |i| print i, } Enough tooting Ruby's horn. Can you accomplish the same types of things in C/C++? Well yes and there's actually an example of this from C's stdlib. qsort. void qsort( void *base, size_t num, size_t width, int (__cdecl *compare )(const void *elem1, const void *elem2 ) ); int MyCompare(void* elem1,void* elem2) { return *(int *)elem1 < *(int *)elem2; } //... somewhere else int a[10]; qsort(a,10,sizeof(a[0]),MyCompare); The complexity of the quick sort is hidden in the qsort function. All you supply the comparison function. But the comparison function is in a different part of the code from the place where the sort is done. You can tell that the array is being quick sorted but you can't tell by what criteria unless you go look for MyCompare. When I code something like this the pseudo code in my head has two key concepts: 1) Sort type ( Not the implementation details. ) 2) Sort criteria These two concepts are linked in my head but I am forced to separate them when implementing as above in C. The file examples above could be done similarly. ReadFile(char* name, void (*Proc)(FILE*) ) { FILE* fin = fopen(name,"r"); if(fin) { Proc(fin); fclose(fin); } } void MyFileProcessing(FILE* fin) { // Do stuff with the file } // Somewhere else ... ReadFile("somefile",MyFileProcessing); But this suffers from the same problems as the qsort example above. There's also the STL functors. int a[10]; bubble_sort(a,10, less<int>()); Cool! That's pretty readable. Uh huh, now try to make a composite functor that sorts and array of object pointers by comparing the results of calling a member function of the object with a parameter. Even with STL you often end up having to fall back to creating a separate function to accomplish what you want. Wouldn't a syntax like this be more readable? SomeClass* a[10]; bubble_sort(a,10) { |SomeClass* x,SomeClass* y| return x->mf(1) < y->mf(1); } So what is the point of all my rambling? 1) Exploiting the semantics of the destructor to automatically execute code at the end of a function block is not possible in D like it was in C++. 2) I think that using the destructor this way was a little quirky to begin with so I don't consider it a big loss. 3) Some sort of syntax for defining implicit functions, in place where they are used in the code, would be useful. This could be used in situations that the destructor was using in above as well as a bunch of other situations.What big advantages are in creating objects on stack, anyhow?IMHO: Because you know exactly when destructors are being called, resource management (memory but mainly other) is excellent when using stack based objects.
Jan 15 2002
"Patrick Down" <pdown austin.rr.com> wrote in message news:a22q7i$p3c$1 digitaldaemon.com...3) Some sort of syntax for defining implicit functions, in place wheretheyare used in the code, would be useful. This could be used in situations that the destructor was using in above as well as a bunch of other situations.There have been many requests for this. It's a good idea, and would be worthwhile to add to D.
Jan 15 2002
"Patrick Down" <pdown austin.rr.com> wrote in message news:a22q7i$p3c$1 digitaldaemon.com...You must: A. Open the file B. Read/Write/Process the data C. Close the file A C++ programmer might attack the problem like this: class FileReader { FILE* fin; FileReader(const char* szName) { fin = fopen(szName,"r"); if(!fin) throw FileError(); } ~FileReader() { if(fin) fclose(fin); } Read(...) Write(...) };In this one case (a very thin wrapper), I'd rather do: class File { FILE* fin; File(const File&); // Disallowed File& operator=(const File&); //Disallowed public: File(const char* szName, const char* szMode) { fin = fopen(szName,szMode); if(!fin) throw FileError(); } ~File() { if(fin) // This "if" is actually not needed. fclose(fin); } operator FILE*() const { return fin; } }; This would be used as such: { File aFile("test"); fprintf(aFile, "Hello"); }The open function above is written in Ruby like this. def open(filename,mode) aFile = File.new(filename.mode) yield aFile aFile.close end But we can also write the same program in Ruby like this: File.open("test","w") do |aFile| aFile.puts "Hello" endThe only two clear advantages of the version in Ruby are in the definition part, not in the usage: - The Ruby way would allow you to do more things than just "wrap" the block. You could call it several times, for example. - The Ruby way has a much more concise way to implement the definition. This goes together with what I said in an earlier post. C++ is a toolbag (not even a toolbox, where things can be at least well organized). I'm using the "class" tool to implement a "smarter" file handle with better syntax. In a way, the same thing is true for Ruby, only the tool is a very different one. The main advantage of the C++ way of doing it is that it can do other things. For example: class BigObject { File cacheFile; ... }; In this case, a "big object" uses a file on the disk to cache data. It keeps that file open as long as it is alive, and closes it automatically. The Ruby way using blocks and "yield" cannot do this kind of thing. My point being that classes with destructors are one of the tools in C++, and they are, indeed, useful, and not having them in a language does limit the possibilities of the programmer. IMHO. Maybe the ideal language would be multi-level, where different capabilities are hidden or expressed in different ways to different kinds of users: the high-level programmer, the library programmer, the systems programmer, etc... Like having different but highly inter-related languages. Salutaciones, JCAB
Jan 16 2002
I know this is beside the point of resource deallocation via destructor, but in D reading a file is as simple as: buffer = file.read("mydatafile.txt"); "Juan Carlos Arevalo Baeza" <jcab roningames.com> wrote in message news:a24nvs$2229$1 digitaldaemon.com..."Patrick Down" <pdown austin.rr.com> wrote in message news:a22q7i$p3c$1 digitaldaemon.com...block.You must: A. Open the file B. Read/Write/Process the data C. Close the file A C++ programmer might attack the problem like this: class FileReader { FILE* fin; FileReader(const char* szName) { fin = fopen(szName,"r"); if(!fin) throw FileError(); } ~FileReader() { if(fin) fclose(fin); } Read(...) Write(...) };In this one case (a very thin wrapper), I'd rather do: class File { FILE* fin; File(const File&); // Disallowed File& operator=(const File&); file://Disallowed public: File(const char* szName, const char* szMode) { fin = fopen(szName,szMode); if(!fin) throw FileError(); } ~File() { if(fin) // This "if" is actually not needed. fclose(fin); } operator FILE*() const { return fin; } }; This would be used as such: { File aFile("test"); fprintf(aFile, "Hello"); }The open function above is written in Ruby like this. def open(filename,mode) aFile = File.new(filename.mode) yield aFile aFile.close end But we can also write the same program in Ruby like this: File.open("test","w") do |aFile| aFile.puts "Hello" endThe only two clear advantages of the version in Ruby are in the definition part, not in the usage: - The Ruby way would allow you to do more things than just "wrap" theYou could call it several times, for example. - The Ruby way has a much more concise way to implement the definition. This goes together with what I said in an earlier post. C++ is atoolbag(not even a toolbox, where things can be at least well organized). I'musingthe "class" tool to implement a "smarter" file handle with better syntax.Ina way, the same thing is true for Ruby, only the tool is a very different one. The main advantage of the C++ way of doing it is that it can do other things. For example: class BigObject { File cacheFile; ... }; In this case, a "big object" uses a file on the disk to cache data. It keeps that file open as long as it is alive, and closes it automatically. The Ruby way using blocks and "yield" cannot do this kind of thing. My point being that classes with destructors are one of the tools inC++,and they are, indeed, useful, and not having them in a language does limit the possibilities of the programmer. IMHO. Maybe the ideal language would be multi-level, where different capabilities are hidden or expressed in different ways to different kindsofusers: the high-level programmer, the library programmer, the systems programmer, etc... Like having different but highly inter-relatedlanguages.Salutaciones, JCAB
Jan 16 2002
"Walter" <walter digitalmars.com> wrote in message news:a24v01$26ji$1 digitaldaemon.com...I know this is beside the point of resource deallocation via destructor,butin D reading a file is as simple as: buffer = file.read("mydatafile.txt");Or, if you like streams (like I do =)), you can use my version: File file = new File("mydatafile.txt") buffer = new ubyte[file.size()]; file.read(buffer, file.size()); file.close(); By the way, it uses destructors to close opened files automatically...
Jan 17 2002