stolen Yaslander technology: a really funky implementation of std::vector's constructor

  • Follow


Through ocult paths that you don't need to know about, I got one piece of
technology from the Yaslanders. As we all know, the Yaslanders are the
implementers of YASLI (Yet Another Standard Library Implementation), which
is by definition the best implementation there could ever be. Here's that
piece of code.

template <class InputIteratorOrN, class InputIteratorOrT>
explicit vector(InputIteratorOrN parm1, const InputIteratorOrT& parm2 = T(),
         const Allocator& a = Allocator()) : alloc_(a)
{
     init_empty();
     struct Guard {
         vector* pThis_;
         ~Guard() { if (pThis_) pThis_->~vector(); }
     } guard = { this };
     assign(parm1, parm2);
     guard.pThis_ = 0;
}

There are a couple of really funny aspects in this implementation of
std::vector's constructor:

* The constructor serves as both the one that takes a length and a value, as
well as the one that takes two iterators.

* It has a default value of T() assigned to the latter. This doesn't seem to
bother the template engine in the least when dealing with iterators
(although most of the time it doesn't make sense to initialize an iterator
with a T()). This is because when instantiating a template, the default
parameter is only tried if needed.

* Because of the above, the second parameter is taken by const reference.
This way, the length/value constructor works properly, while the
iterator/iterator constructor is not phased off in the least (although in
the standard the second iterator is taken by value).

* A simple technique a la ScopeGuard serves as a rock-solid and efficient
exception safety device that doesn't do any manual cleanup, but simply uses
the destructor for that. Such use is safe because at the time the guard is
in effect, the vector is fully and properly initialized.

* The work is done by delegating to assign(). Now here's the really nice
part. The assign function has the following overloads: assign(size_type,
const T&) and template <class Iter> assign(Iter, Iter). Notice how these two
overloads match the two uses of the constructor like a glove (well, like a
glove with two fingers only). So in fact the constructor above blindly
forwards its arguments to assign() without having the faintest idea of
what's going on! Then assign has all the machinery needed to distinguish
between length/object and iterator/iterator. All of the STL implementation
I've seen have some (likely a LOT of) duplication between the constructor
code and the assign() code. This has none. Kewl!

The question is: is this implementation 100% Standard-compliant? Or did the
Yaslanders overlook an important corner case?


Andrei



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply SeeWebsiteForEmail (94) 7/31/2004 1:46:43 PM

Andrei Alexandrescu (See Website for Email) wrote:
> Through ocult paths that you don't need to know about, I got one piece of
> technology from the Yaslanders. As we all know, the Yaslanders are the
> implementers of YASLI (Yet Another Standard Library Implementation),
> which is by definition the best implementation there could ever be.
> Here's that piece of code.
>
> template <class InputIteratorOrN, class InputIteratorOrT>
> explicit vector(InputIteratorOrN parm1, const InputIteratorOrT& parm2 =
>          T(), const Allocator& a = Allocator()) : alloc_(a)
> {
>      init_empty();
>      struct Guard {
>          vector* pThis_;
>          ~Guard() { if (pThis_) pThis_->~vector(); }
>      } guard = { this };
>      assign(parm1, parm2);
>      guard.pThis_ = 0;
> }

[SNIP]

> The question is: is this implementation 100% Standard-compliant? Or did
> the Yaslanders overlook an important corner case?

Note #1: Although only a bit slower, but it is slower than a "normal"
initialization, because first you init_empty(), which is only necessary
because of the hack-guard.

Note #2: The guard does not need to call the destructor.  It only needs to
call a function, which destroys the pointed array properly.  Most probably
the function, which is called from the destructor.

Note #3: Since we cannot really see what is inside a Yas Lee (cf. Bruce Lee,
the fastest and coolest) vector (as members etc.) I need to mention a 3rd
thing: as with the current hack, any base classes and members varianles of
the vector will be destructed twice in case of an exception!

While Note #3 may loose all of its power in face of the fact that vector
usually has no bases and its members are PODs or like with no destructors,
it is still interesting issue, since with the magic going on in Yasli, it is
just Question of Time(TM).

So IMHO this solution is not nice.  But easy to fix: factor out the
necessary delete operation from the destructor, as per Note #2.

There may be many other core/library issues with it, but this is the one
which is obvious enough for me to see... and IMHO one which bites.

I would say that the design shows a little misunderstanding about what a
destructor is, and that it does (well, may do) a lot more than what meets
the eye or IOW what is between the curly braces of it.

-- 
WW aka Attila
:::
I couldn't afford a cool signature, so I just got this one.



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply White 8/1/2004 2:19:09 AM


"White Wolf" <wolof@freemail.hu> wrote in message
news:ceh1k5$f35$1@phys-news1.kolumbus.fi...
 > Andrei Alexandrescu (See Website for Email) wrote:
 > > The question is: is this implementation 100% Standard-compliant? Or did
 > > the Yaslanders overlook an important corner case?
 >
 > Note #1: Although only a bit slower, but it is slower than a "normal"
 > initialization, because first you init_empty(), which is only necessary
 > because of the hack-guard.

I can tell as one who tried a lot, including shamelessly looking at the work
of Howard Hinnant (which I got word will be soon offered the title of
Honoris Citizen of Yasland) that it is very hard to come up with a more
efficient implementation unless you know for sure that the compiler (1)
doesn't do anything funky in catch (...) and (2) implements try with zero
overhead, which many don't.

 > Note #2: The guard does not need to call the destructor.  It only needs to
 > call a function, which destroys the pointed array properly.  Most probably
 > the function, which is called from the destructor.
 >
 > Note #3: Since we cannot really see what is inside a Yas Lee (cf. Bruce
Lee,
 > the fastest and coolest) vector (as members etc.) I need to mention a 3rd
 > thing: as with the current hack, any base classes and members varianles of
 > the vector will be destructed twice in case of an exception!

Aha! Thank you Attila, you found a bug. In fact yasli::vector does have a
potentially non-POD member, its allocator. So yes, the allocator will be
destroyed twice, which is a bug.

A good solution is, as you mentioned, is to define and call a function from
both the destructor and the guard. Thanks again!

 > I would say that the design shows a little misunderstanding about what a
 > destructor is, and that it does (well, may do) a lot more than what meets
 > the eye or IOW what is between the curly braces of it.

Yah! Those Yaslanders suck!!! :oD


Andrei



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Andrei 8/1/2004 11:04:09 AM

In article <2n0bq4FrvsbqU1@uni-berlin.de>, "Andrei Alexandrescu \(See 
Website for Email\)" <SeeWebsiteForEmail@moderncppdesign.com> says...
>      init_empty();
>      struct Guard {
>          vector* pThis_;
>          ~Guard() { if (pThis_) pThis_->~vector(); }
>      } guard = { this };
>      assign(parm1, parm2);
>      guard.pThis_ = 0;
> 
> 

A small question of style, Andrei, if you dont mind...
Why are you using a scope guard in this precise case ?

I mean, it is equivalent to write

       init_empty();
       try
       {
         assign(parm1, parm2);
       }
       catch(...)
       {
         cleanup(); //  ~vector();
         throw;           	
       }

right ?

I ask because my mind - and perhaps others - grasps more quickly the
second form.  And a scope guard is useful when we have (potentially)
more than one atomic operation, isn't it  ?  Something you certainly
dont want in this case, if I got you...


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply news 8/1/2004 5:14:39 PM

"news user" <news@sisyphus.news.be.easynet.net> wrote in message
news:MPG.1b773efee3cc2636989792@news.easynet.be...
 > A small question of style, Andrei, if you dont mind...
 > Why are you using a scope guard in this precise case ?
 >
 > I mean, it is equivalent to write
 >
 >        init_empty();
 >        try
 >        {
 >          assign(parm1, parm2);
 >        }
 >        catch(...)
 >        {
 >          cleanup(); //  ~vector();
 >          throw;
 >        }
 >
 > right ?
 >
 > I ask because my mind - and perhaps others - grasps more quickly the
 > second form.  And a scope guard is useful when we have (potentially)
 > more than one atomic operation, isn't it  ?  Something you certainly
 > dont want in this case, if I got you...

Excellent question! I enjoy your form as well, were it not for a couple of
issues.

One is, catch (...) is best avoided when possible. This is because Windows
(and perhaps other OSs piggyback other failures (such as protection
violation) with C++'s exception mechanism, and with catch (...) you catch
those failures as well. There was a fierce newsgroup discussion (I think
either here or on the boost mailing list) that I am not sure I understood
all about, but one thing came clear: if you can do without catch (...) your
code is more portable and better off.

Second, I have direct word from *the* guy who is the mastermind of Visual
C++'s compiler that try/catch blocks have more overhead than just having
objects with destructors on the stack. With that compiler (and many others
that emulate Visual C++), if your code does not have a try statement, it is
generally smaller and faster than if it does.

For these two reasons, I recently started preferring code a la scopeguard to
code that catches everything and rethrows - at least for libraries that you
can use with various compiler settings and in various environments. It's
just a tad more environment-neutral.

But at the end of the day that's just a "portablity to bad environments"
issue and, as we all know, we should despise that kind of portablity with
all our might :o).


Andrei



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Andrei 8/2/2004 10:12:23 AM

Andrei Alexandrescu (See Website for Email) wrote:
 > "White Wolf" <wolof@freemail.hu> wrote in message
 > news:ceh1k5$f35$1@phys-news1.kolumbus.fi...
 >  > Andrei Alexandrescu (See Website for Email) wrote:
 >  > > The question is: is this implementation 100% Standard-compliant? Or
 >  did > > the Yaslanders overlook an important corner case?
 >  >
 >  > Note #1: Although only a bit slower, but it is slower than a "normal"
 >  > initialization, because first you init_empty(), which is only
 >  necessary > because of the hack-guard.

