C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Template-based implementation of Sutter's exception-safe ope

 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated)
View previous topic :: View next topic  
Author Message
Mikhail N. Kupchik
Guest





PostPosted: Thu Aug 19, 2004 12:01 pm    Post subject: Template-based implementation of Sutter's exception-safe ope Reply with quote



Hi All.

I have a question regarding Herb Sutter's idiom of implementation of
operator= via nonthrowing swap() member function, to guarantee strict
exception safety.

The idea of the idiom is shown by the example:

-- code fragment 1 ----------------------------------------------------
class MyClass
{
...
public:
void swap( MyClass& ) throw();

MyClass& operator=( const MyClass& rvalue )
{
MyClass other( rvalue ); // (1)
swap( other );
return *this;
}
};
-- end of code fragment 1 ---------------------------------------------

Thus we need to implement only copy constructor (used by (1)) and get
strict exception safety: every possible exception in operator= is
produced by (1) and gets thrown out before `this' object is actually
modified.

My question is the following: is it possible to implement the idiom of
exception-safe operator= via template class (with little help of CRTP
of course), so that I could write for many of my classes:

-- code fragment 2 ----------------------------------------------------
class MyClass
: public CanonicalReplacement< MyClass >
{
...
public:
void swap( MyClass& ) throw();
};
-- end of code fragment 2 ---------------------------------------------

My first attempt was

-- code fragment 3 ----------------------------------------------------
template< typename T >
struct CanonicalReplacement
{
T& operator=( const T& other )
{
if( this != &other ) // (2)
{
T temp_object( other ); // (3)
T::swap( temp_object ); // (4)
}
return *this;
}
};
-- end of code fragment 3 ---------------------------------------------

Besides of one slippery thing that in (2) `this' is implicitly upcasted
to CanonicalReplacement<T>* and actually pointers to CanonicalReplacement<T>
rather than T are compared (I'm in doubt if most compilers can optimize
all pointer arithmetic that arises here), there is one bigger trouble:
the code simply does not work, as the operator= is actually declared for
argument types ( CanonicalReplacement<T>&, const T& ), not ( T&, const T& );
thus, compiler generates default version of it for class T.

The following example illustrates it:

-- code fragment 4 ----------------------------------------------------
struct A
{
A& operator=( const A& ) { cout << "operator= of An"; }
};

struct B
: public CanonicalReplacement< A >
{
A a_;

B() { }
B( const B& ) { cout << "cctor of Bn"; }
void swap() throw() { cout << "swap of Bn"; }
};

int main()
{
B() = B(); // (5)
};
-- end of code fragment 4 ---------------------------------------------

In (5), if CanonicalReplacement `cctor of B' in (3) then `swap of B' in (4).
But actually default B::operator= is generated, it calls A::operator= for
`a_' field and prints `operator= of A'.

Unfortunately it is not possible to use Barton-Nackman method of restricted
template expansion with aid of friend member function (see code fragment 5),
because operator= must be a member function (according to section 13.5.3.1
of the ISO standard for C++ programming language).

-- code fragment 5 ----------------------------------------------------
template< typename T >
struct CanonicalReplacement
{
friend T& operator=( T& lvalue, const T& rvalue )
{
if( &lvalue != &rvalue )
{
T temp_object( rvalue );
lvalue.swap( temp_object );
}
return lvalue;
}
};
-- end of code fragment 5 ---------------------------------------------

The idion can be implemented via preprocessor, but this is a hack of course.
Does anybody have any other ideas on this?

-- Mikhail Kupchik

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Stefan Heinzmann
Guest





PostPosted: Fri Aug 20, 2004 1:22 am    Post subject: Re: Template-based implementation of Sutter's exception-safe Reply with quote



Mikhail N. Kupchik wrote:

Quote:
I have a question regarding Herb Sutter's idiom of implementation of
operator= via nonthrowing swap() member function, to guarantee strict
exception safety.

The idea of the idiom is shown by the example:

-- code fragment 1 ----------------------------------------------------
class MyClass
{
...
public:
void swap( MyClass& ) throw();

MyClass& operator=( const MyClass& rvalue )
{
MyClass other( rvalue ); // (1)
swap( other );
return *this;
}
};
-- end of code fragment 1 ---------------------------------------------

