www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Why we need scope(success) if I can write at the end?

reply tamir <tamir.fri gmail.com> writes:
or what's the differents between theese two:
void transactionalCreate(string filename) {
  string tempFilename = filename - ".fragment";
  scope(success) {
    std.file.rename(tempFilename, filename);
  }
  auto f = File(tempFilename, "w");
}
and:
void transactionalCreate(string filename) {
  string tempFilename = filename - ".fragment";
  auto f = File(tempFilename, "w");
  std.file.rename(tempFilename, filename);
}
Jan 21 2011
next sibling parent spir <denis.spir gmail.com> writes:
On 01/21/2011 02:18 PM, tamir wrote:
 or what's the differents between theese two:
 void transactionalCreate(string filename) {
    string tempFilename = filename - ".fragment";
    scope(success) {
      std.file.rename(tempFilename, filename);
    }
    auto f = File(tempFilename, "w");
 }
 and:
 void transactionalCreate(string filename) {
    string tempFilename = filename - ".fragment";
    auto f = File(tempFilename, "w");
    std.file.rename(tempFilename, filename);
 }

I'm a newcomer as well, so take my answer with a good dose of doubt. There is no difference AFAIK. scope(success) allows defining end-of-scope process for a given element --here the file-- in case of success using an idiom consistent with other possible outcomes (read: exit, failure). I find this a Good Thing, personly. Denis _________________ vita es estrany spir.wikidot.com
Jan 21 2011
prev sibling next sibling parent "Steven Schveighoffer" <schveiguy yahoo.com> writes:
On Fri, 21 Jan 2011 08:18:15 -0500, tamir <tamir.fri gmail.com> wrote:

 or what's the differents between theese two:
 void transactionalCreate(string filename) {
   string tempFilename = filename - ".fragment";
   scope(success) {
     std.file.rename(tempFilename, filename);
   }
   auto f = File(tempFilename, "w");
 }
 and:
 void transactionalCreate(string filename) {
   string tempFilename = filename - ".fragment";
   auto f = File(tempFilename, "w");
   std.file.rename(tempFilename, filename);
 }

Look at any linux kernel driver code, and you will see goto used in a very structured way: if(open_port() == -1) goto FAILURE; if(create_device() == -1) goto FAILURE2; if(init_device() == -1) goto FAILURE3; return SUCCESS; FAILURE3: destroy_device(); FAILURE2: close_port(); FAILURE: return ERROR; Basically, this idiom of building a stack of resources that must be destroyed in reverse order on failure is very common in a lot of code. scope(failure) or scope(exit) takes care of these tasks without using gotos, and by reducing the structure required to implement such things: open_port(); // throws exception on failure scope(failure) close_port(); create_device(); scope(failure) destroy_device(); init_device(); Now, scope(success) I believe is less common to require, but it still has the benefit of allowing you to group your dealings with a certain item in one spot. Imagine your code looks more like this: void transactionalCreate(string filename) { string tempFilename = filename ~ ".fragment"; auto f = File(tempFilename, "w"); ... // lots of code ... std.file.rename(tempFilename, filename); } It might be easier to follow code that says "I'm going to create a temp file, and then at the end, if everything works, I'm going to copy it over the original" in one paragraph, rather than have to read the whole function to see what happens to the temp file. It's also easier to write, you just write the scope(success) and never have to worry about special circumstances. You don't have to repeat yourself if you have multiple return statements. You can also return something from a function that may throw an exception without having this silly structure: auto x = fnImGoingToReturnValueOf(); std.file.rename(tempFilename, filename); return x; You can just do return fnImGoingToReturnValueOf(); and scope(success) will ensure your renaming is done properly only if that function doesn't throw. -Steve
Jan 21 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, January 21, 2011 05:18:15 tamir wrote:
 or what's the differents between theese two:
 void transactionalCreate(string filename) {
   string tempFilename = filename - ".fragment";
   scope(success) {
     std.file.rename(tempFilename, filename);
   }
   auto f = File(tempFilename, "w");
 }
 and:
 void transactionalCreate(string filename) {
   string tempFilename = filename - ".fragment";
   auto f = File(tempFilename, "w");
   std.file.rename(tempFilename, filename);
 }

scope(success) will be run _regardless_ of how you leave that scope (except for if an exepction is thrown). So, you can have one line with scope(success) and multiple return statements or breaks or continues or whatnot, and that scope(success) statement will be run in all cases. If you didn't use scope(success), then you'd have to duplicate that code at every point that the scope could be exited successfully. Now, if there's only one way to exit a particular scope successfully, then scope(success) is less useful. However, even in cases like that it can improve code maintanence and readability when it comes to stuff like resources. For instance, if you were to open a file at the beginning of a function, having a scope(success) statement immediately following it makes it clear that closing the function has been taken care of, whereas without the scope statement, you'd have to worry about putting it at the end of the function separate from where the file was opened. Having them together makes the code clearer. Now, a file isn't the best example, since you'd want success(exit) in that case, and the File struct std.stdio deals with it anyway through its destructor, but the example should give you the basic idea. - Jonathan M Davis
Jan 21 2011
prev sibling next sibling parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
When there are multiple calls that can fail, and where I have to do
clean-up code in a certain order and under certain conditions I use
scope(exit). For example:

import std.stdio;
import std.exception;

void main()
{
    foo();
}

enum NoError = true;

bool Initialize() { return true; }
bool Start() { return false; }

void Terminate() { writeln("Terminated."); }
void Stop() { writeln("Stopped."); }

void foo()
{
    auto error = Initialize();
    enforce(error == NoError, new Exception("Couldn't initialize"));
    scope(exit)
        Terminate(); // call Terminate only if Initialize was successful

    error = Start();
    enforce(error == NoError, new Exception("Couldn't start"));
    scope(exit)
        Stop(); // call Stop only if Start was successful
}

If I change the first scope to scope(success), Terminate will not be
called if the call to Start() failed. This can be bad in cases where
Terminate would release some hardware resource (it can lock up an app,
for example). scope statements won't be ran if an exception is throwed
before that scope statement is reached. So, that's one thing to keep
in mind.
Jan 21 2011
prev sibling next sibling parent Jonathan M Davis <jmdavisProg gmx.com> writes:
On Friday, January 21, 2011 14:49:44 Andrej Mitrovic wrote:
 When there are multiple calls that can fail, and where I have to do
 clean-up code in a certain order and under certain conditions I use
 scope(exit). For example:
 
 import std.stdio;
 import std.exception;
 
 void main()
 {
     foo();
 }
 
 enum NoError = true;
 
 bool Initialize() { return true; }
 bool Start() { return false; }
 
 void Terminate() { writeln("Terminated."); }
 void Stop() { writeln("Stopped."); }
 
 void foo()
 {
     auto error = Initialize();
     enforce(error == NoError, new Exception("Couldn't initialize"));
     scope(exit)
         Terminate(); // call Terminate only if Initialize was successful
 
     error = Start();
     enforce(error == NoError, new Exception("Couldn't start"));
     scope(exit)
         Stop(); // call Stop only if Start was successful
 }
 
 If I change the first scope to scope(success), Terminate will not be
 called if the call to Start() failed. This can be bad in cases where
 Terminate would release some hardware resource (it can lock up an app,
 for example). scope statements won't be ran if an exception is throwed
 before that scope statement is reached. So, that's one thing to keep
 in mind.

That's why you choose the appropriate version of scope(). And while scope(success) is still definitely useful, it's far less useful than scope(failure) or scope(exit). - Jonathan M Davis
Jan 21 2011
prev sibling parent spir <denis.spir gmail.com> writes:
On 01/21/2011 09:56 PM, Jonathan M Davis wrote:
 On Friday, January 21, 2011 05:18:15 tamir wrote:
 or what's the differents between theese two:
 void transactionalCreate(string filename) {
    string tempFilename = filename - ".fragment";
    scope(success) {
      std.file.rename(tempFilename, filename);
    }
    auto f = File(tempFilename, "w");
 }
 and:
 void transactionalCreate(string filename) {
    string tempFilename = filename - ".fragment";
    auto f = File(tempFilename, "w");
    std.file.rename(tempFilename, filename);
 }

scope(success) will be run _regardless_ of how you leave that scope (except for if an exepction is thrown). So, you can have one line with scope(success) and multiple return statements or breaks or continues or whatnot, and that scope(success) statement will be run in all cases. If you didn't use scope(success), then you'd have to duplicate that code at every point that the scope could be exited successfully. Now, if there's only one way to exit a particular scope successfully, then scope(success) is less useful. However, even in cases like that it can improve code maintanence and readability when it comes to stuff like resources. For instance, if you were to open a file at the beginning of a function, having a scope(success) statement immediately following it makes it clear that closing the function has been taken care of, whereas without the scope statement, you'd have to worry about putting it at the end of the function separate from where the file was opened. Having them together makes the code clearer. Now, a file isn't the best example, since you'd want success(exit) in that case, and the File struct std.stdio deals with it anyway through its destructor, but the example should give you the basic idea. - Jonathan M Davis

I just realised scope(success) can avoid 2 kinds of bugs, one common, one naughty: * at first coding time, simply forgetting releasing resources (one thinks at that when accessing it, but then, it's easy to forget), or any other exit task * at maintenance time, opening a new exit point for a function (new return), and forgetting the exit task there Denis _________________ vita es estrany spir.wikidot.com
Jan 21 2011