Well, in fact (sad fact) most of the code I have seen creates first an empty
vector, and then goes on from there with push_back, or assign or...  So in
todays news, this little bit of loss won't show up.  Basically you take the
Java's way out, of first initializing everything to empty and then hope to
be able to do more. ;-)

 > I can tell as one who tried a lot, including shamelessly looking at the
 > work of Howard Hinnant (which I got word will be soon offered the title
 > of Honoris Citizen of Yasland) that it is very hard to come up with a
 > more efficient implementation unless you know for sure that the compiler
 > (1) doesn't do anything funky in catch (...) and (2) implements try with
 > zero overhead, which many don't.

Nice trick anyway, employs the old trick of using RAII instead of catch
blocks.  Basically unless one is

1.) "Eating" exceptions
2.) Converting (including augmenting) exceptions
3.) Handling exceptions (errors, really handling)

one has no right to use a catch or a try.  (If I am wrong, tell me.)


 >  > Note #2: The guard does not need to call the destructor.  It only
 >  needs to > call a function, which destroys the pointed array properly.
 >  Most probably > the function, which is called from the destructor.
 >  >
 >  > Note #3: Since we cannot really see what is inside a Yas Lee (cf.
 > Bruce Lee,
 >  > the fastest and coolest) vector (as members etc.) I need to mention a
 >  3rd > thing: as with the current hack, any base classes and members
 >  varianles of > the vector will be destructed twice in case of an
 > exception!
 >
 > Aha! Thank you Attila, you found a bug. In fact yasli::vector does have a
 > potentially non-POD member, its allocator. So yes, the allocator will be
 > destroyed twice, which is a bug.

I knew that.  You always inherit allocators. ;-)  Gotcha!  (Pretending to be
Steve Dewhurstm, with not much luck.)

 > A good solution is, as you mentioned, is to define and call a function
 > from both the destructor and the guard. Thanks again!

Very welcome.  Can I put it onto my resume, that I have helped Andrei
Alexandrescu?  Not that anyone will believe it, once they meet me. :-)

 >  > I would say that the design shows a little misunderstanding about
 >  what a > destructor is, and that it does (well, may do) a lot more than
 >  what meets > the eye or IOW what is between the curly braces of it.
 >
 > Yah! Those Yaslanders suck!!! :oD

See?  Keep some order there! Think about what other bugs can be there if I
have found one in 5 minutes. ;-)

-- 
WW aka Attila
:::
You can't help the poor man by destroying the rich.



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply White 8/2/2004 10:16:33 AM

news user wrote:
 > In article <2n0bq4FrvsbqU1@uni-berlin.de>, "Andrei Alexandrescu \(See
 > Website for Email\)" <SeeWebsiteForEmail@moderncppdesign.com> says...
 >>      init_empty();
 >>      struct Guard {
 >>          vector* pThis_;
 >>          ~Guard() { if (pThis_) pThis_->~vector(); }
 >>      } guard = { this };
 >>      assign(parm1, parm2);
 >>      guard.pThis_ = 0;
 >>
 >>
 >
 > A small question of style, Andrei, if you dont mind...
 > Why are you using a scope guard in this precise case ?
 >
 > I mean, it is equivalent to write
 >
 >        init_empty();
 >        try
 >        {
 >          assign(parm1, parm2);
 >        }
 >        catch(...)
 >        {
 >          cleanup(); //  ~vector();
 >          throw;
 >        }
 >
 > right ?
 >
 > I ask because my mind - and perhaps others - grasps more quickly the
 > second form.  And a scope guard is useful when we have (potentially)
 > more than one atomic operation, isn't it  ?  Something you certainly
 > dont want in this case, if I got you...

Possibly Andrei will be able to answer this much more precisely than I do,
but I will make my point anyway.  I am a rude man. :-)

My approach is rather a philosophical one, with apparently wrong spelling
(but my spell checker refuses to work... regards to B.G.).  The try-catch is
supposed to be an error handler.  Do you handle the error here?

I can recall now only one place where I can accept a catch(...) without much
thinking, and that is at a thread boundary.

The other reason is that while the costs of the guard used there are fairly
predictable, it is not so with the try-catch blocks.

I believe that try-catch blocks should be imagined as the fire-doors of a
building or those waterproof sunsections, which are supposed to make ships
more safe (and does not always work as proven by the Titanic).  You should
have just enough of them, otherwise your building will be too expensive, or
your ship will sink under its own weight.  (Of course what I mean is that
your program will be too resource hungry.)

IMHO most of the code in a system must be exception neutral.  Meaning that
it should work if an exception "runs through it", but should not try-catch
or throw.  The exceptions are mainly two places: places which throw, and
places which must catch.  the latter being:

1. "Eating" exceptions (thread boundary, destructor, catches ...)

2. Really handling an error (exception)

3. Translating or augmenting an error (exception)

The reason for #1 is that sometimes exceptions will be ignored, or converted
into a non-exception form.

The reason for #2 is obvious, the originator of the (trans)action should be
able to handle if it has failed.

The reason for #3 is tracing (when augmenting) or converting an exception
into something what makes sense to the caller, for example turning a
bad_alloc into a release_call(reason::out_of_resources).

-- 
WW aka Attila
:::
A foolproof method for sculpting an elephant: first, get a huge block of
marble, then you chip away everything that doesn't look like an elephant.



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply White 8/2/2004 10:17:29 AM

"Andrei Alexandrescu \(See Website for Email\)" <SeeWebsiteForEmail@moderncppdesign.com> wrote in message news:<2n0bq4FrvsbqU1@uni-berlin.de>...
 > ...
 > template <class InputIteratorOrN, class InputIteratorOrT>
 > explicit vector(InputIteratorOrN parm1, const InputIteratorOrT& parm2 = T(),
 >          const Allocator& a = Allocator()) : alloc_(a)
 > {
 >      init_empty();
 >      struct Guard {
 >          vector* pThis_;
 >          ~Guard() { if (pThis_) pThis_->~vector(); }
 >      } guard = { this };
 >      assign(parm1, parm2);
 >      guard.pThis_ = 0;
 > }
 >
 > There are a couple of really funny aspects in this implementation of
 > std::vector's constructor:
 > ...

At the dawn of computer age when many programmers wrote tricky codes
to save memory shortage, IBM instructed not to write tricky codes, but
to write understandable codes. I think the lesson is still true.

I came to understand why the vector constructor was able to accept
length and value after I read Andrei's long explanation. Even if the
code is used in the deepest part of a library, somebody must maintain
the code. Template could be a "goto" if you use it without control.

Though I know Andrei is interested in exploiting the template
capability, I would like to know Andrei's opinion on this matter, if
he doesn't mind. Comments could be a help, but not enough.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply hosoda 8/2/2004 10:28:28 AM

news user <news@sisyphus.news.be.easynet.net> wrote in message news:<MPG.1b773efee3cc2636989792@news.easynet.be>...
 > A small question of style, Andrei, if you dont mind...
 > Why are you using a scope guard in this precise case ?
 >
 > I mean, it is equivalent to write
 >
 >        init_empty();
 >        try
 >        {
 >          assign(parm1, parm2);
 >        }
 >        catch(...)
 >        {
 >          cleanup(); //  ~vector();
 >          throw;
 >        }
 >
 > right ?
 >

Because catch(...) catches *all* exceptions, calls cleanup, then
rethrows the exception.  It will even catch exceptions that nobody
knows how to cleanup.  Thus, you may not be able to call cleanup().

It is platform dependent if stack based objects are unwound due to an
uncaught exception.  On my platform, they are not.  So, if nobody
catches the exception, then nobody cares about the state of any
objects, so the stack does not need to be unwound.

Another reason, which is again platform dependent, is manipulation of
the stack and program counters.  On my platform, catch (...) and a
re-throw causes the traceback information to remember the location
from which the exception was re-thrown, NOT the original location.
So, when trying to investigate a crash due to a thrown exception, the
only information we have is this re-throw spot.

Finally, catch(...)/throw is simply evil.  See this thread for why:
http://tinyurl.com/5sdp9

-joshua lehrer
factset research systems
NYSE:FDS

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply usenet_cpp 8/2/2004 10:30:07 AM

news user <news@sisyphus.news.be.easynet.net> wrote in message news:<MPG.1b773efee3cc2636989792@news.easynet.be>...
 > In article <2n0bq4FrvsbqU1@uni-berlin.de>, "Andrei Alexandrescu \(See
 > Website for Email\)" <SeeWebsiteForEmail@moderncppdesign.com> says...
 > >      init_empty();
 > >      struct Guard {
 > >          vector* pThis_;
 > >          ~Guard() { if (pThis_) pThis_->~vector(); }
 > >      } guard = { this };
 > >      assign(parm1, parm2);
 > >      guard.pThis_ = 0;
 > >
 > >
 >
 > A small question of style, Andrei, if you dont mind...
 > Why are you using a scope guard in this precise case ?
 >
 > I mean, it is equivalent to write
 >
 >        init_empty();
 >        try
 >        {
 >          assign(parm1, parm2);
 >        }
 >        catch(...)
 >        {
 >          cleanup(); //  ~vector();
 >          throw;
 >        }
 >
 > right ?
 >

yup, I think it is (equivalent).
But I like the guard better - seems to me as a very cool idiom - that
could be used from many constructors (yet to be developed ;))

About forwarding to assign() -> totally rocks, Andrei!

Best,
John

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply jtorjo 8/2/2004 10:35:07 AM

