Exception safety in member function wrapper

B

benben

I have always found Stroustrup's paper on generalized member function
wrapper (http://www.research.att.com/~bs/wrapper.pdf) an interesting
one. Recently I started to play with it. As I tried to put some extra
data members (such as a std::vector) to the Call_proxy<> class template
it started to worry me because if the suffix in the destructor throws,
there can be a memory leak. (See relevant code below)

The Call_proxy<> uses its own destructor to facilitate a call to a
suffix at the end of a statement (as an instance of Call_proxy<> is
always a temporary.) This isn't a problem in the proposal in the paper
because the original version of the class template has no data member
that requires non-trivial destruction.

I am wondering if anyone has an idea as to how to improve the exception
safety. The requirements are:

1. If suffix() call throws, data members in Call_proxy<> are properly
destroyed.

2. The exception thrown is to be catched by the caller (i.e. the
destructor must be allowed to throw.

RELEVANT CODE

/***********************************************
Quoted from

http://www.research.att.com/~bs/wrapper.pdf

pp5.

Changes applied and commented
************************************************/

template <class T> class Wrap;

template <class T>
class Call_proxy{
T* p;
mutable bool own;

Call_proxy(T* pp):p(pp), own(true){}
Call_proxy(const Call_proxy& a): p(a.p), own(true){a.own = false;}
Call_proxy& operator=(const Call_proxy&);

std::vector<int> extra; // extra data member (added by me)

public:
template <class U> friend class Wrap;

~Call_proxy()
{
if(own) suffix(); // if this throws, extra will leak
}

T* operator->()const{return p;}
};

END OF RELEVANT CODE

My current work around is to do a vector swap trick

~Call_proxy()
{
std::vector<int> temp;
temp.swap(extra);

if (own) suffix(); // if throws temp will
// be destructed
// properly
}

But this is cumbersome and hardly general.

Regards,
Ben
 
A

Alf P. Steinbach

* benben:
template <class T> class Wrap;

template <class T>
class Call_proxy{
T* p;
mutable bool own;

Call_proxy(T* pp):p(pp), own(true){}
Call_proxy(const Call_proxy& a): p(a.p), own(true){a.own = false;}
Call_proxy& operator=(const Call_proxy&);

std::vector<int> extra; // extra data member (added by me)

public:
template <class U> friend class Wrap;

~Call_proxy()
{
if(own) suffix(); // if this throws, extra will leak
}

U-huh. The following program,

#include <iostream>
#include <ostream>
#include <stdexcept>

void say( char const s[] ) { std::cout << s << std::endl; }

struct Tracer
{
Tracer() { say( "Constructed." ); }
~Tracer() { say( "Destroyed." ); }
};

struct Thrower
{
Tracer t;
~Thrower() { throw std::runtime_error( "Ballaha!" ); }
};

int main()
{
try
{
Thrower o;
}
catch( std::exception const& x )
{
std::cerr << "!" << x.what() << std::endl;
}
}

should yield

Constructed.
Destroyed.
!Ballaha!

What's the result with your compiler?
 
B

benben

U-huh. The following program,
#include <iostream>
#include <ostream>
#include <stdexcept>

void say( char const s[] ) { std::cout << s << std::endl; }

struct Tracer
{
Tracer() { say( "Constructed." ); }
~Tracer() { say( "Destroyed." ); }
};

struct Thrower
{
Tracer t;
~Thrower() { throw std::runtime_error( "Ballaha!" ); }
};

int main()
{
try
{
Thrower o;
}
catch( std::exception const& x )
{
std::cerr << "!" << x.what() << std::endl;
}
}

should yield

Constructed.
Destroyed.
!Ballaha!

What's the result with your compiler?

Same! I must say I am amazed! Apparently my worry was unnecessary and I
should have gone into researching more before posting.

I have always assumed that destruction of the members happens at the end
of the destructor, so if the destructor throws, it would never make its
way to destroying the members.

Apparently C++ is much better designed than I thought it was.

Many thanks to Alf for putting me out of misery!

Ben
 
F

Frank Puck

benben said:
I have always found Stroustrup's paper on generalized member function
wrapper (http://www.research.att.com/~bs/wrapper.pdf) an interesting one.
Recently I started to play with it. As I tried to put some extra data
members (such as a std::vector) to the Call_proxy<> class template it
started to worry me because if the suffix in the destructor throws, there
can be a memory leak. (See relevant code below)

The Call_proxy<> uses its own destructor to facilitate a call to a suffix
at the end of a statement (as an instance of Call_proxy<> is always a
temporary.) This isn't a problem in the proposal in the paper because the
original version of the class template has no data member that requires
non-trivial destruction.

I am wondering if anyone has an idea as to how to improve the exception
safety. The requirements are:

1. If suffix() call throws, data members in Call_proxy<> are properly
destroyed.

2. The exception thrown is to be catched by the caller (i.e. the
destructor must be allowed to throw.


I never saw the need to abort the destruction of an object.
What would be the result?
Leaving a scope means destroying local objects.
Throwing an exception means leaving a scope.
Thus destructors should never throw.
 

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

Forum statistics

Threads
473,981
Messages
2,570,187
Members
46,730
Latest member
AudryNolan

Latest Threads

Top