That leads me to another question:

Is there anything wrong with abbreviating this to:

class MyClass
{
...
public:
void swap( MyClass& ) throw();

MyClass& operator=( MyClass rvalue )
{
swap( rvalue );
return *this;
}
};

Not that it makes much of a difference; I was just wondering...

--
Cheers
Stefan

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Mikhail N. Kupchik
Guest





PostPosted: Fri Aug 20, 2004 1:46 am    Post subject: Re: Template-based implementation of Sutter's exception-safe Reply with quote



Fix: of course I mean

struct B
: public CanonicalReplacement< B >

in code fragment 4.

-- Mikhail Kupchik

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
Andrei Alexandrescu (See
Guest





PostPosted: Fri Aug 20, 2004 1:44 pm    Post subject: Re: Template-based implementation of Sutter's exception-safe Reply with quote

"Stefan Heinzmann" <stefan_heinzmann (AT) yahoo (DOT) com> wrote

Quote:
That leads me to another question:

Is there anything wrong with abbreviating this to:

class MyClass
{
...
public:
void swap( MyClass& ) throw();

MyClass& operator=( MyClass rvalue )
{
swap( rvalue );
return *this;
}
};

Not that it makes much of a difference; I was just wondering...

It does. See http://moderncppdesign.com/publications/cuj-02-2003.html.

Andrei



[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Hyman Rosen
Guest





PostPosted: Fri Aug 20, 2004 1:50 pm    Post subject: Re: Template-based implementation of Sutter's exception-safe Reply with quote

Stefan Heinzmann wrote:
Quote:
Is there anything wrong with abbreviating this to:
MyClass& operator=( MyClass rvalue )
{
swap( rvalue );
return *this;
}

Not at all. In fact, I would probably make member swap return *this just
so that I could do
MyClass &operator=(MyClass rvalue) { return swap(rvalue); }

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
David Abrahams
Guest





PostPosted: Fri Aug 20, 2004 2:36 pm    Post subject: Re: Template-based implementation of Sutter's exception-safe Reply with quote

[email]mikhkup (AT) mail (DOT) ru[/email] (Mikhail N. Kupchik) writes:

Quote:
Hi All.

I have a question regarding Herb Sutter's idiom of implementation of
operator= via nonthrowing swap() member function, to guarantee strict
exception safety.

Herb doesn't deserve the blame for that technique (though maybe for
recommending it a bit too heartily). The swapping assignment operator
is almost always a bad idea, especially in a template, because it
spends cycles on the strong guarantee at what may be the wrong level
of granularity.

(http://lists.boost.org/MailArchives/boost/msg36928.php)

Any time you make assignment give the strong guarantee by copying and
swapping, you force anyone who wants to use assignment in an operation
which doesn't need that strong guarantee to pay for the unneccessary
copy, which could be very expensive.

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
James Hopkin
Guest





PostPosted: Fri Aug 20, 2004 2:38 pm    Post subject: Re: Template-based implementation of Sutter's exception-safe Reply with quote

Oops: in my other reply I left in a paragraph about std::swap, which I
meant to remove.

Default std::swap is definitely *not* what we want, as that will
recursively call the assignment operator until the end of time.

James

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
Back to top
James Hopkin
Guest





PostPosted: Fri Aug 20, 2004 2:39 pm    Post subject: Re: Template-based implementation of Sutter's exception-safe Reply with quote

[email]mikhkup (AT) mail (DOT) ru[/email] (Mikhail N. Kupchik) wrote in message news:<a03cce47.0408181133.3e443587 (AT) posting (DOT) google.com>...
Quote:

Does anybody have any other ideas on this?


I'm not sure I'd want to do this (reasons listed below), but I can
suggest an implementation.

// T requirements: no-throw swap(T&, T&)
// no copy assignment operator defined

template< typename T >
struct CanonicalReplacement
{

BOOST_STATIC_ASSERT(boost::is_base_and_derived<CanonicalReplacement,
T>::value);

CanonicalReplacement& operator=(const CanonicalReplacement&
other)
{
T& derived_this = static_cast<T&> (*this);
const T& derived_other = static_cast<const T&>(other);

if(&derived_this != &derived_other) // this line is an
optimisation only
{
T temp(derived_other);

swap(derived_this, temp);
}
return *this;
}
};

This can be *privately* inherited, since assignment operators don't
get inherited anyway.

I used a namespace-scope swap, because this is more general. You can
always write a global swap to call a member one.

I'm calling a global std::swap, since this is more general (if you
want a member version to be used, you can define a global swap which
calls the member one).

To be extra safe, you can use boost::address_of rather than the &
operator, just in case the built-in & has been overridden.


As for why I wouldn't do this:

1) Assignment by swapping with temporary is a well-known idiom
(largely thanks to Herb - correct me if I'm wrong). Most coders will
immediately understand an assignment operator written this away, but
may puzzle for a while seeing this base. Of course, that problem goes
away if it were to become an accepted idiom (chicken and egg
situation).

2) Ease of mis-use: if the client defines a copy assignment
operator in T or any derived class without removing this base, things
go silently wrong. I can't think of a way of preventing this without
overhead.