"Joshua Lehrer" <usenet_cpp@lehrerfamily.com> wrote in message
news:31c49f0d.0408012136.9292b1e@posting.google.com...
> news user <news@sisyphus.news.be.easynet.net> wrote in message
news:<MPG.1b773efee3cc2636989792@news.easynet.be>...
[]
>
> Because catch(...) catches *all* exceptions, calls cleanup, then
> rethrows the exception.  It will even catch exceptions that nobody
> knows how to cleanup.  Thus, you may not be able to call cleanup().
>
[]
>
> Finally, catch(...)/throw is simply evil.  See this thread for why:
> http://tinyurl.com/5sdp9

There is a part of this thead that is not completely clear to me.  Are you
counting on the fact that hardware exceptions, such as memory access
violations and memory parity errors do _not_ inherit from std::exception?
Is this the way that you want to separate the two types of exceptions?

Robert Kindred

>
> -joshua lehrer
> factset research systems
> NYSE:FDS


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Robert 8/2/2004 3:00:23 PM

usenet_cpp@lehrerfamily.com (Joshua Lehrer) wrote in message news:<31c49f0d.0408012136.9292b1e@posting.google.com>...
> news user <news@sisyphus.news.be.easynet.net> wrote in message news:<MPG.1b773efee3cc2636989792@news.easynet.be>...
>  > A small question of style, Andrei, if you dont mind...
>  > Why are you using a scope guard in this precise case ?
>  >
>  > I mean, it is equivalent to write
>  >
>  >        init_empty();
>  >        try
>  >        {
>  >          assign(parm1, parm2);
>  >        }
>  >        catch(...)
>  >        {
>  >          cleanup(); //  ~vector();
>  >          throw;
>  >        }
>  >
>  > right ?
>  >
> 


A few more reasons:

a stack based object will be unwound in the face of a return statement
which may [inadvertently] be added at a later date.  catch(...) will
not run in the face of a "return".

Second, try/catch can causes excessive nesting.  Which is easier to
read (assume increment may throw):

void func() {
 try {
  increment(i);
  try {
   increment(j);
   //code here
  } catch (...) {
   decrement(j);
  }
 } catch (...) {
  decrement(i);
 }
}

or

void func() {
 increment(i);
 ON_BLOCK_EXIT(decrement,ByRef(i));

 increment(j);
 ON_BLOCK_EXIT(decrement,ByRef(j));

 //code here
}

And finally, my favorite, is something I call "data locale".  In the
try/catch example, incrementing and decrementing 'i' are far apart,
separated by the incrementing and decrementing of 'j', as well as the
guts of the function "//code here".  With a stack based object, the
set-up code is immediately next to the tear-down code.  If you change
the type of 'i', or you decide to modify the set-up, you are
immediately reminded that the tear-down needs to be fixed, as you are
staring right at it.

For me, this last reason is reason enough to prefer stack based
objects over try/catch(...).

joshua lehrer
factset research systems
NYSE:FDS

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply usenet_cpp 8/2/2004 3:11:10 PM

"Andrei Alexandrescu \(See Website for Email\)" <SeeWebsiteForEmail@moderncppdesign.com> wrote in message news:<2n0bq4FrvsbqU1@uni-berlin.de>...

> template <class InputIteratorOrN, class InputIteratorOrT>
> explicit vector(InputIteratorOrN parm1, const InputIteratorOrT& parm2 = T(),
>          const Allocator& a = Allocator()) : alloc_(a)
> {
>      init_empty();
>      struct Guard {
>          vector* pThis_;
>          ~Guard() { if (pThis_) pThis_->~vector(); }
>      } guard = { this };
>      assign(parm1, parm2);
>      guard.pThis_ = 0;
> }


First of all, using an explicit destructor on an object that might be
allocated on the stack is almost always a route to disaster.  You can
remember that as a basic guideline.  There are two reasons:

   1. The built-in destruction of automatic variables is going to
happen anyway

   2. Even if you're going to re-construct the object before the
built-in destruction occurs, if construction can throw there will
still be a double-destruction.

Attila already pointed out you have a double-destruction problem here,
but it is a problem regardless of whether or not there are non-POD
members or bases -- vector itself has a non-trivial destructor.


Second of all, the right way to do what you're trying to accomplish
without try/catch is already present in the STLPort codebase.  I know
because I put it there myself.  You partition the phases of
construction into members and bases.  In the existing implementation
the init_empty() part is handled by a base class, and the part that
does assign() is handled in the vector itself.  The base class is also
responsible for cleaning up any constructed elements.

Of course, the existing implementation doesn't use assign() because
that would generate wasted code to destroy any existing elements.
Most compilers aren't smart enough to deduce that the code is dead in
that case because the vector's start and end pointers are both 0.

> There are a couple of really funny aspects in this implementation of
> std::vector's constructor:
>
> * The constructor serves as both the one that takes a length and a value, as
> well as the one that takes two iterators.

You might want to look at 23.1.1/9.  SFINAE might be a more
appropriate tool for this problem.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply dave 8/2/2004 8:08:03 PM

"Andrei Alexandrescu \(See Website for Email\)" <SeeWebsiteForEmail@moderncppdesign.com> wrote in message news:<2n0bq4FrvsbqU1@uni-berlin.de>...
> template <class InputIteratorOrN, class InputIteratorOrT>
> explicit vector(InputIteratorOrN parm1, const InputIteratorOrT& parm2 = T(),
>          const Allocator& a = Allocator()) : alloc_(a)
> {
>      init_empty();
>      struct Guard {
>          vector* pThis_;
>          ~Guard() { if (pThis_) pThis_->~vector(); }
>      } guard = { this };
>      assign(parm1, parm2);
>      guard.pThis_ = 0;
> }

Though Attilla already pointed out the potential double-delete of the
allocator data member, is it really legal for Guard to call the
vector's destructor when its constructor hasn't successfully
completed?

Mike

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply mbland 8/2/2004 10:46:59 PM

"Robert Kindred" <RKindred@SwRI.edu> wrote in message news:<10gshd84lsm0s04@corp.supernews.com>...
> "Joshua Lehrer" <usenet_cpp@lehrerfamily.com> wrote in message
> news:31c49f0d.0408012136.9292b1e@posting.google.com...
> > news user <news@sisyphus.news.be.easynet.net> wrote in message
> news:<MPG.1b773efee3cc2636989792@news.easynet.be>...
> []
> >
> > Because catch(...) catches *all* exceptions, calls cleanup, then
> > rethrows the exception.  It will even catch exceptions that nobody
> > knows how to cleanup.  Thus, you may not be able to call cleanup().
> >
>  []
> >
> > Finally, catch(...)/throw is simply evil.  See this thread for why:
> > http://tinyurl.com/5sdp9
> 
> There is a part of this thead that is not completely clear to me.  Are you
> counting on the fact that hardware exceptions, such as memory access
> violations and memory parity errors do _not_ inherit from std::exception?
> Is this the way that you want to separate the two types of exceptions?
> 

Correct.

(I assume you meant to type "reason" instead of "way" in the last sentence..)


Hillel Y. Sims
FactSet Research Systems

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply the 8/2/2004 10:47:32 PM

"Andrei Alexandrescu \(See Website for Email\)" <SeeWebsiteForEmail@moderncppdesign.com> wrote in message news:<2n4p61Fsokm5U1@uni-berlin.de>...
> 
> One is, catch (...) is best avoided when possible. This is because Windows
> (and perhaps other OSs piggyback other failures (such as protection
> violation) with C++'s exception mechanism, and with catch (...) you catch
> those failures as well. There was a fierce newsgroup discussion (I think
> either here or on the boost mailing list) that I am not sure I understood
> all about, but one thing came clear: if you can do without catch (...) your
> code is more portable and better off.

This situation also exists on VMS. Also see David Abrahams paper
regarding this topic:
http://www.boost.org/more/error_handling.html

> 
> For these two reasons, I recently started preferring code a la scopeguard to
> code that catches everything and rethrows - at least for libraries that you
> can use with various compiler settings and in various environments. It's
> just a tad more environment-neutral.
> 

We are huge fans of ScopeGuard here at FactSet. We teach it to all new
recruits and encourage its use widely.

Using region-guard style macros, I have recently mutated ScopeGuard
into something I call "finally" and "GuardRegion", which I have been
using even more lately.. I hope to write up an article about it some
day when I have time... here's just a taste, hope you like.. this is
actual standard-compliant C++ code (typed by hand, sorry if it doesn't
compile exactly):

void Obj::func()
{
   // we always want to signal the condvar even if
   // an exception occurs:
   finally(bind(&CondVar::signal, ref(m_cv))) {
      LockRegion(m_mx) {
         GuardRegion(updateguard, bind(&Obj::DoUpdate, this,
SOME_STATUS))) {
            do_something_complicated();
            updateguard.Dismiss();
         }
      }
   }
}


--
Hillel Y. Sims
hsims AT factset.com
FactSet Research Systems

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply the 8/2/2004 10:53:56 PM

John Torjo wrote:
> news user <news@sisyphus.news.be.easynet.net> wrote in message news:<MPG.1b773efee3cc2636989792@news.easynet.be>...
>  > In article <2n0bq4FrvsbqU1@uni-berlin.de>, "Andrei Alexandrescu \(See
>  > Website for Email\)" <SeeWebsiteForEmail@moderncppdesign.com> says...
>  > >      init_empty();
>  > >      struct Guard {
>  > >          vector* pThis_;
>  > >          ~Guard() { if (pThis_) pThis_->~vector(); }
>  > >      } guard = { this };
>  > >      assign(parm1, parm2);
>  > >      guard.pThis_ = 0;
>  > >
>  > >
>  >
>  > A small question of style, Andrei, if you dont mind...
>  > Why are you using a scope guard in this precise case ?
>  >
>  > I mean, it is equivalent to write
>  >
>  >        init_empty();
>  >        try
>  >        {
>  >          assign(parm1, parm2);
>  >        }
>  >        catch(...)
>  >        {
>  >          cleanup(); //  ~vector();
>  >          throw;
>  >        }
>  >
>  > right ?
>  >
> 
> yup, I think it is (equivalent).
> But I like the guard better - seems to me as a very cool idiom - that
> could be used from many constructors (yet to be developed ;))
> 
> About forwarding to assign() -> totally rocks, Andrei!
> 

