Exception Safe Guard

B

Barry

After reading Andrei Alexandrescu and Petru Marginean's
"Generic: Change the Way You Write Exception-Safe Code ¡ª Forever"
http://www.ddj.com/cpp/184403758

I borrow Boost.Function, which makes the implementation much simpler.

here goes the demo:

#include <boost/function.hpp>
#include <boost/bind.hpp>
#include <boost/noncopyable.hpp>

#include <iostream>
#include <ctime>
#include <cstdlib>

using namespace boost;

using namespace std;

struct Guard
: private noncopyable
{
Guard(function0<void> const& op)
: op_(op), committed_(false) {}

void Commit() throw()
{
committed_ = true;
}

~Guard()
{
if (!committed_)
op_();
}

private:
bool committed_;
function0<void> op_;
};

struct Obj
{
void Rollback() const {
cout << "Committed, Now Rolling back" << endl;
}
};

void MayThrow() throw(int)
{
srand(time(0));
if (int r = rand() % 2)
throw r;
}

int main()
try
{
Obj obj;
Guard guard(bind(&Obj::Rollback, obj));

// do the stuffs, which may throw
MayThrow();

guard.Commit();
}
catch (int i)
{
cout << "Exception: i = " << i << endl;
}
catch (...)
{
cout << "Unknown Exception" << endl;
}

Any suggestion is welcomed, including coding style.
 
M

Michael DOUBEZ

Barry a ¨¦crit :
After reading Andrei Alexandrescu and Petru Marginean's
"Generic: Change the Way You Write Exception-Safe Code ¡ª Forever"
http://www.ddj.com/cpp/184403758

I borrow Boost.Function, which makes the implementation much simpler.
[snip: good to me]

void MayThrow() throw(int)
{
srand(time(0));

srand(time(NULL)) is fine provided you don't have more than one call per
second. Otherwise, all call will yield the same result.
Nothing to do with exception safety though.
if (int r = rand() % 2)
throw r;
}