3) Personal taste: I wouldn't want to use a base class to do
something so simple.

4) Potential inheritance overheads: with ideal compilers this
method would always be zero overhead, but in the real world we know
that's not always the case, particularly if you needed to inherit
other classes.

5) Compilation time: I would imagine instantiating this template
class everywhere would have some impact on compile times.


Cheers,
James

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Daniel Krügler (ne Spange
Guest





PostPosted: Fri Aug 20, 2004 2:41 pm    Post subject: Re: Template-based implementation of Sutter's exception-safe Reply with quote

Hello Stefan Heinzmann,

Stefan Heinzmann schrieb:

Quote:
Mikhail N. Kupchik wrote:



I have a question regarding Herb Sutter's idiom of implementation of
operator= via nonthrowing swap() member function, to guarantee strict
exception safety.

The idea of the idiom is shown by the example:

-- code fragment 1 ----------------------------------------------------
class MyClass
{
...
public:
void swap( MyClass& ) throw();

MyClass& operator=( const MyClass& rvalue )
{
MyClass other( rvalue ); // (1)
swap( other );
return *this;
}
};
-- end of code fragment 1 ---------------------------------------------



That leads me to another question:

Is there anything wrong with abbreviating this to:

class MyClass
{
...
public:
void swap( MyClass& ) throw();

MyClass& operator=( MyClass rvalue )
{
swap( rvalue );
return *this;
}
};

Not that it makes much of a difference; I was just wondering...

In principal, it doesn't, the standard explicitely allows this signature

for operator= to
be a valid copy assignment operator (12.8/p.9). I personally don't like
that basic approach
very much, because it makes your implementation visible to the
interface. To me its even
worse than

void foo(const int);

instead of

void foo(int);

in the **declaration** of foo. While this also makes implementation
visible, it doesn't do
that in a fundamental way as shown in

void foo( MyClass rvalue )

versus

void foo( const MyClass& rvalue )

which changes from argument-by-ref- to argument-by-value just for
reasons of the
**internals** of foo. Furtheron my first reaction on seeing the interface of

void foo( MyClass rvalue )

is:

1) Aaah, MyClass is a very light class, which has no heavy copy overhead
(That assumption
is false in your generally used version...)

2) Why the heck does that programmer copy a class object here??

Just my personal 2 Euro cent....

Greetings from Bremen,

Daniel Krügler