There have been so many discussions about exceptions
but even experienced people are still making this mistake.
What can you tell about an average programmer!
I am starting to think that C++ exception model
(as it is now) is making more bad than good. :)

try/catch(...) is not equivalent to a scope guard.
catch(...) clause will be invoked for any exception while
scope guard will be invoked only if the exception is ever caught.
It is a big difference!
IMHO a scope guard is a *much* better solution than catch(...).

As for the the code itself.
Calling the destructor from the constructor is not a good idea.
In fact, I think that calling *this* destructor explicitly
(like this->~vector() ) is never a good idea.
If you do this, you are at risk of creating a
conflict in the object's ownership.

Eugene

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply E 8/2/2004 10:54:18 PM

Andrei Alexandrescu (See Website for Email) wrote:
> Through ocult paths that you don't need to know about, I got one piece of
> technology from the Yaslanders. As we all know, the Yaslanders are the
> implementers of YASLI (Yet Another Standard Library Implementation), which
> is by definition the best implementation there could ever be. Here's that
> piece of code.
[...]

Did the ocult paths give us some documentation about YASLI?
I hope the almighty Yaslanders will reveal to the poor Earthlings
that on Earth, the scope guard behavior (in respect to exception 
handling) is platform dependent.
It'll be nice of them to describe how to write
a generic (and platform independent) exception safe
code with YASLI.

Eugene

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply E 8/2/2004 10:55:16 PM

"David Abrahams" <dave@boost-consulting.com> wrote in message
news:8a638f47.0408020827.15a1c75@posting.google.com...
 > "Andrei Alexandrescu \(See Website for Email\)"
<SeeWebsiteForEmail@moderncppdesign.com> wrote in message
news:<2n0bq4FrvsbqU1@uni-berlin.de>...
 > First of all, using an explicit destructor on an object that might be
 > allocated on the stack is almost always a route to disaster.  You can
 > remember that as a basic guideline.  There are two reasons:
 >
 >    1. The built-in destruction of automatic variables is going to
 > happen anyway
 >
 >    2. Even if you're going to re-construct the object before the
 > built-in destruction occurs, if construction can throw there will
 > still be a double-destruction.
 >
 > Attila already pointed out you have a double-destruction problem here,
 > but it is a problem regardless of whether or not there are non-POD
 > members or bases -- vector itself has a non-trivial destructor.

No need to twist the knife in the wound :o).

 > Second of all, the right way to do what you're trying to accomplish
 > without try/catch is already present in the STLPort codebase.  I know
 > because I put it there myself.  You partition the phases of
 > construction into members and bases.  In the existing implementation
 > the init_empty() part is handled by a base class, and the part that
 > does assign() is handled in the vector itself.  The base class is also
 > responsible for cleaning up any constructed elements.
 >
 > Of course, the existing implementation doesn't use assign() because
 > that would generate wasted code to destroy any existing elements.
 > Most compilers aren't smart enough to deduce that the code is dead in
 > that case because the vector's start and end pointers are both 0.

I am not sure what this means, but I take it it has to do with... ah, got
it: assign would nuke the content in case of a failure, and the base class'
destructor tries to nuke some content that doesn't exist.

So, should I take it that STLPort has a little source code duplication then
(between assign and the constructor)? :o)

 > > There are a couple of really funny aspects in this implementation of
 > > std::vector's constructor:
 > >
 > > * The constructor serves as both the one that takes a length and a
value, as
 > > well as the one that takes two iterators.
 >
 > You might want to look at 23.1.1/9.  SFINAE might be a more
 > appropriate tool for this problem.

Nonono. I switched *from* SFINAE because this is much simpler. assign itself
must use SFINAE, and I hated to duplicate it in the constructor as well --
as most other STLs seem to do.

Actually the not-yet-updated code of yasli::vector on
http://moderncppdesign.com does use SFINAE in the ctor, and I didn't like
that.

So the aspect that I liked the most was that the constructor is simple and
ignorant, in that it has no idea on what arguments it was invoked with. It
just serenely passes the buck to assign, which by necessity implements all
of the machinery needed to figure out iterators versus size/value. That's
what I wanted: to eliminate the duplicate deduction machinery, which is now
in one place (well, two if you count insert; I'm working on that). I
consider this simpler and clearer than devising a base class for vector.
Again, I think it's on the border of tautology that construction problems
should be best solved in the constructor (as I kept on saying in another
thread). Anyway, I take it from your tone that the hack above thoroughly
underwelms you :o).


Andrei



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Andrei 8/3/2004 11:18:54 AM

"David Abrahams" <dave@boost-consulting.com> wrote in message
news:8a638f47.0408020827.15a1c75@posting.google.com...
 > First of all, using an explicit destructor on an object that might be
 > allocated on the stack is almost always a route to disaster.  You can
 > remember that as a basic guideline.  There are two reasons:
 >
 >    1. The built-in destruction of automatic variables is going to
 > happen anyway
 >
 >    2. Even if you're going to re-construct the object before the
 > built-in destruction occurs, if construction can throw there will
 > still be a double-destruction.

Oh, one more thing. Was this a general guideline (with which I totally
agree) disconnected from the topic at hand, or it does apply specifically to
it? Because as far as I can tell, only Attila's explanation applies to the
buggy yasli::vector's constructor that I posted, but none of (1) or (2)
above.

Andrei



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Andrei 8/3/2004 11:20:03 AM

"E Gladyshev" <egladyshev@comcast-nosspam.net> wrote in message
news:vzvPc.201616$IQ4.92517@attbi_s02...
 > There have been so many discussions about exceptions
 > but even experienced people are still making this mistake.
 > What can you tell about an average programmer!
 > I am starting to think that C++ exception model
 > (as it is now) is making more bad than good. :)
 >
 > try/catch(...) is not equivalent to a scope guard.
 > catch(...) clause will be invoked for any exception while
 > scope guard will be invoked only if the exception is ever caught.
 > It is a big difference!
 > IMHO a scope guard is a *much* better solution than catch(...).

Yah... was it you who kept me in the air by means of punches in a newsgroup
discussion? :o)

 > As for the the code itself.
 > Calling the destructor from the constructor is not a good idea.
 > In fact, I think that calling *this* destructor explicitly
 > (like this->~vector() ) is never a good idea.
 > If you do this, you are at risk of creating a
 > conflict in the object's ownership.

Correct. Here is how yasli::vector's cdtors look as of now. I replaced
default arguments with overloading because Howard Hinnant discovered a
spurious match that I'll mention at the end of this post. Also, I factored
out the funky template into an 'init' function that's used throughout.

// inside class template vector
     vector()
     {
         init_empty();
     }

private:
     template <class InputIteratorOrN, class InputIteratorOrT>
     void init(InputIteratorOrN parm1, const InputIteratorOrT& parm2)
     {
         // Will avoid the need to use try/catch (...)
         init_empty();
         struct Guard
         {
             vector* pThis_; ~Guard() { if (pThis_) pThis_->destroy(); }
         } guard = { this };
         assign(parm1, parm2);
         guard.pThis_ = 0;
     }

public:
     explicit vector(const Allocator& a)
         : ebo_(a)
     {
         init_empty();
     }

     explicit vector(size_type sz)
     {
         init(sz, T());
     }

     explicit vector(const vector& rhs)
         : ebo_(rhs.ebo_)
     {
         init(rhs.begin(), rhs.end());
     }

     template <class InputIteratorOrN, class InputIteratorOrT>
     vector(InputIteratorOrN parm1, const InputIteratorOrT& parm2)
     {
         init(parm1, parm2);
     }

     template <class InputIteratorOrN, class InputIteratorOrT>
     vector(InputIteratorOrN parm1, const InputIteratorOrT& parm2,
         const Allocator& a) : ebo_(a)
     {
         init(parm1, parm2);
     }

private:
     void destroy();

public:
     ~vector()
     {
         destroy();
     }

I think this combo looks clean, but hey, you never know... any comments are
welcome.


Andrei

P.S. With the previous code, there was a spurious match that was found by
Howard Hinnant:

struct my_allocator : public allocator { ... };
vector<whatever> vec(my_allocator());

That wouldn't be a direct match for vector(const allocator), so the template
constructor will kick in and try to interpret my_allocator as a size_type,
which is obviously not desirable.


Andrei



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Andrei 8/3/2004 11:34:25 AM

"Tokyo Tomy" <hosoda@jtec.or.jp> wrote in message
news:49c1da0b.0408012120.32a08e20@posting.google.com...
 > At the dawn of computer age when many programmers wrote tricky codes
 > to save memory shortage, IBM instructed not to write tricky codes, but
 > to write understandable codes. I think the lesson is still true.

Hopefully it is even more true than before, given that now computers are
faster. "Write programs for people first, computers second" is a nice adage.

 > I came to understand why the vector constructor was able to accept
 > length and value after I read Andrei's long explanation. Even if the
 > code is used in the deepest part of a library, somebody must maintain
 > the code. Template could be a "goto" if you use it without control.
 >
 > Though I know Andrei is interested in exploiting the template
 > capability, I would like to know Andrei's opinion on this matter, if
 > he doesn't mind. Comments could be a help, but not enough.

Thanks for asking. I think you should've looked at how the vector
constructor looked before :o). The thing is, due to the way the standard
specifies vector's constructor, it is hard to implement it in a clear,
simple manner.

Let me give some more context. That templated constructor just intercepts
too many things when the vector is instantiated with an integral type. In
that case the iter/iter constructor hijacks the constructor that takes
size/object, unless the user passes the exact type for size and object. For
example:

