The most recent arcticle CUJ,Feb.2004 is a continuing
discussion on smart_ptr.
I think that the article is missing some clarification
about replacing try/catch with RAII.
I think that at least on Windows, the proposed technique
will work only if the unhandled exception filter
triggers the stack unwinding. However this is not required.
So if you are using a custom unhandled exception filter, be careful!
Andrei's description of this try/catch vs. RAII is that:
--- start of quote (private e-mail) ---
"The following two fragments of code are equivalent:
// 1
try {
statements1
} catch (...) {
statements2
throw;
}
statements2
// 2
struct RAII {
~RAII() {
statements2
}
};
RAII obj;
statements1
Because the two sections of code have identical effect...
--- end of quote ---
In Windows, if the default stack unwinding for *unhandled* exceptions is
disabled, from the exception handling standpoint,
(1) and (2) are obviously not identical.
For example, the following two sections have different effects.
LONG WINAPI er_exception_filter( EXCEPTION_POINTERS *info )
{
exit(0); //don't trigger stack unwinding
}
(1)
main()
{
SetUnhandledExceptionFilter( er_exception_filter );
try {
throw 1;
} catch (...) {
statements2
throw;
}
}
*** statements2 is always called.
(2)
struct RAII {
~RAII() {
statements2
}
};
main()
{
SetUnhandledExceptionFilter( er_exception_filter );
RAII obj;
throw 1;
}
*** statements2 is never called.
In this case you cannot replace try/catch with RAII.
The question is why would someone want to disable
the stack unwinding for unandled exceptions?
For quite sometime, I've been concerned about
frequent catch(...) appearances in CUJ articles.
I finally decided to send an e-mail to Andrei
and David. They were kind enough to follow
up on this issue. Thanks, guys!
I'll be posting another message shortly
about the safety of catch(...) that is
related to the question above.
Eugene
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
I've been very dissapointed in the overall code quality inside of CUJ
for the past year. The frequently have articles that preach exception
safety and other programming techniques, then, in the same, or a
subsequent issue, they ignore those suggestions.
The way to teach people is not only to show them the right way, but to
practice what you preach so that we see it over and over again.
For example, I was very dissapointed in the code that Shehrzad Qureshi
supplied for his article "Policy-Driven Design & The Intel IPP
Library" in the January, 2004 issue. Here is a snippet:
void resize(int N) {
if (m_pSamples) IPP::free(m_pSamples);
m_nSamples=N;
m_pSamples=IPP::malloc(N);
}
we all know that this is backwards, especially because IPP is a policy
class and may throw any number of exceptions. The author should
practice what is preached in the other articles in the magazine and
write execption safe code. (There are two ways to do it here, one is
to have a very simple custom smart pointer class, thus also removing
the requirement for a destructor bodyl another is to simply call
malloc first, and if that succeeds, store the new sample size then
call free.)
In addition, the code is not const-correct (e.g. "int getNumSamples()
{ return m_nSamples; }"). I could go on about this exact article, but
you get my point.
They have had some nice articles/ideas about locking regions of code:
Lock(mutex) {
}
and yet we still see their code riddled with:
mutex.lock();
operation();
mutex.unlock();
The techniques they have presented are very powerful, but people won't
use them if they are presented and dropped.
There needs to be some minimum standard to which all articles/code
fragments must adhere. I would easily add to this standard
const-correctness and exception safety (unless, of course, either
would overly obfuscate the code and take away from the thrust of the
article, neither of which applies in my above examples.)
joshua lehrer
factset research systems
NYSE:FDS
Unfortunately, CUJ isn't a peer-reviewed journal, so unless you're
volunteering to review articles...
Dave
---
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.561 / Virus Database: 353 - Release Date: 1/13/2004
>
> blabla...
>
> The most recent arcticle CUJ,Feb.2004 is a continuing
> discussion on smart_ptr.
>
> I think that the article is missing some clarification
> about replacing try/catch with RAII.
>
> I think that at least on Windows, the proposed technique
> will work only if the unhandled exception filter
> triggers the stack unwinding. However this is not required.
Well if the exception handler does not trigger stack unwinding,
you are out of C++ realms, so CUJ does not have to cover that
>
> So if you are using a custom unhandled exception filter, be careful!
Well if someone use a custom unhandled exception filter, I hope he is
knowledgeable
about how to do it.
>
> Andrei's description of this try/catch vs. RAII is that:
>
> more stuff
>
> In Windows, if the default stack unwinding for *unhandled* exceptions is
> disabled, from the exception handling standpoint,
> (1) and (2) are obviously not identical.
But then you are not in Kansas anymore !
>
> For example, the following two sections have different effects.
>
> LONG WINAPI er_exception_filter( EXCEPTION_POINTERS *info )
> {
> exit(0); //don't trigger stack unwinding
then thowing an exception is equivalent to "exit"
so in your code you could replace all your throw by exit, it would be
equivalent.
> }
>
> (1)
> main()
> {
> SetUnhandledExceptionFilter( er_exception_filter );
>
> try {
> throw 1;
> } catch (...) {
> statements2
> throw;
> }
> }
> *** statements2 is always called.
Are you sure ???
without verifying, it seems that the filter will exit.
unless the catch at the same level prevent the filter from being called.
still you are deep in implementation defined land
>
> (2)
>
> struct RAII {
> ~RAII() {
> statements2
> }
> };
>
> main()
> {
> SetUnhandledExceptionFilter( er_exception_filter );
>
> RAII obj;
>
> throw 1;
> }
> *** statements2 is never called.
What a surprise!!! (mild sarcasm)
>
> In this case you cannot replace try/catch with RAII.
>
> The question is why would someone want to disable
> the stack unwinding for unandled exceptions?
Yeah, why ?
especially if the change breaks valid code.
The best exception filter I've seen was from boost:
LONG WINAPI er_exception_filter( EXCEPTION_POINTERS *info )
{
throw; // operate like real C++ now
}
>
>more stuff deleted...
>
> I'll be posting another message shortly
> about the safety of catch(...) that is
> related to the question above.
>
keep us inform, please.
exception handling is still misterious for many programmers, and
implementation
seems to differ greatly across compiler.
A good thread about exception dos and dont's would be great.
15.5.1/2
Note: in the situation where
no matching handler is found, it is implementation-defined whether or
not the stack is unwound before terminate() is called. In all other
situations, the stack shall not be unwound before terminate() is
called.
I think that it is very important to realize that replacing
try/catch() with RAII is not a portable idiom.
Eugene
It's completely portable w.r.t. C++. What is not portable is the
implementation-specific behavior of unhandled exceptions on
which you depend.
Dave
---
Outgoing mail is certified Virus Free.
Checked by AVG anti-virus system (http://www.grisoft.com).
Version: 6.0.561 / Virus Database: 353 - Release Date: 1/13/2004
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
Furthermore, decorating the code with all of the received wisdom of
the community is likely to obscure whatever point an article is trying
to make.
Maybe a reasonable medium is that authors make their point briefly
print, and then post fully-rendered examples on www.cuj.com, where
peer-review can occur.
Dangerous novices like me benefit greatly from the gurus. ;)
Could you elaborate on this? I don't see how this can be done, well
not without a MACRO anyway.
Here's what I came up with (scoped_lock does what you'd expect).
class Locker {
scoped_lock lock;
public:
Locker(mutex m) : lock(m) { }
operator bool() const { return true; }
}
// For some reason CC doesn't like Locker lock(M) it wants
// an assignment although they're equivalent (AFAIK)
#define Lock(M) \
if(Locker locker_obj_I_hope_this_name_is_not_used_in_the_block = M)
> > For quite sometime, I've been concerned about
> > frequent catch(...) appearances in CUJ articles.
> I've been very dissapointed in the overall code quality inside of CUJ
> for the past year.
I find that the quality is very variable, and there is, from time to
time, an article which is really bad. On the other hand, the quality of
code from their regular contributors is exceptional.
[...]
> They have had some nice articles/ideas about locking regions of code:
> Lock(mutex) {
> }
> and yet we still see their code riddled with:
> mutex.lock();
> operation();
> mutex.unlock();
IMHO, you've hit here on a delicate point. If the article isn't
concerned with threading, then why bother with mutex at all. And if it
is... often, the exact moment locking and unlocking occurs is likely to
be of the utmost importance, and using specific operations to show this
moment is good pedogogy, even if it isn't the way to go in production
code.
> The techniques they have presented are very powerful, but people won't
> use them if they are presented and dropped.
> There needs to be some minimum standard to which all articles/code
> fragments must adhere. I would easily add to this standard
> const-correctness and exception safety (unless, of course, either
> would overly obfuscate the code and take away from the thrust of the
> article, neither of which applies in my above examples.)
I agree that a minimum standard is needed. I certainly think that const
correctness must be part of it. The exception safety issue is somewhat
more delicate, however, since exception safety is much more a question
of contract (strong guarantee, etc.) than anything else. Blatently wrong
code (e.g. where an exception is a definite possiblity, and it would
result in a double delete) should be rejected, but it is not always easy
to recognize.
--
James Kanze GABI Software mailto:ka...@gabi-soft.fr
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16
> 15.5.1/2
> Note: in the situation where
> no matching handler is found, it is implementation-defined whether or
> not the stack is unwound before terminate() is called. In all other
> situations, the stack shall not be unwound before terminate() is
> called.
> I think that it is very important to realize that replacing
> try/catch() with RAII is not a portable idiom.
I think that there is a basic premise when using exceptions that all
exceptions will be caught. Somewhere. I don't think I've written a
single program for a modern compiler in which main didn't consist of a
single try/catch block.
Note that neither RAII nor try/catch can totally ensure that all
resources (including such things as temporary files) are freed. There
are any number of things which can cause the program to terminate
without stack walkback: on my systme: abort or an assertion failure, a
segment violation, a kill -9 on the process... Abort and assertion
failure are generally portable, and on must systems, dereferencing a
null pointer is another good way. Pulling the plug on the computer will
often do the trick as well.
I think that when talking about C++ techniques, there is a general
assumption that the surrounding environment (both inside and outside the
program) is reasonably well behaved. I would consider that this is NOT
the case for a program which allowed an uncaught exception to terminate
the program.
--
James Kanze GABI Software mailto:ka...@gabi-soft.fr
Conseils en informatique orientée objet/ http://www.gabi-soft.fr
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France, +33 (0)1 30 23 45 16
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
Maybe because of lack of reference in constructor? Ie. something like
this:
Locker( mutex &m ) : lock( m ) { };
could be better? I'm not sure which mutexes are you using, however :)
Myself I've written simple Sentry classes:
class MutexSentry
{
private:
Mutex &m;
public:
MutexSentry( Mutex &m )
{
m.Lock();
}
~MutexSentry()
{
m.UnLock();
}
};
works nice for that:
void foo()
{
//...
{
MutexSentry m( mutex );
//...
}
//...
}
Btw. I've found that ocassionally MutexUnSentry is also convenient.
UnSentry is similar to Sentry, but it have Lock() and UnLock() reversed.
--
Best regards from
Kamil Burzynski
They're not equivalent:
Locker lock = M;
is basically equivalent to
Locker lock(Locker(M));
and is not valid because Locker is not copyable.
Your original
Locker lock(M);
"ought to" work but it isn't an acceptable form of declaration in a
conditional or loop statement.
This appears to work, though it's not great:
class Locker
{
public:
Locker(mutex & m) : l_(m), m_(m) {}
Locker(const Locker & l)
: l_(l.m_, false), m_(l.m_)
{
l.l_.unlock();
l_.lock();
}
operator const void *() const { return this; }
private:
mutable scoped_lock l_;
mutex & m_;
};
#define Lock(m) if (Locker Lock = (m))
Hopefully the compiler eliminates the copy, since it is very
inefficient to lock, unlock and then lock again! However this
should work correctly whether or not the copy is done.
Note use of "Lock" as the variable name - this should be safe
since "Lock" is otherwise reserved as the name of the macro.
>>They have had some nice articles/ideas about locking regions of code:
>>
>>Lock(mutex) {
>>}
> Could you elaborate on this? I don't see how this can be done, well
> not without a MACRO anyway.
Not exactly with the syntax above, but the idea is to have a class
that is constructed from the mutex where the mutex is locked in the
constructor and released in the destructor. This way the mutex is
even exception safe. In a second step, derive classes you want to
synchronize on from a base class that just carries the mutex. This
way you can mimic the Java/C# 'synchronized' regions.
class Lock {
//
Mutex &holder;
..
//
public:
Lock(Mutex &mutex)
: holder(mutex)
{
mutex.lock();
}
~Lock()
{
holder.unlock();
}
};
Then:
{
Lock l(mutex);
...
}
synchronizes the region on the mutex.
Greetings,
Thomas
Ben Hutchings schrieb:
>
> They're not equivalent:
> Locker lock = M;
> is basically equivalent to
> Locker lock(Locker(M));
No, they are definitely not equivalent.
The first constructs a lock, while the second declares a function.
regards,
Thomas
No, the standard doesn't require that.
What premise are you talking about?
> Somewhere. I don't think I've written a
> single program for a modern compiler in which main didn't consist of a
> single try/catch block.
I guess I would like to repeat this again.
Unfortunately try/catch block in main() is not always enough.
If you program or any of the third party libraries create
threads, you have to put try/catch in each of these threads.
Unfortunately it is not always possible.
Eugene
>Myself I've written simple Sentry classes:
Snip
>works nice for that:
>
>void foo()
>{
> {
> MutexSentry m( mutex );
> }
>}
Yeah, I've used this kind of class often, the thing I was trying to
reproduce was the syntax in Mr. Lehrer's post:
void foo()
{
Lock(mutex) {
// locked code here.
}
}
*That's* what caught my eye. Not only for locking code but for
profiling it and countless other uses.
BTW, I later thought that changing operator bool to return false and
changing the macro to:
#defile Lock(M) if(Locker Lock = M) {} else
(thanks Ben)
would be better since it prevents appending an else to the locked
block.
In all honestly writing "Lock(mutex) { ... }" is no less work than
writing "{ Lock l(mutex); ... }" (well you save a variable's name and
a semicolon) but the first syntax is much more esthetics in my eyes.
Then again introducing a macro is probably reason enough to avoid this
which is why I wanted to know if there's another way to do it.
I always thought that T t = x; was equivalent to T t(x); but 8.5.5
backs Ben up (thanks for teaching me something new).
>No, they are definitely not equivalent.
>
>The first constructs a lock, while the second declares a function.
So is there a way to declare such a variable in an if condition?
My original attempt was this
struct Scoper {
Scoper() { cout << "Start scope\n"; }
~Scoper() { cout << "End scope\n"; }
operator bool() const { return true; }
};
int main()
{
if (Scoper Scope) {
cout << "In scope\n";
}
}
But this wouldn't compile (on CC and g++)
Q) Why is this? (Unexpected type name "Scoper" encountered)
I tried "if (typename Scoper scope)", didn't work either.
Q) Do I have a fundamental misunderstanding of the keyword typename?
So I changed Scoper to accept an int then "if (Scoper Scope(42))"
wouldn't compile but "if (Scoper Scope = 42)" would.
Which brings me back to me first question, how can I declare an object
with zero, one or more arguments to ctor in an if condition?
|> <ka...@gabi-soft.fr> wrote in message
|> news:d6652001.04012...@posting.google.com...
|> [...]
|> > > I think that it is very important to realize that replacing
|> > > try/catch() with RAII is not a portable idiom.
|> > I think that there is a basic premise when using exceptions that
|> > all exceptions will be caught.
|> No, the standard doesn't require that. What premise are you talking
|> about?
|> > Somewhere. I don't think I've written a single program for a
|> > modern compiler in which main didn't consist of a single try/catch
|> > block.
|> I guess I would like to repeat this again.
|> Unfortunately try/catch block in main() is not always enough.
|> If you program or any of the third party libraries create
|> threads, you have to put try/catch in each of these threads.
|> Unfortunately it is not always possible.
If you are using threads, then you have to program for threads.
Typically, in C++, this will mean some sort of thread class, which wraps
the member function call with a try/catch(...).
Of course, you are always at the mercy of errors or poor programming in
third party libraries.
--
James Kanze mailto:ka...@gabi-soft.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
11 rue de Rambouillet, 78460 Chevreuse, France +33 1 41 89 80 93
Somehow I had completely missed the problem which is in the grammar for
a condition which does not allow declarator that are either
uninitialised or use the function style initialsation. My immediate
reaction is that this is an oversight but OI will raise the issue with
WG21 because it is one more apparently silly inconsistency (e.g. you can
use function style but not assignment style in ctor init lists, almost
the exact reverse of this case.)
>
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
So it does. Make that Locker lock((Locker(M))). The point is that
initialisation with "=" is copy-initialisation and uses the copy
constructor.