[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Steven E. Harris
Guest





PostPosted: Sat Aug 21, 2004 3:56 am    Post subject: Re: Template-based implementation of Sutter's exception-safe Reply with quote

David Abrahams <dave (AT) boost-consulting (DOT) com> writes:

Quote:
Any time you make assignment give the strong guarantee by copying
and swapping, you force anyone who wants to use assignment in an
operation which doesn't need that strong guarantee to pay for the
unneccessary copy, which could be very expensive.

For those who do need the strong guarantee at a higher level, how do
they go about getting it? I understand the "SGI argument" about
concurrency control, but I'm missing the analogous option here to add
exception-safe "locks" at a higher level.

Take your example where a client wants an assignment followed by
push_back() to have the strong guarantee.¹ Is this a potential
solution?


// Neither compiled nor tested.

template <typename C, typename T>
C& safe_assign_push(C& dest, C const& src, T const& val)
{
C temp( src );
temp.push_back( val );
dest.swap( temp );
return dest;
}


Footnotes:
¹ http://lists.boost.org/MailArchives/boost/msg36928.php

--
Steven E. Harris

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Mikhail N. Kupchik
Guest





PostPosted: Sat Aug 21, 2004 10:20 am    Post subject: Re: Template-based implementation of Sutter's exception-safe Reply with quote

[email]jhopkin (AT) reflectionsinteractive (DOT) com[/email] (James Hopkin) wrote in message news:<a0368c9c.0408200228.2ccd283 (AT) posting (DOT) google.com>...
Quote:
mikhkup (AT) mail (DOT) ru (Mikhail N. Kupchik) wrote in message news:<a03cce47.0408181133.3e443587 (AT) posting (DOT) google.com>...

Does anybody have any other ideas on this?


I'm not sure I'd want to do this (reasons listed below), but I can
suggest an implementation.

Hi.

Default operator= in T calls operator= for all base classes and fields, not only
for CanonicalReplacement< T >.

The program below

-- code fragment 6 ----------------------------------------------------

template< typename T >
struct CanonicalReplacement
{
CanonicalReplacement& operator=(const CanonicalReplacement& other)
{
T& derived_this = static_cast<T&> (*this);
const T& derived_other = static_cast<const T&>(other);

if(&derived_this != &derived_other) // this line is an optimisation only
{
T temp(derived_other);

derived_this.swap(temp);
}
return *this;
}
};

struct A
{
A& operator=( const A& ) { cout << "A::operator= (should not be called)n"; }
};

struct B
: public CanonicalReplacement< B >
{
A a_;
B() { }
void swap( B& ) throw() { cout << "B::swap()n"; }
B( const B& ) { cout << "cctor of Bn"; }
};

int main()
{
B() = B();
}

-- code fragment 6 ----------------------------------------------------

prints

cctor of B
B::swap()
A::operator= (should not be called)

-- Mikhail Kupchik

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Stefan Heinzmann
Guest





PostPosted: Sat Aug 21, 2004 4:04 pm    Post subject: Re: Template-based implementation of Sutter's exception-safe Reply with quote

Daniel Krügler (ne Spangenberg) wrote:
Quote:
Stefan Heinzmann schrieb:
Mikhail N. Kupchik wrote:

MyClass& operator=( const MyClass& rvalue )
{
MyClass other( rvalue ); // (1)
swap( other );
return *this;
}

Is there anything wrong with abbreviating this to:

MyClass& operator=( MyClass rvalue )
{
swap( rvalue );
return *this;
}

Not that it makes much of a difference; I was just wondering...

[...] I personally don't like
that basic approach
very much, because it makes your implementation visible to the
interface. [...] [It] changes from argument-by-ref- to argument-by-value just for
reasons of the
**internals** of foo. Furtheron my first reaction on seeing the interface of

void foo( MyClass rvalue )

is:

1) Aaah, MyClass is a very light class, which has no heavy copy overhead
(That assumption
is false in your generally used version...)

2) Why the heck does that programmer copy a class object here??

Combine this with the arguments of Hyman and particularly Andrei in his
article and we have a spat ;-)

Isn't the choice between passing arguments by value and passing them by
const reference mainly driven by efficiency considerations? Inasmuch
this is the case I would regard both forms as more or less equivalent
and I would leave the decision which one to use to the designer of the
function in question.

--
Cheers
Stefan

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
David Abrahams
Guest





PostPosted: Sat Aug 21, 2004 4:04 pm    Post subject: Re: Template-based implementation of Sutter's exception-safe Reply with quote

"Steven E. Harris" <seh (AT) panix (DOT) com> writes:

Quote:
David Abrahams <dave (AT) boost-consulting (DOT) com> writes:

Any time you make assignment give the strong guarantee by copying
and swapping, you force anyone who wants to use assignment in an
operation which doesn't need that strong guarantee to pay for the
unneccessary copy, which could be very expensive.