vector<int> vec(5, 10); // 5 elements, each having value 10

Say the vector<T> has two ctors:

vector(size_type, T);
template <class Iter> vector(Iter, Iter);

In that case, too bad - because the template constructor intercepted your
call by instantiating Iter to int. You could make your code work by saying:
vector<int> vec(5u, 10); but heck, that's just too subtle.

So the standard specifies that any call that has an integral type (int,
char, unsigned...) should work with the expected effect. There are a number
of ways to ensure that, and none is super simple. It turns out that two
other of vector's functions (insert and assign) need to figure out the exact
same thing

Now here's why I wanted to share the solution in this thread - because it
takes the "figuring out" away from the constructor and uses whatever
"figuring out" assign() must do anyway. So there is one less problem to take
care of. In that respect, yasli's solution is simpler, not more complicated.


Andrei



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Andrei 8/3/2004 11:35:01 AM

In article <31c49f0d.0408012136.9292b1e@posting.google.com>,
usenet_cpp@lehrerfamily.com says...

 > It is platform dependent if stack based objects are unwound due to an
 > uncaught exception.  On my platform, they are not.  So, if nobody
 > catches the exception, then nobody cares about the state of any
 > objects, so the stack does not need to be unwound.
 >

Very interesting responses, thanks to everybody

I wonder whether really nobody cares about the state of objects
in case of uncaught exception...

I mean, assume that an automatic object's destructor gracefully closes
a network connection.

If I got you, in case of uncaught exceptions, on some implementations
the destructor will be executed, and the connection will be closed
gracefully.

On some others, the destructor wont be executed,  and the connection
wont be closed gracefully by the C++ program. The OS will (hopefully)
close the connection later on, but likely with some abort error.

That's definitely observable behavior, isn't it ?




      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply news 8/4/2004 10:02:36 AM

Andrei Alexandrescu (See Website for Email) wrote:
> "E Gladyshev" <egladyshev@comcast-nosspam.net> wrote in message
> news:vzvPc.201616$IQ4.92517@attbi_s02...
>  > There have been so many discussions about exceptions
>  > but even experienced people are still making this mistake.
>  > What can you tell about an average programmer!
>  > I am starting to think that C++ exception model
>  > (as it is now) is making more bad than good. :)
>  >
>  > try/catch(...) is not equivalent to a scope guard.
>  > catch(...) clause will be invoked for any exception while
>  > scope guard will be invoked only if the exception is ever caught.
>  > It is a big difference!
>  > IMHO a scope guard is a *much* better solution than catch(...).
> 
> Yah... was it you who kept me in the air by means of punches in a newsgroup
> discussion? :o)

LOL!
Yes, we (+David B. Held) had some discussion about
it after I noticed an almost indiscriminate use
of try/catch(...) in CUJ articles (by many authors).


> I replaced
> default arguments with overloading because Howard Hinnant discovered a
> spurious match that I'll mention at the end of this post. Also, I factored
> out the funky template into an 'init' function that's used throughout.
> 
> // inside class template vector
>      vector()
>      {
>          init_empty();
>      }
> 
> private:
>      template <class InputIteratorOrN, class InputIteratorOrT>
>      void init(InputIteratorOrN parm1, const InputIteratorOrT& parm2)
>      {
>          // Will avoid the need to use try/catch (...)
>          init_empty();
>          struct Guard
>          {
>              vector* pThis_; ~Guard() { if (pThis_) pThis_->destroy(); }
>          } guard = { this };
>          assign(parm1, parm2);
>          guard.pThis_ = 0;
>      }
> 
> public:
>      explicit vector(const Allocator& a)
>          : ebo_(a)
>      {
>          init_empty();
>      }
> 
>      explicit vector(size_type sz)
>      {
>          init(sz, T());
>      }
> 
>      explicit vector(const vector& rhs)
>          : ebo_(rhs.ebo_)
>      {
>          init(rhs.begin(), rhs.end());
>      }
> 
>      template <class InputIteratorOrN, class InputIteratorOrT>
>      vector(InputIteratorOrN parm1, const InputIteratorOrT& parm2)
>      {
>          init(parm1, parm2);
>      }
> 
>      template <class InputIteratorOrN, class InputIteratorOrT>
>      vector(InputIteratorOrN parm1, const InputIteratorOrT& parm2,
>          const Allocator& a) : ebo_(a)
>      {
>          init(parm1, parm2);
>      }
> 
> private:
>      void destroy();
> 
> public:
>      ~vector()
>      {
>          destroy();
>      }
> 
> I think this combo looks clean, but hey, you never know... any comments are
> welcome.
> 

Why not to use a generic 'initializer' class which would work
as the scope guard as well?
If we latter need to overload the init() function,
we won't have to write the local Guard class again.
We just add the new init to the initializer class.

// inside class template vector

      struct initializer
      {
           vector* v_;

           explicit initializer(vector* v_)
               : v_(v)
           {
           }

           template <class InputIteratorOrN, class InputIteratorOrT>
           void init( InputIteratorOrN parm1, const InputIteratorOrT& 
parm2 )
           {
               v_->assign(parm1, parm2);
               done();
           }

           ~initializer()
           {
               if( v_ ) v_->destroy();
           }
      private:
           void done() { v_ = 0; }
      };

      friend struct initializer;

      template <class InputIteratorOrN, class InputIteratorOrT>
      vector(InputIteratorOrN parm1, const InputIteratorOrT& parm2)
      {
          initializer tmp(this);
          tmp.init(parm1, parm2);
      }

Eugene

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply E 8/4/2004 12:08:46 PM

the.sim@gmail.com (Hillel Y. Sims) wrote in message news:<a4f56b4b.0408021017.e1ded52@posting.google.com>...
> "Andrei Alexandrescu \(See Website for Email\)" <SeeWebsiteForEmail@moderncppdesign.com> wrote in message news:<2n4p61Fsokm5U1@uni-berlin.de>...
> 
> > 
> > For these two reasons, I recently started preferring code a la scopeguard to
> > code that catches everything and rethrows - at least for libraries that you
> > can use with various compiler settings and in various environments. It's
> > just a tad more environment-neutral.
> > 
> 
[..]
> void Obj::func()
> {
>    // we always want to signal the condvar even if
>    // an exception occurs:
>    finally(bind(&CondVar::signal, ref(m_cv))) {
>       LockRegion(m_mx) {
>          GuardRegion(updateguard, bind(&Obj::DoUpdate, this,
> SOME_STATUS))) {
>             do_something_complicated();
>             updateguard.Dismiss();
>          }
>       }
>    }
> }

Sorry, bad example; I was just making it up on the fly.. {why would
you want to signal the condvar if you've cancelled the update?} Anyhow
that was just to give a flavor of how they are used.. Let me know if
you'd like more info, the implementation is fairly simple..

hys
--
Hillel Y. Sims
hsims AT factset.com
FactSet Research Systems

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply the 8/4/2004 12:16:53 PM

the.sim@gmail.com (Hillel Y. Sims) wrote in message news:<a4f56b4b.0408020943.430df820@posting.google.com>...
> "Robert Kindred" <RKindred@SwRI.edu> wrote in message news:<10gshd84lsm0s04@corp.supernews.com>...
> > 
> > There is a part of this thead that is not completely clear to me.  Are you
> > counting on the fact that hardware exceptions, such as memory access
> > violations and memory parity errors do _not_ inherit from std::exception?
> > Is this the way that you want to separate the two types of exceptions?
> > 
> 
> Correct.
> 
> (I assume you meant to type "reason" instead of "way" in the last sentence..)
> 
> 

Actually I have no idea why I assumed that.. It is correct either way though. :D

--
hys

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply the 8/4/2004 12:19:07 PM

"Andrei Alexandrescu (See Website for Email)"
<SeeWebsiteForEmail@moderncppdesign.com> wrote in message
>      explicit vector(const vector& rhs)

Before everybody shoots me: no explicit here. Don't shoot the pianist.

Andrei



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Andrei 8/4/2004 12:22:28 PM

E Gladyshev <egladyshev@comcast-nosspam.net> wrote in message news:<vzvPc.201616$IQ4.92517@attbi_s02>...
> John Torjo wrote:
> > news user <news@sisyphus.news.be.easynet.net> wrote in message news:<MPG.1b773efee3cc2636989792@news.easynet.be>...
> >  > In article <2n0bq4FrvsbqU1@uni-berlin.de>, "Andrei Alexandrescu \(See
> >  > Website for Email\)" <SeeWebsiteForEmail@moderncppdesign.com> says...
> >  > >      init_empty();
> >  > >      struct Guard {
> >  > >          vector* pThis_;
> >  > >          ~Guard() { if (pThis_) pThis_->~vector(); }
> >  > >      } guard = { this };
> >  > >      assign(parm1, parm2);
> >  > >      guard.pThis_ = 0;
> >  > >
> >  > >
> >  >
> >  > A small question of style, Andrei, if you dont mind...
> >  > Why are you using a scope guard in this precise case ?
> >  >
> >  > I mean, it is equivalent to write
> >  >
> >  >        init_empty();
> >  >        try
> >  >        {
> >  >          assign(parm1, parm2);
> >  >        }
> >  >        catch(...)
> >  >        {
> >  >          cleanup(); //  ~vector();
> >  >          throw;
> >  >        }
> >  >
> >  > right ?
> >  >
> > 
> > yup, I think it is (equivalent).
> > But I like the guard better - seems to me as a very cool idiom - that
> > could be used from many constructors (yet to be developed ;))
> > 
> > About forwarding to assign() -> totally rocks, Andrei!
> > 
> 
> There have been so many discussions about exceptions
> but even experienced people are still making this mistake.
> What can you tell about an average programmer!
> I am starting to think that C++ exception model
> (as it is now) is making more bad than good. :)
> 
> try/catch(...) is not equivalent to a scope guard.
> catch(...) clause will be invoked for any exception while
> scope guard will be invoked only if the exception is ever caught.
> It is a big difference!
> IMHO a scope guard is a *much* better solution than catch(...).
> 

