views:

1311

answers:

6

Since there is no finally in C++ you have to use the RAII design pattern instead, if you want your code to be exception safe. One way to do this is by using the destructor of a local class like this:

void foo() {
    struct Finally {
        ~Finally() { /* cleanup code */ }
    } finalizer();
    // ...code that might throw an exception...
}

This is a big advantage over the straight forward solution, because you don't have to write the cleanup code 2 times:

try {
    // ...code that might throw an exception...
    // cleanup code (no exception)
} catch (...) {
    // cleanup code (exception)
    throw;
}

A big disadvantage of the local class solution is that you can't directly access local variables in your cleanup code. So it will bloat your code a lot if you need access to them regardless:

void foo() {
    Task* task;
    while (task = nextTask()) {
        task->status = running;
        struct Finally {
            Task* task;
            Finally(Task* task) : task(task) {}
            ~Finally() { task->status = idle; }
        } finalizer(task);
        // ...code that might throw an exception...
    }
}

So my question is: Is there a solution which combines both advantages? So that you a) don't have to write duplicate code and b) can access local variables in the cleanup code, like task in the last example, but without such code bloat.

+8  A: 

I don't think there is a cleaner way to achieve what you're trying to do, but I think the main problem with the 'finally approach' in your example is an improper separation of concerns.

E.g. the function foo() is responsible for the Task object's consistency, this is rarely a good idea, the methods of Task themselves should be responsible for setting the status to something sensible.

I do realise sometimes there is a real need for finally, and your code is obviously just a simple example to show a point, but those cases are rare. And a bit more contrived code in rare cases is acceptable to me.

What I'm trying to say is, you should rarely have a need for finally constructs, and for the few cases where you do, I'd say don't waste time on constructing some nicer way. It will only encourage you to use finally more than you really should...

Pieter
+10  A: 

Instead of defining struct Finally, you could extract your cleanup code in a function of the class Task and use Loki's ScopeGuard.

ScopeGuard guard = MakeGuard(&Task::cleanup, task);

See also this DrDobb's article and this other article for more about ScopeGuards.

Sébastien RoccaSerra
+1  A: 

As others have said, the "solution" is better separation of concerns. In your case, why can't the task variable take care of cleaning up after itself? If any cleanup needs to be done on it, then it shouldn't be a pointer, but a RAII object.

void foo() {
//    Task* task;
ScopedTask task; // Some type which internally stores a Task*, but also contains a destructor for RAII cleanup
    while (task = nextTask()) {
        task->status = running;
        // ...code that might throw an exception...
    }
}

Smart pointers may be what you need in this case (boost::shared_ptr will delete the pointer by default, but you can specify custom deleter functions instead, which can perform arbitrary cleanup takss instead. For RAII on pointers, that's usually what you'll want.

The problem isn't the lack of a finally keyword, it's that you use raw pointers, which can't implement RAII.

But usually, every type should know how to clean up after itself. Not after every object that were in scope when the exception was thrown (which is what finally does, and what you were trying to do), just after itself. And if every object does that, then you don't need the big catch-all "clean up after every object in scope" function at all.

jalf
deleted my comment, as my socket example was meh :) i think i agree there are few cases where finally can be used. but it would be nice anyway to have/simulate it :)
Johannes Schaub - litb
+4  A: 

That is such an ugly way of doing it: (are you coming from Java?)

Please Read this article:
http://stackoverflow.com/questions/161177/does-c-support-finally-blocks-and-whats-this-raii-i-keep-hearing-about

It explains why finally is such an ugly concept and why RIAA is much more elegant.

Martin York
+1  A: 

I came to C++ from Delphi so I know what I'm talking about. I HATE finally!!! It's ugly. I really don't think C++ is missing finally.

Sergey Skoblikov
+2  A: 

I normally use something more like this:

class Runner {
private:
  Task & task;
  State oldstate;
public:
  Runner (Task &t, State newstate) : task(t), oldstate(t.status); 
  {
    task.status = newstate;
  };

  ~Runner() 
  {
    task.status = oldstate;
  };
};

void foo() 
{
  Task* task;
  while (task = nextTask())
  {
    Runner r(*task, running);
            // ...code that might throw an exception...
  }
}
Roddy