For those who do need the strong guarantee at a higher level, how do
they go about getting it?

Copy and swap works just as well for clients as it does for the class
itself.

Quote:
I understand the "SGI argument" about concurrency control, but I'm
missing the analogous option here to add exception-safe "locks" at a
higher level.

Take your example where a client wants an assignment followed by
push_back() to have the strong guarantee.¹ Is this a potential
solution?


// Neither compiled nor tested.

template C& safe_assign_push(C& dest, C const& src, T const& val)
{
C temp( src );
temp.push_back( val );
dest.swap( temp );
return dest;
}

Yes, exactly, provided that C provides a nothrow swap (which was
required for the alternative anyway).

--
Dave Abrahams
Boost Consulting
http://www.boost-consulting.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Glen Low
Guest





PostPosted: Mon Aug 23, 2004 10:31 am    Post subject: Re: Template-based implementation of Sutter's exception-safe Reply with quote

Quote:
In (5), if CanonicalReplacement<B>::operator= is called, it will print
`cctor of B' in (3) then `swap of B' in (4).
But actually default B::operator= is generated, it calls A::operator= for
`a_' field and prints `operator= of A'.

The problem is copy constructors are not inherited; it only seems that
way because if you don't define a copy constructor, the compiler tries
to chain the superclass one with the ones for each member.

You can kludgify it by creating an Replace member in
CanonicalReplacement<B> to do the copy and swap idiom, then calling
this from the defined copy constructor in B. Or even make Replace a
function template like std::swap is.

The only other thing I can think of (which is subtly icky on other
levels) is to reverse the inheritance:

1. Put all the core B stuff into B_core.
2. Make B inherit from CanonicalReplacement<B_core>, and ensure B
itself has no member variables etc.
3. Regularize your other constructors (that's the icky part).

Cheers,
Glen Low, Pixelglow Software
www.pixelglow.com

[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]

Back to top
Daniel Krügler (ne Spange
Guest





PostPosted: Tue Aug 24, 2004 7:53 pm    Post subject: Re: Template-based implementation of Sutter's exception-safe Reply with quote

Good morning Stefan Heinzmann,

Stefan Heinzmann schrieb:

Quote:
Combine this with the arguments of Hyman and particularly Andrei in his
article and we have a spat ;-)

Isn't the choice between passing arguments by value and passing them by
const reference mainly driven by efficiency considerations? Inasmuch
this is the case I would regard both forms as more or less equivalent
and I would leave the decision which one to use to the designer of the
function in question.

My opinion stands. I think, the usage of call-by-value versus

call-by-reference in the mentioned
case is purely due to take advantage of an optimization opportunity of
the compiler, Thus it tells
via its interface about its internal implementation.

Consider a more complex function with signature

class Heavy{..};

void do_hard_number_crunching(Heavy);

versus

void do_hard_number_crunching(const Heavy&);

I think, most programmers will accept the 2nd version as the canonical
interface, not the 1st
version.

Now assume, the reason for choosing the first version was done to take
advantage of the
compiler optimizations mentioned by other posters, because the current
implementation of
do_hard_number_crunching needs a copy of its argument. Lets further
assume, 2 month
later you find an alternative implementation, which doesn't need this
copy of the argument.
To ensure stability of the interface you are forced to left the
signature as it is, but now are
forced to accept an unnecessary copy of the argument which could
probably loose a
good part of your performance advantage from the new implementation.

I think this situation shows that we encounter here the often found
discrepance between
efficiency and coupling. The rule to get rid of unnecessary coupling (to
a given implementation)
says we should not take the call-by-value here, but effeciency says
otherwise.

I think as e general guide I would recommend the following:

- Comment such unusual signatures.
- Use the forementioned call-by-value alternative in case of functions,
which probably have
a stable implementation or just are part of the implementation (non-public).

Just my personal 2 Euro cent...

Daniel






[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]


Back to top
Display posts from previous:   
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ Language (Moderated) All times are GMT
Page 1 of 1

 
Jump to:  
You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum


Powered by phpBB © 2001, 2006 phpBB Group
SEO toolkit © 2004-2006 webmedic.