indeed, my mistake.

> As for the the code itself.
> Calling the destructor from the constructor is not a good idea.

and yet, this seemed a good case when it's ok. Even though Andrei
refactored it.

> In fact, I think that calling *this* destructor explicitly
> (like this->~vector() ) is never a good idea.

a wise man said "never and always are two words you should always
remember never to use" ;)
But in general I do agree with you.

Best,
John


John Torjo
Freelancer
-- john@torjo.com

Contributing editor, C/C++ Users Journal
-- "Win32 GUI Generics" -- generics & GUI do mix, after all
-- http://www.torjo.com/win32gui/
   
Professional Logging Solution for FREE
-- http://www.torjo.com/code/logging.zip (logging           - C++)
-- http://www.torjo.com/logview/         (viewing/filtering - Win32)
-- http://www.torjo.com/logbreak/        (debugging         - Win32)
                                         (source code available)

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply jtorjo 8/4/2004 12:44:43 PM

"Andrei Alexandrescu \(See Website for Email\)" <SeeWebsiteForEmail@moderncppdesign.com> wrote in message news:<2n8691FtuiqhU1@uni-berlin.de>...

 > ...
 > Now here's why I wanted to share the solution in this thread - because it
 > takes the "figuring out" away from the constructor and uses whatever
 > "figuring out" assign() must do anyway. So there is one less problem to take
 > care of. In that respect, yasli's solution is simpler, not more complicated.
 >
 >
 > Andrei
 >

Andrei, thank you for your kind explanation. I came to understand what
you wanted to say.

I thought that a template argument should represent one category of
concept, so that users easily understand what kind of parameter to be
passed. In the example code, a template argument represents two
categories of concept. I overlooked the "OrN" part of
"InputIteratorOrN". My complaint could be that the "OrN" part should
have replaced for  something longer enough to be easily identified.
However This will be no more problem if the technique become popular
among programmers and template arguments often represent two or more
categories of concept.

I know I cannot write my post only to say "Andrei, thank you".

Here is my question.
Will this technique overcome Aspect Oriented Program (AOP)? What are
the stronger points and weaker points of this technique in compare
with AOP. Thank you in advance.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply hosoda 8/5/2004 10:49:23 AM

"Andrei Alexandrescu \(See Website for Email\)" <SeeWebsiteForEmail@moderncppdesign.com> wrote in message news:<2n7md3Ft5jt6U1@uni-berlin.de>...
 > "David Abrahams" <dave@boost-consulting.com> wrote in message
 > news:8a638f47.0408020827.15a1c75@posting.google.com...
 >  > "Andrei Alexandrescu \(See Website for Email\)"
 > <SeeWebsiteForEmail@moderncppdesign.com> wrote in message
 > news:<2n0bq4FrvsbqU1@uni-berlin.de>...

 >  > Attila already pointed out you have a double-destruction problem here,
 >  > but it is a problem regardless of whether or not there are non-POD
 >  > members or bases -- vector itself has a non-trivial destructor.
 >
 > No need to twist the knife in the wound :o).

Sorry, I was wrong to anyway.

 >  > Second of all, the right way to do what you're trying to accomplish
 >  > without try/catch is already present in the STLPort codebase.  I know
 >  > because I put it there myself.  You partition the phases of
 >  > construction into members and bases.  In the existing implementation
 >  > the init_empty() part is handled by a base class, and the part that
 >  > does assign() is handled in the vector itself.  The base class is also
 >  > responsible for cleaning up any constructed elements.
 >  >
 >  > Of course, the existing implementation doesn't use assign() because
 >  > that would generate wasted code to destroy any existing elements.
 >  > Most compilers aren't smart enough to deduce that the code is dead in
 >  > that case because the vector's start and end pointers are both 0.
 >
 > I am not sure what this means, but I take it it has to do with... ah, got
 > it: assign would nuke the content in case of a failure

No, this has nothing to do with failures.  assign tries to nuke the
content even in case of success, but when you're constructing the
vector there is no initial content.

 > and the base class'
 > destructor tries to nuke some content that doesn't exist.

I don't think so.

 > So, should I take it that STLPort has a little source code duplication then
 > (between assign and the constructor)? :o)

I don't know; I suppose you can read the source yourself, as I've been
encouraging you to.  Even if it does have source duplication there, it
can easily be eliminated.

 >  > You might want to look at 23.1.1/9.  SFINAE might be a more
 >  > appropriate tool for this problem.
 >
 > Nonono. I switched *from* SFINAE because this is much simpler. assign itself
 > must use SFINAE

"must" seems unlikely to me.

 > , and I hated to duplicate it in the constructor as well --
 > as most other STLs seem to do.

OK.

 > Actually the not-yet-updated code of yasli::vector on
 > http://moderncppdesign.com does use SFINAE in the ctor, and I didn't like
 > that.
 >
 > So the aspect that I liked the most was that the constructor is simple and
 > ignorant, in that it has no idea on what arguments it was invoked with. It
 > just serenely passes the buck to assign, which by necessity implements all
 > of the machinery needed to figure out iterators versus size/value. That's
 > what I wanted: to eliminate the duplicate deduction machinery, which is now
 > in one place (well, two if you count insert; I'm working on that). I
 > consider this simpler and clearer than devising a base class for vector.

Surely there are ways to factor that stuff out without incurring the
extra code generation or EH problems we saw here, though?

 > Again, I think it's on the border of tautology that construction problems
 > should be best solved in the constructor (as I kept on saying in another
 > thread). Anyway, I take it from your tone that the hack above thoroughly
 > underwelms you :o).

I didn't mean to transmit any such impression.  That said, it seems
like you're fighting against the language here in a way I've seen you
do before.  There really is a fairly well-known and clean idiom for
handling these sorts of EH issues, and it would be nice to see you
pursue that before innovating.

--
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! ]
0
Reply dave 8/5/2004 10:50:47 AM

"David Abrahams" <dave@boost-consulting.com> wrote in message
news:8a638f47.0408042040.64b18491@posting.google.com...
 >  > Nonono. I switched *from* SFINAE because this is much simpler. assign
itself
 >  > must use SFINAE
 >
 > "must" seems unlikely to me.

True; many other techniques could be used, such as simple overloading (as
traditionals STLs do). I'm just looking for one that would be easiest to
write and read.

The question remains as to whether there is some corner cases in which my
hack fails. I couldn't think of any, but that's hardly soothing :o).

 >  > Again, I think it's on the border of tautology that construction
problems
 >  > should be best solved in the constructor (as I kept on saying in
another
 >  > thread). Anyway, I take it from your tone that the hack above
thoroughly
 >  > underwelms you :o).
 >
 > I didn't mean to transmit any such impression.  That said, it seems
 > like you're fighting against the language here in a way I've seen you
 > do before.  There really is a fairly well-known and clean idiom for
 > handling these sorts of EH issues, and it would be nice to see you
 > pursue that before innovating.

If the fight is to confine a constructor-related problem inside the
constructor, fight on! Reminds me of a line from a movie:

"What are you rebelling against?"
"What have you got?"

:oD


Andrei



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Andrei 8/6/2004 10:29:21 AM

"E Gladyshev" <egladyshev@comcast-nosspam.net> wrote in message
news:PwQPc.71896$8_6.5527@attbi_s04...
> Why not to use a generic 'initializer' class which would work
> as the scope guard as well?
>
> If we latter need to overload the init() function,
> we won't have to write the local Guard class again.
> We just add the new init to the initializer class.

That's a good idea, but I prefer separating concerns. ScopeGuard (as my last
posting shows) is already factored out, has two lines, and does a very
specific clear task. Then it's true that the initializer class would do one
task but of a larger scope - initialization, so that's a valid design as
well.

(Sneak preview into an upcoming book on coding standards: "Prefer to give
each entity (class, function, module, library) one well-defined
responsibility. As an entity grows its scope of responsibility naturally
increases, but its responsibility should not diverge. [...] When designing
larger entities, prefer to build higher-level abstractions from smaller
lower-level abstractions. Avoid collecting several low-level abstractions
into a larger low-level conglomerate.")

One could argue that my merging the constructors into one that works against
separating concerns because one ctor does two things (take iter/iter versus
take number/object). But the thing is, their bodies are identical because
they actually deal with one thing that's orthogonal with both: exception
safety.


Andrei



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Andrei 8/7/2004 3:32:07 AM

"Tokyo Tomy" <hosoda@jtec.or.jp> wrote in message
news:49c1da0b.0408041900.16efd5a3@posting.google.com...
 > Here is my question.
 > Will this technique overcome Aspect Oriented Program (AOP)? What are
 > the stronger points and weaker points of this technique in compare
 > with AOP. Thank you in advance.

Not sure about what you refer to as "this technique"... is it templates,
metaprogramming...? In that case, there is some overlap with AOP but also a
lot of area in which one's better than the other. I touched the issue in an
OOPSLA talk in 2002. Would be interesting if a chat on the relative merits
of the technology would ensue.

Andrei



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Andrei 8/7/2004 9:21:09 AM

Andrei Alexandrescu (See Website for Email) wrote:
 > "E Gladyshev" <egladyshev@comcast-nosspam.net> wrote in message
 > news:PwQPc.71896$8_6.5527@attbi_s04...
 >
 >>Why not to use a generic 'initializer' class which would work
 >>as the scope guard as well?
 >>
 >>If we latter need to overload the init() function,
 >>we won't have to write the local Guard class again.
 >>We just add the new init to the initializer class.
 >
 >
 > That's a good idea, but I prefer separating concerns. ScopeGuard (as my last
 > posting shows) is already factored out, has two lines, and does a very
 > specific clear task. Then it's true that the initializer class would do one
 > task but of a larger scope - initialization, so that's a valid design as
 > well.