int main()
try
{
Obj obj;
Guard guard(bind(&Obj::Rollback, obj));

// do the stuffs, which may throw
MayThrow();

guard.Commit();
}
catch (int i)
{
cout << "Exception: i = " << i << endl;
}
catch (...)
{
cout << "Unknown Exception" << endl;

Avoid the catch all in main() unless you can handle it. You will not
know which exception you don't know about has been thrown.

If you really want to, you can catch std::exception in order to catch
bad_alloc or other derived exception:
catch (exception& e)
{
cout << "Standard exception" << e.what() << endl;
}

A scheme that can be used to extend your system is based on the Memento
GoF pattern. It would consist in making functions performing changes
return a memento structure (a boost::function0 in your case) and append
it to a stack of changes performed. Upon destruction without commit, you
pop the mementos and apply them.

Michael
 
B

Barry

Michael said:
Barry a ¨¦crit :
After reading Andrei Alexandrescu and Petru Marginean's
"Generic: Change the Way You Write Exception-Safe Code ¡ª Forever"
http://www.ddj.com/cpp/184403758

I borrow Boost.Function, which makes the implementation much simpler.
[snip: good to me]

void MayThrow() throw(int)
{
srand(time(0));

srand(time(NULL)) is fine provided you don't have more than one call per

I thought in C++, always write 0 as null pointer.
here writing NULL is because /srand/ is C function?
second. Otherwise, all call will yield the same result.
Nothing to do with exception safety though.

/MayThrow/ here is just a `Mock object' to produce exception randomly.
Avoid the catch all in main() unless you can handle it. You will not
know which exception you don't know about has been thrown.

so I should've written

int main()
{
try
{
Obj obj;
Guard guard(bind(&Obj::Rollback, obj));

// do the stuffs, which may throw
MayThrow();

guard.Commit();
}
catch (int i)
{
cout << "Exception: i = " << i << endl;
}
}

as MayThrow throws /int/ only
right?
If you really want to, you can catch std::exception in order to catch
bad_alloc or other derived exception:
catch (exception& e)
{
cout << "Standard exception" << e.what() << endl;
}


A scheme that can be used to extend your system is based on the Memento
GoF pattern. It would consist in making functions performing changes
return a memento structure (a boost::function0 in your case) and append
it to a stack of changes performed. Upon destruction without commit, you
pop the mementos and apply them.

Well, this code is only a demo to the article that I referred to at the
beginning of my post. Anyway I will study the pattern you mention here.

Thanks a lot.
 
D

Default User

Barry said:
Michael said:
Barry a (&crit :
After reading Andrei Alexandrescu and Petru Marginean's
"Generic: Change the Way You Write Exception-Safe Code !* Forever"
http://www.ddj.com/cpp/184403758

I borrow Boost.Function, which makes the implementation much
simpler. [snip: good to me]

void MayThrow() throw(int)
{
srand(time(0));

srand(time(NULL)) is fine provided you don't have more than one
call per

I thought in C++, always write 0 as null pointer.
here writing NULL is because srand is C function?


Many people prefer using 0, but NULL is perfectly standard and safe.
Also, 0 works just fine in C.



Brian
 
M

Michael DOUBEZ

Barry a ¨¦crit :
Michael said:
Barry a ¨¦crit :
After reading Andrei Alexandrescu and Petru Marginean's
"Generic: Change the Way You Write Exception-Safe Code ¡ª Forever"
http://www.ddj.com/cpp/184403758

I borrow Boost.Function, which makes the implementation much simpler.
[snip: good to me]

void MayThrow() throw(int)
{
srand(time(0));

srand(time(NULL)) is fine provided you don't have more than one call per

I thought in C++, always write 0 as null pointer.
here writing NULL is because /srand/ is C function?

It is a personnal preference. I always use NULL when the intended
parameter is a pointer; seeing 0 makes me wonder whether a 0 integer was
intended.
/MayThrow/ here is just a `Mock object' to produce exception randomly.

Which means that is I want to play with your sample, I would have to
launch it multiple time unless I am lucky :).
And a bash script 'while true; do ./test_throw ; done' would in fact be
likely to throw only once per second.
so I should've written

int main()
{
try
{
Obj obj;
Guard guard(bind(&Obj::Rollback, obj));

// do the stuffs, which may throw
MayThrow();

guard.Commit();
}
catch (int i)
{
cout << "Exception: i = " << i << endl;
}
}

as MayThrow throws /int/ only
right?

Yes and if you want to demonstrate guard idiom, you should definitly not
use catch(...) otherwise the following code is enough:
Obj obj;
try
{
// do the stuffs, which may throw
MayThrow();
}
catch (int i)
{
//...
}
catch(..)
{
//...
}
obj.Rollback()

Here catch(...) allows a 'finaly like' system which is not the point.
Well, this code is only a demo to the article that I referred to at the
beginning of my post. Anyway I will study the pattern you mention here.

Forget it. I was thinking of incremental restoration of state and this
way of doing it is not exception safe.
The pure Memento GoF pattern is actually what you are doing: Object
creation saves state and rollback() restores it.

Michael
 
B

Barry

Michael said:
Yes and if you want to demonstrate guard idiom, you should definitly not
use catch(...) otherwise the following code is enough:
Obj obj;
try
{
// do the stuffs, which may throw
MayThrow();
}
catch (int i)
{
//...
}
catch(..)
{
//...
}
obj.Rollback()

Here catch(...) allows a 'finaly like' system which is not the point.

Michael,
you miss the most important part here.
Maybe I should've written the code in this may to make it clearer

void Transact() throw(int)
{
Obj obj;
Guard guard(bind(&Obj::Rollback, obj));

// do the stuffs, which may throw
MayThrow();

// coming this far, nothing thrown, we commit the transaction
guard.Commit();
cout << "Transaction successful" << endl;

// no matter exception thrown or not
// guard will be destructed, ~Guard() will be called

// 1. if exception throw then committed_ must be false
// the roll back functor will be called
// 2. else roll back functor won't be called, as
// committed_ is true
}

int main()
{
try
{
Transact();
}
catch (int i)
{
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,537
Members
45,022
Latest member
MaybelleMa

Latest Threads

Top