Exactly, I could argue that the initializer class is not separating
concepts. It is *eliminating* the ScopeGuard concept.
It is taking care of a larger scope - initialization and
the exception safety is a free bonus of this design.

 > (Sneak preview into an upcoming book on coding standards: "Prefer to give
 > each entity (class, function, module, library) one well-defined
 > responsibility. As an entity grows its scope of responsibility naturally
 > increases, but its responsibility should not diverge. [...] When designing
 > larger entities, prefer to build higher-level abstractions from smaller
 > lower-level abstractions. Avoid collecting several low-level abstractions
 > into a larger low-level conglomerate.")

I'd like to read the whole book but
I am pretty skeptical about guidelines
like these ones.
I think that they are very subjective
and everybody will interpret and apply them
differently. Here is my *main* point.
Typically such guidelines don't provide
any way to measure which of several designs
is more correct. So in practice they are
of limited use. If you could come with
a figure of merit for different designs
against your guidelines, they would be much
more interesting. It is a challenge!

Let me play devil's advocate here.
What does "Avoid collecting several
low-level abstractions into a larger low-level
conglomerate" really mean?

What does "low-level conglomerate" mean exactly?
Is it ok to collect several
*high-level* abstractions into a larger
*high-level* conglomerate?
How do you decide which level is low and
which is high?

What does "its responsibility should not
diverge" mean?
It is increasing but not diverging?
How do we measure increasing vs. diverging?

I could go on and on... :)

 > One could argue that my merging the constructors into one that works against
 > separating concerns because one ctor does two things (take iter/iter versus
 > take number/object). But the thing is, their bodies are identical because
 > they actually deal with one thing that's orthogonal with both: exception
 > safety.
 >

I would not argue. Your constructor is
in line with my common sense.
Those separation concerns recommendations
only go as far as our common sense let
them to go.
I think a smart person will always find
some reasonable justification for his design
decisions...like you just did with:
"...they actually deal with one thing
that's orthogonal with both: exception safety"

Eugene

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply E 8/7/2004 9:34:31 AM

"E Gladyshev" <egladyshev@comcast-nosspam.net> wrote in message
news:7Z_Qc.260435$XM6.87701@attbi_s53...
> I'd like to read the whole book but
> I am pretty skeptical about guidelines
> like these ones.
> I think that they are very subjective
> and everybody will interpret and apply them
> differently. Here is my *main* point.
> Typically such guidelines don't provide
> any way to measure which of several designs
> is more correct. So in practice they are
> of limited use. If you could come with
> a figure of merit for different designs
> against your guidelines, they would be much
> more interesting. It is a challenge!

Software engineering has tried time and again with ways to measure quality,
and not much has been achieved beyond "C-style semicolons (statement
termination) are better than Pascal-style semicolon (statement separation)".

Then, of course, an incompetent armed with a coding standard does not
automatically become competent.

> Let me play devil's advocate here.
> What does "Avoid collecting several
> low-level abstractions into a larger low-level
> conglomerate" really mean?

See below.

> What does "low-level conglomerate" mean exactly?

A collection of pieces dependent of each other that can only be deployed as
a unit.

> Is it ok to collect several
> *high-level* abstractions into a larger
> *high-level* conglomerate?
> How do you decide which level is low and
> which is high?

Well, the essential aspect is the operation of making interdependent pieces
that could remain dependent.

> What does "its responsibility should not
> diverge" mean?
> It is increasing but not diverging?
> How do we measure increasing vs. diverging?

Divergence means, a module takes several unrelated responsibilities, as
opposed to one responsibility of larger scope. As of measuring... no comment
:o).

> I could go on and on... :)

I totally understand the "devil's advocate" point. Of course, it's hard to
talk when each term is not precisely defined; some common sense is needed.
The whole point was that you shouldn't have a unit take on several
independent responsibilities, but instead a responsibility of increasing
scope.

>  > One could argue that my merging the constructors into one that works
against
>  > separating concerns because one ctor does two things (take iter/iter
versus
>  > take number/object). But the thing is, their bodies are identical
because
>  > they actually deal with one thing that's orthogonal with both:
exception
>  > safety.
>
> I would not argue. Your constructor is
> in line with my common sense.
> Those separation concerns recommendations
> only go as far as our common sense let
> them to go.
> I think a smart person will always find
> some reasonable justification for his design
> decisions...like you just did with:
> "...they actually deal with one thing
> that's orthogonal with both: exception safety"

Oh, that's easy: the code is identical. That's the best proof.


Andrei



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Andrei 8/8/2004 2:14:37 AM

"Andrei Alexandrescu \(See Website for Email\)" <SeeWebsiteForEmail@moderncppdesign.com> writes:

 > Dave writes:
 >  > it seems
 >  > like you're fighting against the language here in a way I've seen you
 >  > do before.  There really is a fairly well-known and clean idiom for
 >  > handling these sorts of EH issues, and it would be nice to see you
 >  > pursue that before innovating.
 >
 > If the fight is to confine a constructor-related problem inside the
 > constructor, fight on!

That makes for a good slogan, but it seems like an oversimplification
to me. There's nothing particularly admirable about keeping all the
code executed by a function in one place -- refactoring often produces
multiple functions/classes with fine-grained responsibilities -- and
your approach is no different in that respect.  It "confines the
problem" inside the constructor, a nested class, and a destructor.

-- 
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! ]
0
Reply David 8/8/2004 10:03:54 AM

Andrei Alexandrescu (See Website for Email) wrote:
 > Software engineering has tried time and again with ways to measure quality,
 > and not much has been achieved beyond "C-style semicolons (statement
 > termination) are better than Pascal-style semicolon (statement separation)".
 >

:). This is why programming is sill an art in a way.

[...]
 > I totally understand the "devil's advocate" point. Of course, it's hard to
 > talk when each term is not precisely defined; some common sense is needed.

I think that we agree that it is good to have
some guidelines but we should be careful
not to get carried away trying to accommodate them.
The ultimate judgment is still up to your talent
and common sense.

Eugene

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply E 8/8/2004 10:09:19 AM

In general, the Visual C++ compiler can cause problems with
scope_guards in certain cases.  However, these cases are sufficiently
unlikely that I still use scope_guards very often in all of my code
and have been teaching the developers at my company to do the same.

Visual C++ differentiates between synchronous and asynchronous
exceptions.  With asynchronous exceptions, the destructors of stack
variables are guaranteed to be called, no matter which line of code
causes an exception to be thrown (including all platform exceptions).
With synchronous exceptions, however, the compiler makes some
assumptions about what lines of code can cause an exception to be
thrown, which allows the compiler to do some extra optimizations.
Starting with Visual C++ 6.0, the default exception handling type is
synchronous.

In the case of this constructor, if your call to assign() gets inlined
by the compiler and it is sufficiently simple (doesn't call any other
methods or explicitly call throw), the destructor of the scope_guard
would not get called.  Even more frustrating is that this would only
happen with release builds, as the debug build would not inline
assign() and, therefore, would properly call the scope_guard
destructor.

While this sounds pretty bad, it isn't too bad once you realize the
cases that wouldn't get caught.  First of all, it would have to be a
platform exception, such as invalid memory access.  C++ exceptions
will never show this behavior.  Secondly, it will only occur if the
code between the creation of the scope_guard and the end of the scope
does not contain any (non-inlined) method calls or anything else that
could possibly throw a C++ exception.

Because of these limits, I still use scope_guards very often.  Let's
face it, if your code is written properly, you shouldn't be causing
access violations, anyway.  Besides, if you really want to get around
it, you can just turn asynchronous exceptions back on.  The
performance isn't really noticeably worse in my experience.

John Sheehan
Softricity, Inc.


"Andrei Alexandrescu \(See Website for Email\)" <SeeWebsiteForEmail@moderncppdesign.com> wrote in message news:<2nf9mpFa8ctU1@uni-berlin.de>...
 > "David Abrahams" <dave@boost-consulting.com> wrote in message
 > news:8a638f47.0408042040.64b18491@posting.google.com...
 >  >  > Nonono. I switched *from* SFINAE because this is much simpler. assign
 >  itself
 >  >  > must use SFINAE
 >  >
 >  > "must" seems unlikely to me.
 >
 > True; many other techniques could be used, such as simple overloading (as
 > traditionals STLs do). I'm just looking for one that would be easiest to
 > write and read.
 >
 > The question remains as to whether there is some corner cases in which my
 > hack fails. I couldn't think of any, but that's hardly soothing :o).
 >
 >  >  > Again, I think it's on the border of tautology that construction
 >  problems
 >  >  > should be best solved in the constructor (as I kept on saying in
 >  another
 >  >  > thread). Anyway, I take it from your tone that the hack above
 >  thoroughly
 >  >  > underwelms you :o).
 >  >
 >  > I didn't mean to transmit any such impression.  That said, it seems
 >  > like you're fighting against the language here in a way I've seen you
 >  > do before.  There really is a fairly well-known and clean idiom for
 >  > handling these sorts of EH issues, and it would be nice to see you
 >  > pursue that before innovating.
 >
 > If the fight is to confine a constructor-related problem inside the
 > constructor, fight on! Reminds me of a line from a movie:
 >
 > "What are you rebelling against?"
 > "What have you got?"


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply jsheehan 8/9/2004 9:31:29 AM

"Andrei Alexandrescu \(See Website for Email\)" <SeeWebsiteForEmail@moderncppdesign.com> wrote in message news:<2ni8daF17shfU1@uni-berlin.de>...
 > "Tokyo Tomy" <hosoda@jtec.or.jp> wrote in message
 > news:49c1da0b.0408041900.16efd5a3@posting.google.com...
 >  > Here is my question.
 >  > Will this technique overcome Aspect Oriented Program (AOP)? What are
 >  > the stronger points and weaker points of this technique in compare
 >  > with AOP. Thank you in advance.
 >
 > Not sure about what you refer to as "this technique"... is it templates,
 > metaprogramming...? In that case, there is some overlap with AOP but also a
 > lot of area in which one's better than the other. I touched the issue in an
 > OOPSLA talk in 2002. Would be interesting if a chat on the relative merits
 > of the technology would ensue.
 >
 > Andrei

Andrei, thank you for your reply.  I am sorry to say that I don't know
much about AOP. But After reading "Aspect-Oriented Programming & C" by
Christopher Diggisns at Dr. Dobb's Journal August 2004, I came to
think the "guard" might be one of crosscutting concerns which would be
scattered in many places of your codes.

If "guard" is used only at one place, you cannot call it a
crosscutting concern any more and you don' t need to apply AOP
paradigm.

Andrei wrote in his first post:
 > All of the STL implementation I've seen have some (likely a LOT of)
 > duplication between the constructor code and the assign() code.
 > This has none.

If you can limit "guard" at one place, using the technique, what shall
I call?, "occult" technique or Yaslander technology, and you don't
need AOP any more.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply hosoda 8/9/2004 9:32:11 AM

In article <570fbf87.0408081802.42349906@posting.google.com>, 
jsheehan@gmail.com says...
> In the case of this constructor, if your call to assign() gets inlined
> by the compiler and it is sufficiently simple (doesn't call any other
> methods or explicitly call throw), the destructor of the scope_guard
> would not get called.  

> While this sounds pretty bad, it isn't too bad once you realize the
> cases that wouldn't get caught.  First of all, it would have to be a
> platform exception, such as invalid memory access.  

Well, I'm lost again  :-)

People over here do they or do they not want to have the destructors
of automatic objects executed when a platform exception is thrown ?
 

 

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply news 8/9/2004 8:26:21 PM

"John Sheehan" <jsheehan@gmail.com> wrote in message
news:570fbf87.0408081802.42349906@posting.google.com...
> In general, the Visual C++ compiler can cause problems with
> scope_guards in certain cases.  However, these cases are sufficiently
> unlikely that I still use scope_guards very often in all of my code
> and have been teaching the developers at my company to do the same.
[snip]

If I understand your explanation correctly, it deals only with cases in
which behavior is undefined anyway, is that correct?

Case in which it's not too bad - "undefined" leaves quite some leeway to
implementations, including not calling some destructor :o).


Andrei



      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply Andrei 8/9/2004 11:41:37 PM

 >
 > If I understand your explanation correctly, it deals only with cases in
 > which behavior is undefined anyway, is that correct?
 >
 > Case in which it's not too bad - "undefined" leaves quite some leeway to
 > implementations, including not calling some destructor :o).
 >
 > [snip]

Yeah, I don't think it is that bad.  However, it may surprise some
people to know that even a divide by zero error can cause this
particular problem.  Go figure.

John


"Andrei Alexandrescu \(See Website for Email\)" <SeeWebsiteForEmail@moderncppdesign.com> wrote in message news:<2nqbbrF3js6qU1@uni-berlin.de>...
 > "John Sheehan" <jsheehan@gmail.com> wrote in message
 > news:570fbf87.0408081802.42349906@posting.google.com...
 > > In general, the Visual C++ compiler can cause problems with
 > > scope_guards in certain cases.  However, these cases are sufficiently
 > > unlikely that I still use scope_guards very often in all of my code
 > > and have been teaching the developers at my company to do the same.
 > [snip]
 >
 > If I understand your explanation correctly, it deals only with cases in
 > which behavior is undefined anyway, is that correct?
 >
 > Case in which it's not too bad - "undefined" leaves quite some leeway to
 > implementations, including not calling some destructor :o).

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply jsheehan 8/12/2004 12:12:36 PM

In article <570fbf87.0408111826.647a7b11@posting.google.com>,
jsheehan@gmail.com says...
>>
>> If I understand your explanation correctly, it deals only with cases 
>> in
>> which behavior is undefined anyway, is that correct?
>>
>> Case in which it's not too bad - "undefined" leaves quite some leeway 
>> to
>> implementations, including not calling some destructor :o).
>>
>> [snip]
>
> Yeah, I don't think it is that bad.  However, it may surprise some
> people to know that even a divide by zero error can cause this
> particular problem.  Go figure.
>

However, from a previous question on why we would want to prefer
a scope guard to catch (...), I seem to remember that one of the
answers was that the former does not catch platform exceptions,
while the later does...

It didnt look stupid, to me. And it does not look stupid to try to have
some well-defined (perhaps platform dependent) behavior in case of
divide by zero error.

I really would like to know how people here handle the problem in
real life.

Do they ignore the problem and let the program do anything it wants
when a divide by zero exception is thrown ?

Do they expect destructors to be executed ?

Not executed ?

Thanks


      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply news 8/13/2004 1:18:56 PM

usenet_cpp@lehrerfamily.com (Joshua Lehrer) wrote
> Because catch(...) catches *all* exceptions, calls cleanup, then
> rethrows the exception.  It will even catch exceptions that nobody
> knows how to cleanup.  Thus, you may not be able to call cleanup().

You don't have to know how to clean up the exception itself... you
just need to know how to clean up anything your code has going. If
you allocated memory, this may be your only chance to free it. If you
opened a file, close it. In short, exactly the same reasons that you
would use RAII.

The try/catch(...) might be harder to write and more error-prone,
but the semantics are well-defined.

> It is platform dependent if stack based objects are unwound due to an
> uncaught exception.  On my platform, they are not.  So, if nobody
> catches the exception, then nobody cares about the state of any
> objects, so the stack does not need to be unwound.

Unless, of course, you've allocated resources that won't be returned
without one of these techniques.

> Another reason, which is again platform dependent, is manipulation of
> the stack and program counters.  On my platform, catch (...) and a
> re-throw causes the traceback information to remember the location
> from which the exception was re-thrown, NOT the original location.
> So, when trying to investigate a crash due to a thrown exception, the
> only information we have is this re-throw spot.

This might be the way it works on your platform, but it is clearly not
standard.

15.1.6:
   A throw-expression with no operand rethrows the exception being handled.
   The exception is reactivated with the *existing temporary*; no new
   temporary exception is created. The exception is no longer considered
   to be caught; therefore, the value of uncaught_exception() will again
   be true. [Example elided] [Emphasis added]

So throw; does not throw an exception identical to the one caught; it throws
the *very same* exception, which should no be re-initialized.

> Finally, catch(...)/throw is simply evil.  See this thread for why:
> http://tinyurl.com/5sdp9

Okay, so use catch(std::exception&) instead. Or RAII (which is still superior).

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply allan_w 8/18/2004 6:43:47 PM

hosoda@jtec.or.jp (Tokyo Tomy) wrote
 > At the dawn of computer age when many programmers wrote tricky codes
 > to save memory shortage, IBM instructed not to write tricky codes, but
 > to write understandable codes. I think the lesson is still true.

The story (it may be apocryphal) is that this lesson was not learned for
free. Some programmer in IBM made a change to the startup code for one
of their operating systems which saved a few bytes of code. Unfortunately
the revised code would not accept February 29 as a valid date, but
routine testing did not catch this problem. On February 29, 1964, some
of their clients had trouble starting up their computer – but since this
was a Saturday, nobody took the trouble call until the following Monday,
and by then the problem was gone. But February 29, 1968 was a Thursday,
so this time when the same problem re-appeared the support lines were
swamped.

I have no idea if this story is true, or if I even got the dates right
(perhaps it was actually sixteen years earlier, on Sunday 29-Feb-1948
and Friday 29-Feb-1952). Does anybody know? Did anybody else even hear
this story before?

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply allan_w 8/19/2004 12:15:24 PM

usenet_cpp@lehrerfamily.com (Joshua Lehrer) writes:

> Finally, catch(...)/throw is simply evil.  See this thread for why:
> http://tinyurl.com/5sdp9

No.  catch(...)/throw has been made evil *on some platforms* by insane
OS/compiler implementation choices.  There's nothing inherently evil
about it from a pure standard C++ POV.  Hopefully OS and compiler
vendors will start to fix those mistakes and make catch(...)/throw
viable again.

-- 
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! ]
0
Reply David 8/20/2004 1:25:56 AM

allan_w@my-dejanews.com (Allan W) wrote in message news:<7f2735a5.0408181317.8c9f0cd@posting.google.com>...
 > hosoda@jtec.or.jp (Tokyo Tomy) wrote
 >  > At the dawn of computer age when many programmers wrote tricky codes
 >  > to save memory shortage, IBM instructed not to write tricky codes, but
 >  > to write understandable codes. I think the lesson is still true.
 >
 > ...
 > I have no idea if this story is true, or if I even got the dates right
 > (perhaps it was actually sixteen years earlier, on Sunday 29-Feb-1948
 > and Friday 29-Feb-1952). Does anybody know? Did anybody else even hear
 > this story before?
 >
 > ...

The story, I remember, appeared on a Japanese newspaper, with a
picture of a woman. The instruction was made at a IBM training school
for programmers. The wording in my post is not accurate, because the
story was written in Japanese and based on my memory. I remember the
story because I was impressed by the story, but my memory is not
enough to recall the date of the article and the date of the
instruction.

      [ See http://www.gotw.ca/resources/clcm.htm for info about ]
      [ comp.lang.c++.moderated.    First time posters: Do this! ]
0
Reply hosoda 8/20/2004 10:07:36 AM

46 Replies
89 Views

(page loaded in 0.507 seconds)


Reply: