Topic: Defining undefined, etc., behavior
Author: bart@ingen.ddns.info (Bart van Ingen Schenau)
Date: Sat, 20 May 2006 15:01:57 GMT Raw View
ThosRTanner wrote:
>
> Bart van Ingen Schenau wrote:
>> ThosRTanner wrote:
>>
>> >
>> > I'll just have to repeat what I said above. Under the current
>> > regime, you can't provide an exception specification, because if
>> > you do, your code will will CRASH, CEASE, STOP DEAD, FALL OVER IN A
>> > NASTY HEAP because the compiler inserts code to check at runtime
>> > something that it could have checked at compile time. In what way
>> > would providing compile time checks make this worse?
>>
>> It is worse, because perfectly safe code, like this:
>>
>> void foo(void) throw(std::bad_alloc)
>> {
>> std::vector<int> v;
>> v.push_back(1);
>> }
>>
>> will no longer compile.
>
>> Or are you suggesting that we should rewrite such functions as:
>>
>> void foo(void) throw(std::bad_alloc)
>> {
>> try
>> {
>> std::vector<int> v;
>> v.push_back(1);
>> }
>> catch(std::bad_alloc&)
>> {
>> /* Can't handle it here, but need this catch() to be able to let
>> the
>> exception pass out of the function */
>> throw;
>> }
>> catch(...)
>> {
>> assert(0 && "If we ever get here, we are hosed anyway");
>> }
>> }
> Under the circumstances, yes. What happens if someone changes int to
> some other class? That can throw some other exception?
If that person fails to update the internal try-catch construct (which
is very likely if the function has any level of complexity), then you
get either an assertion failure or the thrown exception is ignored
silently.
This is *at best* the same behaviour as you have currently, and at worst
a problem that is very hard to debug.
>
> The trouble is that push_back has an unlimited throw specification.
> So, although you might know that the specialisation for int can't
> throw anything other than a std::bad_alloc, you can't know it for any
> other class. So, if someone changes the int to a class which happens
> to throw something else, then you suddenly get a runtime error. Which
> is not acceptable as noted above.
Without an update to the try-catch block in the function, you still have
only a run-time failure.
>
> I'd be happy for
> a) An option like 'throw using' or something that switches on compile
> time checks
> b) (given that most template stuff is written inline anyway, because
> export seems to be rather unpopular) a throw spec that makes the
> compiler deduce what will be thrown from the implementation. Like
> perhaps 'throw auto' ?
If a good solution can be found for the problem of getting the exception
specifications for templated code right (perhaps in line with your
option b), then I think that compile-time checks of exception
specifications can be an added value.
>
>> Bart v Ingen Schenau
>
Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://www.eskimo.com/~scs/C-faq/top.html
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: bart@ingen.ddns.info (Bart van Ingen Schenau)
Date: Sun, 14 May 2006 02:04:50 GMT Raw View
ThosRTanner wrote:
>
> I'll just have to repeat what I said above. Under the current regime,
> you can't provide an exception specification, because if you do, your
> code will will CRASH, CEASE, STOP DEAD, FALL OVER IN A NASTY HEAP
> because the compiler inserts code to check at runtime something that
> it could have checked at compile time. In what way would providing
> compile time checks make this worse?
It is worse, because perfectly safe code, like this:
void foo(void) throw(std::bad_alloc)
{
std::vector<int> v;
v.push_back(1);
}
will no longer compile.
Or are you suggesting that we should rewrite such functions as:
void foo(void) throw(std::bad_alloc)
{
try
{
std::vector<int> v;
v.push_back(1);
}
catch(std::bad_alloc&)
{
/* Can't handle it here, but need this catch() to be able to let the
exception pass out of the function */
throw;
}
catch(...)
{
assert(0 && "If we ever get here, we are hosed anyway");
}
}
>
> It is NOT ACCEPTABLE for particular classes of software to crash. And
> therefore you can't use exception specifications in those classes of
> software without compile time checks.
Then DON'T USE exception specifications for those classes of software.
Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://www.eskimo.com/~scs/C-faq/top.html
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "ThosRTanner" <ttanner2@bloomberg.net>
Date: Mon, 15 May 2006 10:40:45 CST Raw View
Bart van Ingen Schenau wrote:
> ThosRTanner wrote:
>
> >
> > I'll just have to repeat what I said above. Under the current regime,
> > you can't provide an exception specification, because if you do, your
> > code will will CRASH, CEASE, STOP DEAD, FALL OVER IN A NASTY HEAP
> > because the compiler inserts code to check at runtime something that
> > it could have checked at compile time. In what way would providing
> > compile time checks make this worse?
>
> It is worse, because perfectly safe code, like this:
>
> void foo(void) throw(std::bad_alloc)
> {
> std::vector<int> v;
> v.push_back(1);
> }
>
> will no longer compile.
> Or are you suggesting that we should rewrite such functions as:
>
> void foo(void) throw(std::bad_alloc)
> {
> try
> {
> std::vector<int> v;
> v.push_back(1);
> }
> catch(std::bad_alloc&)
> {
> /* Can't handle it here, but need this catch() to be able to let the
> exception pass out of the function */
> throw;
> }
> catch(...)
> {
> assert(0 && "If we ever get here, we are hosed anyway");
> }
> }
Under the circumstances, yes. What happens if someone changes int to
some other class? That can throw some other exception?
The trouble is that push_back has an unlimited throw specification. So,
although you might know that the specialisation for int can't throw
anything other than a std::bad_alloc, you can't know it for any other
class. So, if someone changes the int to a class which happens to throw
something else, then you suddenly get a runtime error. Which is not
acceptable as noted above.
I'd be happy for
a) An option like 'throw using' or something that switches on compile
time checks
b) (given that most template stuff is written inline anyway, because
export seems to be rather unpopular) a throw spec that makes the
compiler deduce what will be thrown from the implementation. Like
perhaps 'throw auto' ?
> >
> > It is NOT ACCEPTABLE for particular classes of software to crash. And
> > therefore you can't use exception specifications in those classes of
> > software without compile time checks.
>
> Then DON'T USE exception specifications for those classes of software.
That means I can't use parts of the standard library, because that has
throw specifications.
But it is illogical to have something that can be compile time checked
checked at runtime. And the illogicality of it makes exception
specifications useless. It's like checking const correctness at
runtime.
> Bart v Ingen Schenau
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Bart van Ingen Schenau <bart@ingen.ddns.info>
Date: 6 May 2006 17:40:05 GMT Raw View
ThosRTanner wrote:
>
> Bart van Ingen Schenau wrote:
>> The problem with exception specifications for templates is that you
>> can not know what you should put in the exception specification.
>> Consider, for example, the following:
>>
>> // file: template.h
>> // provided as part of a 3rd party library
>>
>> template <class T>
>> T* createDefault() throw(std::bad_alloc /*, and what else? */)
>> {
>> return new T();
>> }
>>
>> // file my_source.cpp
>> #include "template.h"
>>
>> struct A
>> {
>> A() throw() {}
>> };
>>
>> struct B
>> {
>> B() throw(int) {}
>> };
>>
>> void foo() throw(std::bad_alloc)
>> {
>> A* a = createDefault<A>();
>>
>> try
>> {
>> B* b = createDefault<B>();
>> }
>> catch(int)
>> {
>> }
>> }
>>
>> Assuming a compile-time check of the exception specifications, what
>> should the exception specification on createDefault<>() have been to
>> avoid a compiler diagnostic.
> With the code as you have written it, there is no way you can do this.
> Suggestions that come to mind are:
>
> 1) No exception specification (allows anything to be thrown, and
> therefore everything must be caught.
Which will effectively mean that the use of exception specifications
will be discouraged, just like the current status quo.
Or you would end up with an enormous number of catch()-blocks where
humans can prove that the will never be reached, but the compilers
can't.
> 2) A specific exception, e.g. std::runtime_error
But as an author of a templated library (like Boost or Loki), how would
I know which exception is suitable for my users?
Because it is not an exception that is generated by my code, but it is
generated in a function provided by the user and only passes through my
code.
For that reason, translating the exception from the user into something
else is not acceptable, because it means an inevitable loss of
information.
> 3) A template, such as
> template <class T> T* createDefault() throw(std::bad_alloc,
> my_exception<T>)
> {
> return new T();
> }
And then the client must be forced into the mold that I have created.
But what if the same function is used in two templated libraries, where
both expect different, incompatible, exception specifications?
>
> Agreed, you'd have to rewrite your code slightly, as
> void foo() throw(std::bad_alloc)
> {
> try {
> A* a = createDefault<A>();
> }
> catch (my_exception<A> &) { }
> try
> {
> B* b = createDefault<B>();
> }
> catch(my_exception<B>& )
> {
> }
> }
But why would I have to try to catch an exception that can provably
never be thrown?
>
> A template still specificies an interface to be implemented, and you
> must then consider how much you care about the exceptions which you
> desire your implementation to throw.
This is where we differ in opinion.
If you want to consider the exception specification as part of the
interface contract, then there must be a way to specify 'I might throw
these exceptions, plus whatever the client-provided code throws'.
The main thing that I am concerned with is that, if I call a
client-provided function and that function decides to throw an
exception, then that exception must pass through my code without
breaking anything.
In other words, as long as my code has a sufficient level of exception
correctness, I could not care less what exceptions might pass through.
Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://www.eskimo.com/~scs/C-faq/top.html
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "ThosRTanner" <ttanner2@bloomberg.net>
Date: Mon, 8 May 2006 11:27:20 CST Raw View
Bart van Ingen Schenau wrote:
> ThosRTanner wrote:
>
> >
> > Bart van Ingen Schenau wrote:
> >> The problem with exception specifications for templates is that you
> >> can not know what you should put in the exception specification.
> >> Consider, for example, the following:
> >>
> >> // file: template.h
> >> // provided as part of a 3rd party library
> >>
> >> template <class T>
> >> T* createDefault() throw(std::bad_alloc /*, and what else? */)
> >> {
> >> return new T();
> >> }
> >>
> >> // file my_source.cpp
> >> #include "template.h"
> >>
> >> struct A
> >> {
> >> A() throw() {}
> >> };
> >>
> >> struct B
> >> {
> >> B() throw(int) {}
> >> };
> >>
> >> void foo() throw(std::bad_alloc)
> >> {
> >> A* a = createDefault<A>();
> >>
> >> try
> >> {
> >> B* b = createDefault<B>();
> >> }
> >> catch(int)
> >> {
> >> }
> >> }
> >>
> >> Assuming a compile-time check of the exception specifications, what
> >> should the exception specification on createDefault<>() have been to
> >> avoid a compiler diagnostic.
> > With the code as you have written it, there is no way you can do this.
> > Suggestions that come to mind are:
> >
> > 1) No exception specification (allows anything to be thrown, and
> > therefore everything must be caught.
>
> Which will effectively mean that the use of exception specifications
> will be discouraged, just like the current status quo.
I realise people are lazy. People didn't like the introduction of the
const keyword to C, because if you used it it would force you to write
correct code. But people accept it now without query.
The reason people may be discouraged now is that if you write an
exception specification, and you do something wrong in a client, it
causes a FATAL runtime error. In ALL places I have worked, this as NOT
been acceptable behaviour for a program. It therefore means that
exception specifications CANNOT be used, except in some places where
you have to use throw() in order to get certain compilers to actually
inline inline functions (and you have to then be pretty sure that
anything called doesn't throw).
> Or you would end up with an enormous number of catch()-blocks where
> humans can prove that the will never be reached, but the compilers
> can't.
You have evidence for that, or is that, as I suspect, just FUD. If you
throw only exception X, then you will only need try/catch blocks where
you call something that has a looser exception specification than you
do. This is possible if you are retrofitting this to existing code and
trying to do it top down rather than bottom up. In the vast majority of
other cases, it will be because you have a potential runtime error.
>
> > 2) A specific exception, e.g. std::runtime_error
>
> But as an author of a templated library (like Boost or Loki), how would
> I know which exception is suitable for my users?
Don't use an exception specification then.
> Because it is not an exception that is generated by my code, but it is
> generated in a function provided by the user and only passes through my
> code.
No, it won't pass through your code. If will cause your code to CRASH,
to CEASE, to STOP DEAD, to FALL OVER IN A NASTY HEAP if you use an
exception specification. And that is simply not acceptable in many
situations.
> For that reason, translating the exception from the user into something
> else is not acceptable, because it means an inevitable loss of
> information.
For that reason, you cannot have an exception specification. Because if
you do, under the current situation, your program will CRASH, CEASE,
STOP DEAD, FALL OVER IN A NASTY HEAP because the compiler inserts code
to check at runtime something that it could have checked at compile
time.
> > 3) A template, such as
> > template <class T> T* createDefault() throw(std::bad_alloc,
> > my_exception<T>)
> > {
> > return new T();
> > }
>
> And then the client must be forced into the mold that I have created.
> But what if the same function is used in two templated libraries, where
> both expect different, incompatible, exception specifications?
Then, under the current regime, your program CRASH, CEASE, STOP DEAD,
FALL OVER IN A NASTY HEAP because the compiler inserts code to check at
runtime something that it could have checked at compile time.
> >
> > Agreed, you'd have to rewrite your code slightly, as
> > void foo() throw(std::bad_alloc)
> > {
> > try {
> > A* a = createDefault<A>();
> > }
> > catch (my_exception<A> &) { }
> > try
> > {
> > B* b = createDefault<B>();
> > }
> > catch(my_exception<B>& )
> > {
> > }
> > }
>
> But why would I have to try to catch an exception that can provably
> never be thrown?
It's a template, how can you prove it'll never be thrown? You complain
because if its a template you don't know what exceptions might be
thrown, and now you complain because you know a certain exception will
never be thrown. You can't have it both ways.
>
> >
> > A template still specificies an interface to be implemented, and you
> > must then consider how much you care about the exceptions which you
> > desire your implementation to throw.
>
> This is where we differ in opinion.
> If you want to consider the exception specification as part of the
> interface contract, then there must be a way to specify 'I might throw
> these exceptions, plus whatever the client-provided code throws'.
That's what the example I did above does. Remember that the throw spec
includes all classes derived from the specified class.
>
> The main thing that I am concerned with is that, if I call a
> client-provided function and that function decides to throw an
> exception, then that exception must pass through my code without
> breaking anything.
Then in the current regime, you can't provide an exception
specification, because if you do, your code will will CRASH, CEASE,
STOP DEAD, FALL OVER IN A NASTY HEAP because the compiler inserts code
to check at runtime something that it could have checked at compile
time.
> In other words, as long as my code has a sufficient level of exception
> correctness, I could not care less what exceptions might pass through.
I'll just have to repeat what I said above. Under the current regime,
you can't provide an exception specification, because if you do, your
code will will CRASH, CEASE, STOP DEAD, FALL OVER IN A NASTY HEAP
because the compiler inserts code to check at runtime something that it
could have checked at compile time. In what way would providing compile
time checks make this worse?
It is NOT ACCEPTABLE for particular classes of software to crash. And
therefore you can't use exception specifications in those classes of
software without compile time checks.
>
> Bart v Ingen Schenau
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Robert Mabee <rmabee@comcast.net>
Date: Sun, 30 Apr 2006 07:29:04 CST Raw View
Scott Meyers wrote:
> SuperKoko wrote:
> > If you want, you can propose such addition to C++0x
>
> You certainly can, but my understanding is that the standardization
> committee is no longer considering proposals for language changes to
> C++0x, so if what you have in mind isn't already on the docket, you're
> probably looking at C++1x :-|
Then how about diagnosing the undefined behavior with a stand-alone
program like lint, or with a special option to the compiler? Were all
of the undefined cases detectable in one compilation unit, or at least
detectable by a reasonable source scan, or are there troubling cases
that can only be detected at run-time? That could still be addressed
by a special compiler option and some run-time helpers.
If this is a real problem (as I believe) then there should be some money
looking for a commercial solution.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "SuperKoko" <tabkannaz@yahoo.fr>
Date: Sun, 23 Apr 2006 20:09:23 CST Raw View
kuyper@wizard.net wrote:
> SuperKoko wrote:
> > Nicolas Pavlidis wrote:
> >
> > > There is a great problem with exception - spec, and it is named
> > > templates :-).
> > >
> >
> > In fact, even with templates, the notion of "exception-specification" i
> > gave, remains, is well-defined, and easily checkable by compilers (at
> > the point of template instanciation)...
> >
> > However, I agree that it is extremely inconvenient with templates (and
> > all callbacks mechanisms).
> >
> > The greater problem is templates, because, in a template definition,
> > the exception-specification is static (i.e. It can't depend on the
> > template parameters):
>
> Is that so? My C++ books are at home, so I can't check right now, but I
> would have thought that having a throw specification that's template
> type-dependent was legal. To take an absurdly simple case:
>
> template <typename T> void throwwrap(T t) throw(T) { throw(t); }
>
> int main() {throwwrap(1);}
>
> My understanding is that the real problem is that a template function's
> throw specification can't easily be adapted to automatically cover the
> throw specifications of the template-argument dependent functions that
> it calls.
>
My point is that they can!
At the point of instantiation, of course!
At the point of instantiation, all the required is information to
compute the induced exception specification is available.
For instance:
template <class T, class U>
void DoWork(T& x,U u) // induced exception-specification
{
x.Work1();
x.Work2(u);
}
class Aux1
{
public:
Aux1(const Aux1&); // no explicit exception-specification : thus it is
implicitly the infinite-set-exception-specification
};
class Exception1;
class Exception2;
class Exception3;
class Worker1
{
public:
void Work1() throw();
void Work2(Aux1) throw(Exception1);
};
class Aux2
{
public:
Aux2(const Aux2&) throw();
};
class Worker2
{
public:
void Work1() throw(Exception2);
void Work2(Aux2) throw(Exception1);
};
void func() throw(Exception1)
{
Aux1 a1;
Aux2 a2;
Worker1 w1;
Worker2 w2;
DoWork(w1,a1); // warning : The induced exception-specification of
DoWork<Worker1, Aux1> is the infinite set of exceptions, which is
looser than the exception-specification of func()
DoWork(w2,a2); // warning : The induced exception-specification of
DoWork<Worker2, Aux2> contains Exception2, which is looser than func().
}
void func2() throw(Exception1, Exception2)
{
Aux1 a1;
Aux2 a2;
Worker1 w1;
Worker2 w2;
try{
DoWork(w1,a1);
}
catch(...){ // okay, no exception can escape
}
DoWork(w2,a2); // okay : the induced exception-specification of
DoWork<Worker2, Aux2> is throw(Exception1, Exception2)
}
There are three benefits
1) For the compiler : A good analyzis of exceptions would allow
generation of a code containing few or zero, implicit catch blocks, for
catching unexpected exceptions.
2) For the programmer : Information about where unexpected exceptions
can occur.
The programmer can, in that case, see whether it is due to a logical
programming error, or simply due to a limitation of the static analyzis
of the compiler : For example, where there are callbacks, or other
runtime mechanisms.
3) No language change... except the addition of a warning message.
It is just better usage of the already-existing exception system.
It can be a guideline to the programmer, answering to the question :
Where must I put exception specifications, knowing that there is a risk
of excessive coupling if I put them into templates?
David Abrahams wrote:
> No, the real problem is that in general, exception specifications
> (whether statically or dynamically checked) cause an undesirable
> coupling with code that shouldn't be coupled, because the code doesn't
> really care what exception types are passing through it, and nobody
> thinks it's worth extending the language to better support a concept
> that is, for most applications, worse than useless.
With that, templates would not need to care at all about exception
specifications.
Nicolas Pavlidis wrote:
> One problem remains here, C++ does not force one to do an exception -
> specification, so this leads to the same problem (as my Idea of a static
> typeid operator does), that not all exceptions can be caught, if they
> are not specified. So you still have to leave them out, or reduce the
> freedom a C++ - programmer has
Actually C++ does force exception-specifications!
In fact, if there are no explicit exception-specification, it is
implicitly, the infinite set of exceptions that can be thrown.
But, with the above proposal, the syntax of
no-explicit-exception-specification would be interpreted, depending on
the context as:
(1) The infinite set of exceptions, for normal functions, or at least
for imported functions (thus, functions whose code is not available in
the current translation unit).
(2) The smallest induced set of exceptions, that the compiler can
statically analyze, for templates, at the point of their instantiation,
and perhaps, for all non-imported functions, whose code is available in
the current translation unit.
If a function has an explicit exception-specification more restrictive
than the exceptions that the compiler can deduce it can throw, then the
compiler will emit a warning message, and generate implicit try/catch
blocks for generating unexpected exceptions at runtime.
In fact, you see that the programmer would really need to explicit
exception specifications, at translation unit boundaries, but inside a
translation unit, the compiler can deduce all the induced exception
specifications.
Nicolas Pavlidis wrote:
> Partly yes. The problem with this approach is, that in this case, you
> can not instantiate a template with an incomplete type. Because the
> compiler needs to know much more the e.g. a forward declaration gives.
No.
The exception specification of all operations (member functions,
operators, constructors...) on forward-declared types are unknown at
the point of instantiation, but they can't be accessed without
generating a compile-time error.
So, there is no problem.
At the point of instantiation, the compiler can fully, and statically
deduce all exceptions that may be thrown by a template function (member
or not).
Nicolas Pavlidis wrote:
> ACK, but this would be resolvable with some kind of a static typeid or
> something else.
I don't exactly how, but there may be solutions...
However, my solution has the big advantage of requiring no work for the
template class/function programmer.
There is no useless coupling between the template and its actual
template arguments, too.
Nicolas Pavlidis wrote:
> What is [possible IMHO is to make typeid (the one which exsists), or
> better the typeinfo - clas more powerful, so that it can provide more
> information than just (the non standardized) class name of some type.
Independently of exceptions, I agree on that point.
typeinfo information should be standardized, and perhaps extended.
For instance, currently, compilers have to chose between a
user-friendly name() and a computer-friendly name(). There could be two
member functions, instead of one.
Nicolas Pavlidis wrote:
> For example, that I can call it in a catch(...) - Block, and ask it to
> give me the last (unknown) exception that was thrown, to react to this.
We can't do much on an unknown exception. But, I agree that having a
typeinfo object could at least be useful for logging purposes.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "ThosRTanner" <ttanner2@bloomberg.net>
Date: Mon, 24 Apr 2006 09:17:25 CST Raw View
Bart van Ingen Schenau wrote:
> ThosRTanner wrote:
>
> >
> > Nicolas Pavlidis wrote:
> >> ThosRTanner wrote:
> >> > Nicolas Pavlidis wrote:
> >> >> ThosRTanner wrote:
> >> >>> SuperKoko wrote:
> >> >>> But people don't use throw specifications because the runtime
> >> >>> check makes them unusable in systems where abnormal termination
> >> >>> is not an option.
> >> >> There is a great problem with exception - spec, and it is named
> >> >> templates :-).
> >> >>
> >> >> You can not react to exception specifications in templates, so it
> >> >> is better IMHO to leave them out, if you use templates.
> >> >
> >> > What I was suggesting doesn't stop you doing that. But at least you
> >> > would get a compiler error rather than a runtime error if you did
> >> > this.
> >>
> >>
> >> We've some kind of a misunderstanding.
> >>
> >> I made use of throw - spec, until I found out that they are
> >> incompatible to templates, so I left them out in template code.
> >>
> >> In non template code I leave them out because of the runtime check.
> >>
> >> If the compiler would check this it would be fine, but this does not
> >> solve the template - problem, which was the intend of my post.
> >
> > If the language won't let you write
> >
> > template <class T> void fred() throw(My_Exception<T>) { ... }
> >
> > then you can't. That's a language restriction, and quite outside the
> > scope of whether or not the compiler should do a run time or compile
> > time check. If it has enough information to do one, it has enough to
> > do the other.
>
> The problem with exception specifications for templates is that you can
> not know what you should put in the exception specification.
> Consider, for example, the following:
>
> // file: template.h
> // provided as part of a 3rd party library
>
> template <class T>
> T* createDefault() throw(std::bad_alloc /*, and what else? */)
> {
> return new T();
> }
>
> // file my_source.cpp
> #include "template.h"
>
> struct A
> {
> A() throw() {}
> };
>
> struct B
> {
> B() throw(int) {}
> };
>
> void foo() throw(std::bad_alloc)
> {
> A* a = createDefault<A>();
>
> try
> {
> B* b = createDefault<B>();
> }
> catch(int)
> {
> }
> }
>
> Assuming a compile-time check of the exception specifications, what
> should the exception specification on createDefault<>() have been to
> avoid a compiler diagnostic.
With the code as you have written it, there is no way you can do this.
Suggestions that come to mind are:
1) No exception specification (allows anything to be thrown, and
therefore everything must be caught.
2) A specific exception, e.g. std::runtime_error
3) A template, such as
template <class T> T* createDefault() throw(std::bad_alloc,
my_exception<T>)
{
return new T();
}
Agreed, you'd have to rewrite your code slightly, as
void foo() throw(std::bad_alloc)
{
try {
A* a = createDefault<A>();
}
catch (my_exception<A> &) { }
try
{
B* b = createDefault<B>();
}
catch(my_exception<B>& )
{
}
}
A template still specificies an interface to be implemented, and you
must then consider how much you care about the exceptions which you
desire your implementation to throw.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: usenet@aristeia.com (Scott Meyers)
Date: Sat, 29 Apr 2006 04:23:32 GMT Raw View
Greg Herlihy wrote:
> Scott Meyers wrote:
>> What work has been done on eliminating undefined behavior in C or C++?
> A good starting point for investigating C++'s undefined (et al.)
> behaviors would be this paper: "An investigation of the unpredictable
> features of the C++ language."
Thanks for this reference (and I apologize for starting a thread in this
newsgroup and then disappearing, but I hit the road shortly after posting, and
for reasons I'll spare you, my Usenet access on the road was minimal).
James Kanze wrote:
> My impression was that Scott was asking about possible changes,
> to eliminate unnecessary undefined behaviors.
To be honest, my interest in "eliminating" it focuses on guidelines for
programmers that would help them avoid it, rather than any kind of proposal for
modifying C++ itself. I think the chances of making such modifications to the
standard for C++ are essentially zero.
I have to check out the new JSF guidelines
(http://public.research.att.com/~bs/JSF-AV-rules.pdf), and I know that MISRA is
working on a set of guidelines for C++. A quick search for "undefined" in the
JSF document reveals that at least some of the guidelines focus on avoiding "a
number of features whose behaviors are local-specific, unspecified, undefined,
implementation-defined, or otherwise poorly defined and hence error prone."
ThosRTanner wrote:
> Some of that work must have been done for the embedded C++ language
> subset, though admittedly that is likely to go more on performance than
> safety critical.
I have not read the spec for Embedded C++, but my understanding is that
eliminating undefined behavior was not one of their goals, and a quick perusal
of the information at http://www.caravan.net/ec2plus/spec.html doesn't suggest
that they worked on that at all. In fact, the spec appears to add lots of
explicitly undefined behavior.
SuperKoko wrote:
> This can be solved by compiler warnings (GCC emit a warning when
> defining a class containing virtual methods, but no virtual
> destructor).
> If you want, you can propose such addition to C++0x
You certainly can, but my understanding is that the standardization committee is
no longer considering proposals for language changes to C++0x, so if what you
have in mind isn't already on the docket, you're probably looking at C++1x :-|
Scott
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: SeeWebsiteForEmail@erdani.org ("Andrei Alexandrescu (See Website For Email)")
Date: Sun, 16 Apr 2006 23:20:09 GMT Raw View
SuperKoko wrote:
> "Andrei Alexandrescu See Website For Email a =E9crit :
>=20
>=20
>>It could be argued that the function call can be faster. Each PUSH will
>>decrement the stack register. PUSH looks small, but it's not; it entail=
s
>>a fair amount of microcode, and since we restrict ourselves to 8088,
>>that microcode will execute slowly, too - only a tad faster than the
>>equivalent assembly code. Or is it just as slow? I don't remember.
>>
>=20
> You know, I exactly counted the CPU cycles needed!
> And, modern compilers have dependencies on push operation, but not the
> 8088.
> And, mov [bp - ...],ax is slower than push ( 22 cycles instead of 15
> cycles).
> There is also the problem of accessing the stack with bp... as I said,
> with a three-parameters function, the compiler needs to save the old
> bp, install a new bp, mov arguments, and then restore the old bp.
> IMHO 102 cycles is significantly more than 57 cycles.
>=20
>=20
>>In contrast, this other method computes the stack frame of f()
>>statically, and then writes to the stack without needing to manipulate
>>sp. As a consequence, there are less RAW dependencies and the potential
>>for parallelization is superior (a potential not illustrated by the
>>example at hand).
>=20
> Only on some modern architectures.
> But, C++ is a general purpose language meant to be portable on many
> CPU, not only PowerPC and PC.
I believe we kind of reached agreement. I agree that if we sat down and=20
counted cycles on an ancient CPU that has special microcode for push and=20
pop operations, no cache, no pipeline, no superscalar architecture, no=20
nothing that has been invented in the past 20 years, fine: RTL is better=20
than LTR.
I would also agree that it's nice to leave a chance for people to write=20
C++0x code on a wristwatch, and have that code not waste 0.1% efficiency.
But, as I mentioned in a previous discussion on c.l.c++.m on the=20
subject, that would be missing the big picture.
The big picture is, it is bad for programmers that arguments of a=20
function aren't evaluated in a known order. Very bad, I think, and=20
gratuitously bad, if you consider the dynamics of hardware and software=20
development.
If we look at things that way, it would be easy to reach an agreement=20
that a defined order of evaluation would be desirable, and that the=20
price to pay for that is negligible. Right now, we're hurting too many=20
for the doubtful benefit of very few.
> Anyway, I think that, for performance reasons, if the C++ standard
> specifies all order of evaulation (especially for the order of side
> effects), optimizing compilers will have compiler switches in order to
> get the old behaviour.
> On superscalar CPU, the compiler freedom of order of evaluation of side
> effects reduces assembly instruction dependencies.
That is correct in theory, but in practice it doesn't make a difference.=20
Out-of-order execution is in place anyway, and today's processors figure=20
out on the fly what can be parallelized within a few instructions. There=20
is a hardware unit created exactly for that very purpose.
> And, as soon as references/pointers are used, compilers have much
> difficulty to make no-alias optimizations. Actually, they are at least
> allowed to do no-alias optimizations between two sequence points.
I do not understand this point, and I'd be curious (unless I convinced=20
you there' no more need to explain :o)). Thanks.
Andrei
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "ThosRTanner" <ttanner2@bloomberg.net>
Date: Mon, 17 Apr 2006 09:44:50 CST Raw View
John Nagle wrote:
> Scott Meyers wrote:
> > What work has been done on eliminating undefined behavior in C or C++?
> > Off the top of my head, some undefined behavior is due to "stupid user
> > behavior," e.g., indexing beyond the end of an array, dereferencing a
> > null pointer, etc. Other undefined behavior -- or maybe it's
> > unspecified or implementation-defined behavior, I can never keep those
> > terms straight -- arises from flexibility for implementers, e.g., order
> > of evaluating arguments for function calls, order of expression
> > evaluation between sequence points, order of initialization of non-local
> > statics defined in separate translation units. I'd be interested in any
> > work that's been done on identifying these kinds of behavior (beyond
> > grepping the standards for "undefined", "unspecified", etc.) and
> > suggesting ways to eliminate or avoid them, ideally without any obvious
> > runtime cost. (Checking array bounds would fail the "no obvious runtime
> > cost" test, while defining function argument evaluation order would
> > pass, because, well, because it's not obvious to me that imposing a
> > required order would necessarily incur a runtime penalty :-})
>
> There really is more than one type of undefined behavior, and
> classifying the ones in the C++ specification might be useful.
> A few obvious classes:
>
> -- Violations of the storage model
> Examples:
> -- Indexing beyond the limits of an array
> -- Use of an iterator after an invalidating operation on its collection
>
> -- Violations of the construction model
> Examples:
> -- Access to an object before the constructor has completed or after
> the destructor has run (includes references to uninitialized variables)
> -- Casting into a type for which not all possible values are valid
> (pointers, constructed objects)
>
> -- Violations of the sequencing model
> Examples:
> -- An expression with calls to two functions with (interfering?) side effects
> for which the evaluation rules do not explicitly determine the order
> of evaluation
>
> -- Violations of the concurrency model (insofar as C++ has one)
> Examples:
> -- Non-volatile data shared between two threads.
>
> With a taxonomy like this, some issues become clearer. The last three
> categories, for example, are potentially detectable at compile time.
> The issues for those categories revolve around whether certain
> formally undefined behavior should be permitted.
>
> John Nagle
> Animats
>
I think to that it would be worth adding instances where behaviour
isn't undefined but at best unhelpful and arguably completely stupid,
such as:
Inheriting from a class with a non-virtual destructor - this is well
defined, but can break code in a really nasty way.
Incorrectly overriding virtual functions (again, the behaviour is well
defined, but results in a source of almost endless confusion)
<bang drum> throwing an exception that isn't in a clients throw() spec
and isn't caught by the client</bang>
All of these can be detected by the compiler. All of these behaviours
are not (to my mind) intuitive. And they all (except the last) take a
lot of debugging time to sort out. They do, in my experience, put the
novice off the language.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Bo Persson" <bop@gmb.dk>
Date: Mon, 17 Apr 2006 09:45:14 CST Raw View
""Andrei Alexandrescu (See Website For Email)""
<SeeWebsiteForEmail@erdani.org> skrev i meddelandet
news:4442AF15.6000801@erdani.org...
SuperKoko wrote:
>> Only on some modern architectures.
>> But, C++ is a general purpose language meant to be portable on many
>> CPU, not only PowerPC and PC.
>I believe we kind of reached agreement. I agree that if we sat down
>and counted cycles on an ancient CPU that has special microcode for
>push and pop operations, no cache, no pipeline, no superscalar
>architecture, no nothing that has been invented in the past 20 years,
>fine: RTL is better than LTR.
>
>I would also agree that it's nice to leave a chance for people to
>write C++0x code on a wristwatch, and have that code not waste 0.1%
>efficiency.
That could be tuned, if necessary.
For most code, evaluation order does not matter. We also trust the
compiler to find a good order for us. Even if we had a LTR order
defined, in the absence of side effects the compiler could use the
"as-if" rule to continue evaluation RTL.
"Properly" written code would not be affected.
> If we look at things that way, it would be easy to reach an
> agreement that a defined order of evaluation would be desirable, and
> that the price to pay for that is negligible. Right now, we're
> hurting too many for the doubtful benefit of very few.
I don't really agree that it is very desirable, as it is of no use to
me. I do agree that the cost is negligible, performance-wise. You just
have to persuade the compiler writers. :-)
Bo Persson
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: kuyper@wizard.net
Date: Mon, 17 Apr 2006 09:51:25 CST Raw View
Pete Becker wrote:
> Francis Glassborow wrote:
.
> > However every time I do arithmetic I
> > have to go through the same thought and error trapping.
>
> No, you don't. Once you've validated the input you don't have to keep
> rechecking it.
That's true only if it's impossible for valid inputs to lead to
overflow at any intermediate step in the calculations that depend upon
them. That's true in some problem domains, but not in all. If I'm
writing a spreadsheat, and a user inserts a valid value in one cell, a
valid value in another cell, and a valid formule which calculates their
product the third cell, which user input am I supposed to reject as
invalid to prevent having to check whether that product will result in
an overflow? The correct, defined, behavior for my spreadsheet is not
to reject any of those three inputs, but rather to display, in the
third cell, an indication that the calculation resulted in overflow.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "SuperKoko" <tabkannaz@yahoo.fr>
Date: Mon, 17 Apr 2006 09:52:23 CST Raw View
Francis Glassborow wrote:
> In article <e1p3fi$bj2$1@emma.aioe.org>, James Kanze
> <kanze.james@neuf.fr> writes
> >That's what the standard says (and I suspect that Scott knows
> >this). The problem Scott has is more likely knowing when each
> >of the above applies. (There's also the problem that although
> >the standard requires an implementation to document
> >implementation defined behavior, to my knowledge none do.)
>
> The problem is that it seems that the choices for implementation defined
> behaviour must always be ones that allow a program to continue
> execution.
>
> The problem with this is that it makes overflow of signed and floating
> point values undefined behaviour. If we allowed an implementation to
> specify that, for example, overflow of an int value results in the
> program terminating we could then provide well-defined behaviour for all
> the cases where the implementation does not go to that extreme.
Theorically a compiler is allowed to document a good behaviour for
undefined behaviour constructs.
So, IMHO, all compilers that can (for example all compilers on x86,
PowerPC, 680x0, Z80, and many other processors) have a correct
behaviour should document it.
1.4.12 undefined behavior
[defns.undefined]
behavior, such as might arise upon use of an erroneous program
con-
struct or of erroneous data, for which the Standard imposes
no
requirements. Undefined behavior may also be expected when the
stan-
dard omits the description of any explicit definition of
behavior.
_________________________
3) Function signatures do not include return type, because that
does
not participate in overload resolution.
[Note: permissible undefined behavior ranges from ignoring the
situa-
tion completely with unpredictable results, to behaving during
trans-
lation or program execution in a documented manner characteristic
of
the environment (with or without the issuance of a diagnostic
mes-
sage), to terminating a translation or execution (with the issuance
of
a diagnostic message). Note that many erroneous program constructs
do
not engender undefined behavior; they are required to be diagnosed.
]
The problem is that an implementation is not required to define its
behaviour.
But I am not sure that crashing is allowed for implementation-defined
behaviours, so I don't think we could change undefined behaviours to
implementation-defined behaviours.
However, since there are not so much "undefined behaviours" in the
standard, and that writing a compiler is a huge work, I would like that
ALL undefined behaviours be documented.
Of course, doing things such as dereferencing the NULL pointer would be
documented as "undefined" by the compiler.
But, things such as integer overflow would probably be well documented
by many compilers.
I would like to reduce, as far as possible, implementation-dependent
behaviours.
Two examples come in mind:
The interpretation of multicharacters literals : i.e. 'abcd'
And, a much more important one:
The order of initialization of global variables in multiple translation
units.
The fact that it is unspecified is mainly due to the fact that the
standard does not talk about order of modules (i.e. order of object
files passed to the linker), and allows an implementation to initialize
global variables when a module is "entered" from the first time.
IMHO, it is a bad idea, because portability is much harder.
It would be better to specify that all static-storage duration objects
are initialized before main is entered, and, in the "order of modules"
(as far as possible there would be good to find a mean to say more
rigourously what is this order), and portable-code writers would use
singletons or such workarounds in order to get the "behaviour" of lazy
initialization.
Such behaviour is perhaps permitted in order to allow C++ interpretors,
with dynamic loading of modules.
In that case, it should be possible to specify that initialization is
done in the order modules are loaded.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "SuperKoko" <tabkannaz@yahoo.fr>
Date: Mon, 17 Apr 2006 09:51:52 CST Raw View
> That is correct in theory, but in practice it doesn't make a difference.
> Out-of-order execution is in place anyway, and today's processors figure
> out on the fly what can be parallelized within a few instructions. There
> is a hardware unit created exactly for that very purpose.
But, there will be more dependent instructions.
> > And, as soon as references/pointers are used, compilers have much
> > difficulty to make no-alias optimizations. Actually, they are at least
> > allowed to do no-alias optimizations between two sequence points.
>
> I do not understand this point, and I'd be curious (unless I convinced
> you there' no more need to explain :o)). Thanks.
Let me explain with a trivial example:
void func(int* p,int *q,int *r)
{
(*p)=(++(*q)) + (++(*r));
}
Actually, here is what the compiler (GCC 4.1.0) can generate actually
(for k6-2 CPU):
0x08048420 <_Z4funcPiS_S_+0>: mov eax,DWORD PTR [esp+8]
0x08048424 <_Z4funcPiS_S_+4>: mov ecx,DWORD PTR [esp+12]
0x08048428 <_Z4funcPiS_S_+8>: mov edx,DWORD PTR [eax] ; 3
0x0804842a <_Z4funcPiS_S_+10>: inc edx
0x0804842b <_Z4funcPiS_S_+11>: mov DWORD PTR [eax],edx
0x0804842d <_Z4funcPiS_S_+13>: mov eax,DWORD PTR [ecx]
0x0804842f <_Z4funcPiS_S_+15>: inc eax
0x08048430 <_Z4funcPiS_S_+16>: mov DWORD PTR [ecx],eax
0x08048432 <_Z4funcPiS_S_+18>: add edx,eax ;8
0x08048434 <_Z4funcPiS_S_+20>: mov eax,DWORD PTR [esp+4] ;9
0x08048438 <_Z4funcPiS_S_+24>: mov DWORD PTR [eax],edx
0x0804843a <_Z4funcPiS_S_+26>: ret ;10
10 CPU cycles
And, optimized for pentium:
0x08048410 <_Z4funcPiS_S_+0>: mov eax,DWORD PTR [esp+8]
0x08048414 <_Z4funcPiS_S_+4>: mov ecx,DWORD PTR [esp+12] ;1
0x08048418 <_Z4funcPiS_S_+8>: mov edx,DWORD PTR [eax] ;3
0x0804841a <_Z4funcPiS_S_+10>: inc edx ;4
0x0804841b <_Z4funcPiS_S_+11>: mov DWORD PTR [eax],edx
0x0804841d <_Z4funcPiS_S_+13>: mov eax,DWORD PTR [ecx] ;5
0x0804841f <_Z4funcPiS_S_+15>: inc eax ;6
0x08048420 <_Z4funcPiS_S_+16>: mov DWORD PTR [ecx],eax
0x08048422 <_Z4funcPiS_S_+18>: add edx,eax ; 7
0x08048424 <_Z4funcPiS_S_+20>: mov eax,DWORD PTR [esp+4] ;8
0x08048428 <_Z4funcPiS_S_+24>: mov DWORD PTR [eax],edx ;10
0x0804842a <_Z4funcPiS_S_+26>: ret ;11
11 CPU cycles
However, with a superscalar 64 bits CPU it would be more optimizable
(here, the lack of scratch registers makes lose 2 CPU cycles).
I don't know the architecture of pentium IV processors, but I think
that it would use only 8 CPU cycles here.
But you see that there is no need to read back *q and *r after having
written them to the memory.
But, if order of side effects was defined, there would be a necessity.
The generated code for k6-2 could be:
mov ecx,[esp+8] ;1
mov edx,[esp+12] ;2
inc [ecx] ;5
inc [edx] ;8
mov eax,[esp+4] ;9
mov ecx,[ecx] ;10
mov edx,[edx] ;11
add ecx,edx ;12
mov [eax],ecx
ret ; 13 CPU cycles
So, you see that it is not possible to store data in registers because
p, q and r can be aliases (i.e., p can perhaps be equal to q or r).
The difference is not huge, but it would be greater if the code was
inlined:
Especially on modern 64 bits CPU (PC and PowerPC) which have a lot of
registers.
In fact, with very few registers, using memory is necessary, but on
x86-64 computers, there are many more registers, and thus, the benefit
would be even greater.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: bop@gmb.dk ("Bo Persson")
Date: Mon, 17 Apr 2006 16:02:53 GMT Raw View
"ThosRTanner" <ttanner2@bloomberg.net> skrev i meddelandet
news:1145261505.465774.326140@z34g2000cwc.googlegroups.com...
>
>
> I think to that it would be worth adding instances where behaviour
> isn't undefined but at best unhelpful and arguably completely
> stupid,
> such as:
>
> Inheriting from a class with a non-virtual destructor - this is well
> defined, but can break code in a really nasty way.
It's nasty only if you delete an object through a pointer to base
class. I never ever do.
Should my code suffer from other's stupidity? :-)
>
> Incorrectly overriding virtual functions (again, the behaviour is
> well
> defined, but results in a source of almost endless confusion)
How is the compiler to know when a function is hiding, instead of
overriding, a base class function (without breaking existing code) ?
>
> <bang drum> throwing an exception that isn't in a clients throw()
> spec
> and isn't caught by the client</bang>
The compiler is allowed, but not required, to warn about this. In the
general case, it is not possible to detect all problems.
void g();
void f() throw(std::bad_alloc)
{
g();
}
Is this code ok?
> All of these can be detected by the compiler.
That's the problem. They can *sometimes* be detected by the compiler.
What happens when they cannot?
Bo Persson
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "SuperKoko" <tabkannaz@yahoo.fr>
Date: 17 Apr 2006 17:20:02 GMT Raw View
ThosRTanner wrote:
> I think to that it would be worth adding instances where behaviour
> isn't undefined but at best unhelpful and arguably completely stupid,
> such as:
>
> Inheriting from a class with a non-virtual destructor - this is well
> defined, but can break code in a really nasty way.
This can be solved by compiler warnings (GCC emit a warning when
defining a class containing virtual methods, but no virtual
destructor).
This could also be solved by specifying that any class having one or
more virtual method has implicitly, a virtual destructor (overhead
would be very small).
>
> Incorrectly overriding virtual functions (again, the behaviour is well
> defined, but results in a source of almost endless confusion)
A few compilers provide an __override keyword that solves that problem.
class Base
{
public:
virtual void func(long x);
};
class Derived:public Base
{
public:
virtual void func(int x) __override; // error : must override a virtual
method of a base class.
};
If you want, you can propose such addition to C++0x
> <bang drum> throwing an exception that isn't in a clients throw() spec
> and isn't caught by the client</bang>
IMHO, that could not be sensibly specified in the standard.
But, surely, compilers could emit warnings when the error is
diagnosable.
Similarly, misuse of the assignment in conditions:
if (a=b){
}
Is detected by many compilers.
should the standard define a minimal set of warning messages? Perhaps.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: johnchx2@yahoo.com
Date: Mon, 17 Apr 2006 18:21:52 CST Raw View
"Andrei Alexandrescu See Website For Email wrote:
> I believe we kind of reached agreement. I agree that if we sat down and
> counted cycles on an ancient CPU that has special microcode for push and
> pop operations, no cache, no pipeline, no superscalar architecture, no
> nothing that has been invented in the past 20 years, fine: RTL is better
> than LTR.
I'm probably missing something, but it seems like the entire RTL-vs-LTR
conflict could be resolved (and eliminated) by allowing the compiler to
implicitly rewrite the order of a function's formal parameters
depending on whether the target architecture were "naturally" RTL or
LTR (with corresponding rewrites at the call sites).
Here's an example of what I mean:
Let's say that we have a function:
int f( int a, int b );
and a couple of functions which either cause or are affected by
side-effects:
int g(); // impure
int h(); // impure
and let's further assume that we impose a LTR order of evaluation, so
that:
f( g(), h() );
is guaranteed to evaluate g() first, then h().
On a "naturally LTR" machine, the compiler does everything in "source
code order", and (since this is an LTR machine) this is efficient.
On a "naturally RTL" machine, however, this would be inefficient...so
ALL function signatures are rewritten "in reverse," so that f() is
compiled *as if* it were declared:
int f( int b, int a );
and the call is rewritten:
f( h(), g() );
The compiler is then free to evaluate the arguments to f() (as
rewritten) in the "natural" RTL order...but behold!...RTL now means
that g() is executed before h(). Which is just what the programmer
expected.
(Adapting this scheme to variable length argument lists is left as an
exercise for the reader....)
The major practical obstacle, of course, is shared libraries...callers
& callees would need to be recompiled "together" to ensure that
everyone is using the same rewrite conventions. But that's not much
worse than any old ABI transition.
So...what am I missing?
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: bop@gmb.dk ("Bo Persson")
Date: Tue, 18 Apr 2006 02:50:44 GMT Raw View
"Nicolas Pavlidis" <aon.912719634@aon.at> skrev i meddelandet
news:4442b95c$0$12939$91cee783@newsreader02.highway.telekom.at...
> Hi!
>
> Again a disclaimer: I'm far away from beeing an expert, but I whant
> to
> take this thread to note some things, which seem anoying to me.
>
> NULL@NULL.NULL wrote:
>> Scott Meyers posted:
>>
>>> What work has been done on eliminating undefined behavior in C or
>>> C++?
>>> Off the top of my head, some undefined behavior is due to "stupid
>>> user
>>> behavior," e.g., indexing beyond the end of an array,
>>> dereferencing a
>>> null pointer, etc.
>>
>>
>> I agree. Stuff like the following is just "stupid":
>>
>> int *p;
>>
>> *p = 5; //Undefined Behaviour
>>
>
> This is one thing, and in that case its really stupid, but, the same
> behaviour can happen across longer code, with some function - calls
> inside, AFAIK this code is also undefined:
>
> int my_int; // missing initialisation
> cout << my_int << endl;
>
> Why is it impossible to let the compiler do the initialisation for
> me, I
> have to do it anyway, either in my code, where I can leave it otu,
> or,
> on the secure side, the compiler does tyhis thing for me.
Some compilers do warn you if you use uninitialized variables. That is
fine.
One expert argument against having the compiler "fix" broken code, is
that the code is old C-style anyway. In C++ the rule was changed, so
you generally can delay the declaration of the variable until you have
its initial value available. That solves the problem.
Also, in non-broken code, zero initializing all variables at the point
of declaration and then assigning them later, would make the old C
code run slower when compiled as C++. Not the best marketing trick!
>
>
>>> Other undefined behavior -- or maybe it's
>>> unspecified or implementation-defined behavior, I can never keep
>>> those
>>> terms straight -- arises from flexibility for implementers, e.g.,
>>> order
>>> of evaluating arguments for function calls, order of expression
>>> evaluation between sequence points, order of initialization of
>>> non-local statics defined in separate translation units.
>>
>> You're either writing:
>>
>> a) Portable 100% Standard-compliant Code
>
> Even if I do that, I can run into problems, think of
> std::list::front.
> If the list is empyt everythign can happen, again the question, why
> not
> pakking the code into the method. Why should the user must care
> about
> the inetrnal state of the list, ind this example.
Why should the programmer be bothered with the correctness of his
program? :-)
>
> he typemixing is another problem, that is solved by specific
> compilers,
> they warn you at least, why not forbis some things, you all have
> called
> stupid,if they are soo stupid?
This is all part of the C inheritance. The combined blessing and curse
of C++.
There are several other languages designed to avoid these problems.
Can you name one? Why not? Were they not successful?
Bo Persson
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: kanze.james@neuf.fr (James Kanze)
Date: Tue, 18 Apr 2006 02:53:39 GMT Raw View
Francis Glassborow wrote:
> In article <e1q8q9$1se$1@emma.aioe.org>, James Kanze
> <kanze.james@neuf.fr> writes
>> The unspecified order of evaluation can be a problem in a few
>> specific cases, but I can't see where the undefined behavior
>> of arithmetic overflow is a problem (in this regard -- it
>> does cause problems otherwise). Good programmers, regardless
>> of the language, don't write programs with integer overflow.
>> The "undefined behavior" aspect only comes into play with
>> regards to the reproducibility of tests, and in this case,
>> it's not really much of a problem in practice.
> 'Good programmers' =3D=3D 'Elite programmers' ? :-)
Good programmers =3D=3D well managed programmers. I've met very,
very few programmers who weren't capable of doing correct input
validation if the shop standards asked for it. I don't think it
is a problem of competence.
> And yes, I think you are right re the non-interaction of order
> of evaluation and overflow because the order of evaluation of
> operators is defined.
> However I remain profoundly uncomfortable with the fact that
> this simple program has undefined behaviour:
> #include <iostream>
> int main(){
> int i(2);
> int j(0);
> std::cout << "Please input a number: ";
> std::cin >> j;
> if(std::cin.good(){
Attention. If the input is redirected from a file, and the file
only contains a number (without a final newline), this test will
be false in almost every implementation -- in fact, I don't know
how to implement it to be true.
Don't be fancy. The standard idiom is either:
if ( std::cin >> j ) ...
or
std::cin >> j ;
if ( std::cin ) ...
I don't personally like it, but it is so ubiquious that I would
oppose any other solution. Especially as almost all of the
other solutions are generally wrong -- frankly, if someone as
competent as you can't get it right, it's probably better to
avoid the issue completely.
(For what it's worth, I'm pretty sure what you wanted to do is:
if ( ! std::cin::fail() )...
.)
> j *=3D 2;
> }
> else {
> std::cout << "That was not accepted as a number. \n";
> }
> }
> Yes I know how to clean that one up, and I know what
> assumptions I have made which should be made explicit.
> However every time I do arithmetic I have to go through the
> same thought and error trapping. IOWs the error trapping
> becomes part of the normal code.
Do you write real applications, or just demonstration programs
for training purposes. Real applications DO validate input.
Almost always. It's an almost automatic part of the process.
And real applications document the valid range of input values
for functions.
I agree that it would be better to define the behavior.
Overflow should, say, call a specific function, which by default
aborts the program (but can be replaced by the user to, say,
throw an exception). Or should it? : the IEEE propagates
special values, this design was carefully thought out, and so
presumably, in some cases, that would be a better solution. Or
better yet, the type changes dynamically to something like
BigInteger, so you always get the correct results, just slower.
(Well, in a extreme case, you get a bad_alloc exception.)
All of these have fairly high run-time cost on most current
hardware. Too high for many people or many applications. To
the point where Java standardized returning a wrong result --
probably worse than the undefined behavior we have in C++, since
it guarantees that the error must go unnoticed.
> That is not the C++ way, suspect operations throw exceptions
> or work for some definition of 'work' which might not be the
> one the programmer expected.
Or abort.
--=20
James Kanze kanze.james@neuf.fr
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France +33 (0)1 30 23 00 34
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: francis@robinton.demon.co.uk (Francis Glassborow)
Date: Tue, 18 Apr 2006 02:51:51 GMT Raw View
In article <9uSdnZJJpJGsBd_ZnZ2dnUVZ_v2dnZ2d@giganews.com>, Pete Becker
<petebecker@acm.org> writes
>> However every time I do arithmetic I
>> have to go through the same thought and error trapping.
>
>No, you don't. Once you've validated the input you don't have to keep
>rechecking it.
But input includes arguments and values returned from functions. It is
actually relatively rare that none of the data used in an arithmetic
expression is from some form of external (to the current function)
input.
>
>> IOWs the error
>> trapping becomes part of the normal code.
>
>Input validation ought to be part of the normal code. Anything else is too
>little, too late.
I see so I should never write:
(given the specifications of foo() and bar() are that they return a
positive int)
int func(int val){
int i = foo(val) + bar(val);
if(i < 0) throw error;
// rest of function
};
Programs should always avoid undefined behaviour, but other forms of
error can often be dealt with internally but undefined behaviour makes
that too late.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: kanze.james@neuf.fr (James Kanze)
Date: Tue, 18 Apr 2006 02:54:13 GMT Raw View
kuyper@wizard.net wrote:
> Pete Becker wrote:
>> Francis Glassborow wrote:
>>> However every time I do arithmetic I have to go through the
>>> same thought and error trapping.
>> No, you don't. Once you've validated the input you don't have
>> to keep rechecking it.
> That's true only if it's impossible for valid inputs to lead
> to overflow at any intermediate step in the calculations that
> depend upon them.
That's usually the definition of "valid". Or part of it, at
least.
> That's true in some problem domains, but not in all. If I'm
> writing a spreadsheat, and a user inserts a valid value in one
> cell, a valid value in another cell, and a valid formule which
> calculates their product the third cell, which user input am I
> supposed to reject as invalid to prevent having to check
> whether that product will result in an overflow?
A spreadsheet is a special case, in that it is, itself, a
programming language. Without knowing the operations to be
applied to a number, the program cannot validate it. It's the
job of the spreadsheet author to validate the input, just as in
C++, it is the job of the programmer, and not the compiler. A
well written spreadsheet will validate input.
(But of course, spreadsheets target a different set of
"programmers" than C++. And in a lot of cases, the spreadsheet
is a one-of, with all of the input embedded in the spreadsheet,
so the spreadsheet programmer doesn't have to worry about it.)
> The correct, defined, behavior for my spreadsheet is not to
> reject any of those three inputs, but rather to display, in
> the third cell, an indication that the calculation resulted in
> overflow.
Given the target audience, I'd have thought that the correct
behavior was to automatically shift to a type with a greater
range, so that the error didn't occur. Failing that, I agree.
In this particular case, you have to check at each operation.
(Given a complicated formula, the spread sheet could optimize,
by evaluating the input for which it would overflow, and
indicating overflow without attempting to evaluate the
expression for such input.)
The difference here is that you are defining the behavior in
case of overflow. Most programs don't want any overflow,
period, and prefer a contract which says that input which would
provoke overflow is not supported.
--=20
James Kanze kanze.james@neuf.fr
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France +33 (0)1 30 23 00 34
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: James Kanze <kanze.james@neuf.fr>
Date: Mon, 17 Apr 2006 21:56:45 CST Raw View
SuperKoko wrote:
> Francis Glassborow wrote:
>> In article <e1p3fi$bj2$1@emma.aioe.org>, James Kanze
>> <kanze.james@neuf.fr> writes
>>> That's what the standard says (and I suspect that Scott
>>> knows this). The problem Scott has is more likely knowing
>>> when each of the above applies. (There's also the problem
>>> that although the standard requires an implementation to
>>> document implementation defined behavior, to my knowledge
>>> none do.)
>> The problem is that it seems that the choices for
>> implementation defined behaviour must always be ones that
>> allow a program to continue execution.
>> The problem with this is that it makes overflow of signed and
>> floating point values undefined behaviour. If we allowed an
>> implementation to specify that, for example, overflow of an
>> int value results in the program terminating we could then
>> provide well-defined behaviour for all the cases where the
>> implementation does not go to that extreme.
> Theorically a compiler is allowed to document a good behaviour
> for undefined behaviour constructs. So, IMHO, all compilers
> that can (for example all compilers on x86, PowerPC, 680x0,
> Z80, and many other processors) have a correct behaviour
> should document it.
Except that the compilers I know don't have good behavior in the
case of integral overflow -- they silently give a wrong result.
[...]
> The problem is that an implementation is not required to
> define its behaviour. But I am not sure that crashing is
> allowed for implementation-defined behaviours, so I don't
> think we could change undefined behaviours to
> implementation-defined behaviours.
Implementation defined behavior must normally come from a set of
possible behaviors. If the standard defined one of these
behaviors as "raising an implementation defined signal", or
"aborting program execution in an implementation defined
manner", then the implementation can do so -- in the case of a
conversion of a signed type where the target type cannot contain
the value to be converted, the C standard explicitly says that
an implementation defined signal can be raised.
The problem with overflow of a numeric operator is somewhat more
difficult -- when the C standard was being written, a number of
processors (8086, 68000) used, or could use, external floating
point processors which operated in parallel. If configured to
trap on overflow (rare), the trap occured asynchronously with
the main flow of program execution. This would have made it
extremely difficult to specify behavior in such cases.
> However, since there are not so much "undefined behaviours" in
> the standard, and that writing a compiler is a huge work, I
> would like that ALL undefined behaviours be documented.
I would like for it all to be caught. At runtime, if not
earlier. At least in debug mode -- I can imagine that some
verifications are expensive enough that one would not (or could
not) leave them in release builds.
> Of course, doing things such as dereferencing the NULL pointer
> would be documented as "undefined" by the compiler.
Why? That one has very reproduceable behaviour on most modern
platforms.
Dereferencing an uninitialized pointer. Now that one would be
hard to specify.
> But, things such as integer overflow would probably be well
> documented by many compilers.
In practice, I think any compiler that did anything other than
"what the hardware does" would document this fact. And the
behavior is documented by the hardware manufacturers. At least,
I know what happens with all of the compilers I use.
> I would like to reduce, as far as possible,
> implementation-dependent behaviours.
> Two examples come in mind:
> The interpretation of multicharacters literals : i.e. 'abcd'
A difficult one. The obvious solution would be to ban them
entirely -- I think some compilers warn when they are used
anyway. But there is the issue of existing code which uses
them. Given their utility, I can't see adding rules which might
break this code.
> And, a much more important one:
> The order of initialization of global variables in multiple
> translation units.
This is a difficult one. In practice, the order of
initialization almost always depends on the order of linking.
And the standard doesn't address how the linker is invoked, so
it cannot say anything about the order of linking.
> The fact that it is unspecified is mainly due to the fact that
> the standard does not talk about order of modules (i.e. order
> of object files passed to the linker), and allows an
> implementation to initialize global variables when a module is
> "entered" from the first time.
There was also some vague goal to allow dynamic linking. Other
places in the standard are worded such that dynamic linking is
undefined behavior, though, so why here?
> IMHO, it is a bad idea, because portability is much harder.
> It would be better to specify that all static-storage duration
> objects are initialized before main is entered,
They are in practice, for statically linked modules.
> and, in the "order of modules" (as far as possible there would
> be good to find a mean to say more rigourously what is this
> order), and portable-code writers would use singletons or such
> workarounds in order to get the "behaviour" of lazy
> initialization.
There is, or was, existing practice for initializing in the
inverse order of link inclusion -- there is a logical
justification for this, in that a module from a library will
only be included if someone uses it (and thus, after that user).
Specifying this is almost impossible, however, since the order
of inclusion of modules from a library is so arbitrary.
There is currently a proposal before the C++ committee to add
modules to C++; a module uses specific other modules, and that
use provides an explicit partial ordering which must be
respected; cycles are detected and result in an error. The
proposal is based on Modula-2 (or at least looks very similar to
Modula-2). All that's lacking at present is an actual
implementation, so people can try it out, to be sure that there
aren't any hidden gotcha's. (Obviously, it would be very
irresponsible to standardize it without it at least having been
used in a prototype implementation. Even the best designs --
and the people behind this proposal are amongst the best I know
-- are subject to unexpected gotcha's. Usually easily
corrected. Unless they are cast in the stone of a standard.)
--
James Kanze kanze.james@neuf.fr
Conseils en informatique orient e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S mard, 78210 St.-Cyr-l' cole, France +33 (0)1 30 23 00 34
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: kanze.james@neuf.fr (James Kanze)
Date: Tue, 18 Apr 2006 02:57:10 GMT Raw View
Bo Persson wrote:
> ""Andrei Alexandrescu (See Website For Email)""
> <SeeWebsiteForEmail@erdani.org> skrev i meddelandet
> news:4442AF15.6000801@erdani.org...
> SuperKoko wrote:
[...]
> For most code, evaluation order does not matter. We also trust
> the compiler to find a good order for us. Even if we had a LTR
> order defined, in the absence of side effects the compiler
> could use the "as-if" rule to continue evaluation RTL.
> "Properly" written code would not be affected.
Agreed.
There are a few cases where at least some limits are in order.
Cases where exceptions are involved, for example. The real win,
however, is reproduceability.
>> If we look at things that way, it would be easy to reach an
>> agreement that a defined order of evaluation would be
>> desirable, and that the price to pay for that is negligible.
>> Right now, we're hurting too many for the doubtful benefit of
>> very few.
> I don't really agree that it is very desirable, as it is of no
> use to me.
You don't test your code in debug mode? And expect that the
results of those tests are valid in release mode as well?
The issue isn't so much one as whether a programmer should count
on the order of evaluation, in order to write unreadable
expressions with side effects in four or five different places.
The issue is whether the code has reproduceable behavior if he
accidentally does violate a coding guideline. If all of your
programmers are perfect, and never make mistakes, it's not a big
advantage. Otherwise, it's almost a necessity, at least if you
expect to release code compiled in release mode. (Note that
most of the places I've worked do NOT release optimized code,
precisely because of this.)
> I do agree that the cost is negligible, performance-wise. You
> just have to persuade the compiler writers. :-)
Actually, you have to persuade the corporate infrastructure who
pays the compiler writers:-). "It is required by the standard"
is an argument they can (almost) understand. "It will allow our
customers to write more robust software", no.
--=20
James Kanze kanze.james@neuf.fr
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France +33 (0)1 30 23 00 34
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: wade@stoner.com
Date: Mon, 17 Apr 2006 21:52:49 CST Raw View
Greg Herlihy wrote:
> A good starting point for investigating C++'s undefined (et al.)
> behaviors would be this paper: "An investigation of the unpredictable
> features of the C++ language." The authors actually take a complete
> inventory of every unpredictable behavior mentioned in the Standard,
> including both the C++ language and the Standard Library.
>
> Here are the grand totals from the paper's tally:
>
> Behavior Language Library Total
> Undefined 77 29 106
> Unspecified 25 25 50
> Indeterminate 5 0 5
> Implementation 58 23 81
> No diagnostic 18 0 18
>
> Here's a link to the paper:
> http://www.aitcnet.org/isai/Resources/C++%20language%20issues%20(unlimited%20and%20signed).pdf
>
> Well, I guess we can all be heartened by the fact that the Library has
> no indeterminate behavior.
I'd say the authors were a bit optimistic. Some things they didn't
mention:
=====
Every recent implementation of std::sort that I know of has O(n log n)
worst-case behavior, but the standard allows O(n*n). This is not
something you can really test for. You've got to ask your vendor (or
inspect his code).
No mention of the possibility of COW strings. This possibility means
you don't know if certain common operations are O(1) or O(n), or if
innocent-looking code is well defined or undefined.
======
My point is that people get used to implementations that perform better
than required by the standard, and start to take that performance for
granted. Some implementations of deque never invalidate references to
elements. Is that a feature, a potential trap for unwary programmers,
or ... ?
It is unspecified how long integer addition (without overflow) takes.
However, lots of us probably assume that the time is bounded by a small
constant (usually smaller than a memory access).
In their defense, the authors recommend avoiding STL (so they don't
really need to list all its uncertainties). Implementations that take
O(n) time to perform n+n are not going to be very popular/common.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: virtanea@isokalakotka.cs.tut.fi (Antti Virtanen)
Date: Tue, 18 Apr 2006 14:52:48 GMT Raw View
On 2006-04-15, James Kanze <kanze.james@neuf.fr> wrote:
> > When we couple unspecified order of evaluation with the
> > undefined nature of overflow of an int value we finish up with
> > forcing very tortuous code on programmers who do not need
> > portability but do need behaviour guarantees.
>
> The unspecified order of evaluation can be a problem in a few
> specific cases, but I can't see where the undefined behavior of
> arithmetic overflow is a problem (in this regard -- it does
> cause problems otherwise). Good programmers, regardless of the
> language, don't write programs with integer overflow.
And good athletes never trip over their feet in competitions,
since they have long time ago passed that point in their
training? I agree that good programmers rarely make that
mistake, but to say they never make that is wishful thinking.
Your argument about good programmers applies to almost all of the
specific undefined constructs of C++. Good programmers rarely fall
into those traps. However, not everyone is a good programmer and
currently C++ is very dangerous tool in the hands of intermediate
programmers. One of the most dangerous things is that it's
difficult to say whether a piece of code is broken or not.
> > I am well aware that the elite can program round this form of
> > undefined behaviour but requiring such avoidance seriously
> > reduces programmer productivity and adds too many
> > opportunities for buggy code.
>
> I don't understand the "elite" part -- code which might overflow
> doesn't get through code review, and it's not that difficult to
> avoid.
And a lot of software ships without much code reviews. If the
code is fundamentally broken and should never be used, IMHO the
compiler ought to warn about it, when possible. (The programmer
can then ignore the warning)
> (We are talking about integral arithmetic here.) And
> it's something that has to be done, so I don't see how it
> affects programmer productivity. (Typically, it's part of input
> data validation. And a negligeable part, at that.)
I agree that integer arithmetic is not the main concern here.
--
// Antti Virtanen -//- http://www.students.tut.fi/~virtanea
--
"Indeed, it is becoming increasingly clear that for almost all programs,
correctness rather than speed is the paramount concern."
Crafting a compiler in C, 1991
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: petebecker@acm.org (Pete Becker)
Date: Tue, 18 Apr 2006 14:53:31 GMT Raw View
Francis Glassborow wrote:
> In article <9uSdnZJJpJGsBd_ZnZ2dnUVZ_v2dnZ2d@giganews.com>, Pete Becker
> <petebecker@acm.org> writes
>
>>>However every time I do arithmetic I
>>>have to go through the same thought and error trapping.
>>
>>No, you don't. Once you've validated the input you don't have to keep
>>rechecking it.
>
>
> But input includes arguments and values returned from functions. It is
> actually relatively rare that none of the data used in an arithmetic
> expression is from some form of external (to the current function)
> input.
>
I guess I underestimated the possibilities of misinterpreting "input." I
meant input to the program, not whatever data you happen to be working
with at the time.
>
>>>IOWs the error
>>>trapping becomes part of the normal code.
>>
>>Input validation ought to be part of the normal code. Anything else is too
>>little, too late.
>
>
> I see so I should never write:
> (given the specifications of foo() and bar() are that they return a
> positive int)
>
> int func(int val){
> int i = foo(val) + bar(val);
> if(i < 0) throw error;
> // rest of function
> };
>
There's no program here, just a fragment. Where did val come from, and
what are the full specifications for foo and bar? Validating input means
doing the work of tracing data flows. That's part of what's referred to
in the literature as "low level design."
> Programs should always avoid undefined behaviour, but other forms of
> error can often be dealt with internally but undefined behaviour makes
> that too late.
>
Undefined behavior combined with lack of data analysis makes error
checking impossible. But it's not the undefined behavior that causes the
problem. It's the lack of data analysis.
To go back to your original example:
#include <iostream>
int main(){
int i(2);
int j(0);
std::cout << "Please input a number: ";
std::cin >> j;
if(std::cin.good(){
j *= 2;
}
else {
std::cout << "That was not accepted as a number. \n";
}
}
Change j *= 2 to j *= INT_MAX. Does your analysis change? Now the only
valid inputs to your program are -1, 0, and 1. Are you still not going
to check that you got valid input?
--
Pete Becker
Roundhouse Consulting, Ltd.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: petebecker@acm.org (Pete Becker)
Date: Tue, 18 Apr 2006 14:53:55 GMT Raw View
Francis Glassborow wrote:
>
> I see so I should never write:
> (given the specifications of foo() and bar() are that they return a
> positive int)
>
> int func(int val){
> int i = foo(val) + bar(val);
> if(i < 0) throw error;
> // rest of function
> };
>
If all you know is that foo and bar return a positive value, then your
code is obviously wrong, because it doesn't handle overflow. The
solution is simple: get rid of the possibility of overflow.
unsigned i = (unsigned)foo(val) + bar(val);
If you actually know what foo and bar do (which you really ought to know
before calling them) there may be other approaches, as well. In the
absence of real specifications, of course, it's impossible to do any
real analysis. That's not a language problem.
--
Pete Becker
Roundhouse Consulting, Ltd.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "kanze" <kanze@gabi-soft.fr>
Date: Tue, 18 Apr 2006 09:55:36 CST Raw View
Francis Glassborow wrote:
> In article <9uSdnZJJpJGsBd_ZnZ2dnUVZ_v2dnZ2d@giganews.com>, Pete Becker
> <petebecker@acm.org> writes
> >> However every time I do arithmetic I have to go through the
> >> same thought and error trapping.
> >No, you don't. Once you've validated the input you don't have
> >to keep rechecking it.
> But input includes arguments and values returned from
> functions. It is actually relatively rare that none of the
> data used in an arithmetic expression is from some form of
> external (to the current function) input.
For functions, you specify the range of valid values of the
parameters. As part of the contract. If pre-condition checking
is on, you check. If it isn't, you don't; you count on the
client not having send a bad value. The client knows your range
of valid values, and uses it when determining his range. And so
up, up the call tree. At some point, you do get to user input,
and at that point, you know the range of valid values which will
not cause overflow. And you check for it. Always -- unlike the
pre-condition checks in the lower levels, you can't turn that
checking off.
> >> IOWs the error trapping becomes part of the normal code.
> >Input validation ought to be part of the normal code.
> >Anything else is too little, too late.
> I see so I should never write: (given the specifications of
> foo() and bar() are that they return a positive int)
> int func(int val){
> int i = foo(val) + bar(val);
> if(i < 0) throw error;
> // rest of function
> };
If that's the only specification of foo() and bar(), no. I've
never seen functions specified with nothing more than "returns a
positive int"; rand() comes pretty close, and you often do
either check or use modulo or something like that on the return
value of rand(), to be sure that you don't run into overflow
later on.
And of course, if overflow occurs, the check if i < 0 comes too
late. If you don't know more about foo and bar, you check their
return values before doing the addition. (But as I said, it's
pretty rare that you don't know more about foo and bar... and
that it makes sense to add them -- functions like the Unix open
do return a positive int (or -1 if the function fails), but I've
never wanted to add a value returned from it.)
--
James Kanze GABI Software
Conseils en informatique orient e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S mard, 78210 St.-Cyr-l' cole, France, +33 (0)1 30 23 00 34
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: aon.912719634@aon.at (Nicolas Pavlidis)
Date: Tue, 18 Apr 2006 16:57:31 GMT Raw View
Bo Persson wrote:
> "Nicolas Pavlidis" <aon.912719634@aon.at> skrev i meddelandet
> news:4442b95c$0$12939$91cee783@newsreader02.highway.telekom.at...
>> Hi!
>>
>> Again a disclaimer: I'm far away from beeing an expert, but I whant
>> to
>> take this thread to note some things, which seem anoying to me.
>>
>> NULL@NULL.NULL wrote:
>>> Scott Meyers posted:
>>>
>>>> What work has been done on eliminating undefined behavior in C or
>>>> C++?
>>>> Off the top of my head, some undefined behavior is due to "stupid
>>>> user
>>>> behavior," e.g., indexing beyond the end of an array,
>>>> dereferencing a
>>>> null pointer, etc.
>>>
>>> I agree. Stuff like the following is just "stupid":
>>>
>>> int *p;
>>>
>>> *p = 5; //Undefined Behaviour
>>>
>> This is one thing, and in that case its really stupid, but, the same
>> behaviour can happen across longer code, with some function - calls
>> inside, AFAIK this code is also undefined:
>>
>> int my_int; // missing initialisation
>> cout << my_int << endl;
>>
>> Why is it impossible to let the compiler do the initialisation for
>> me, I
>> have to do it anyway, either in my code, where I can leave it otu,
>> or,
>> on the secure side, the compiler does tyhis thing for me.
>
> Some compilers do warn you if you use uninitialized variables. That is
> fine.
That is what I mean. Why not all? If I like to use uninitialized
variables, I can use them and I'm able to ignore or even supress the
warning.
But if I don't like them, but write code that uses them I've the
opportunity to get reid of them in my code, and yes, I don't liek them :-).
> One expert argument against having the compiler "fix" broken code, is
> that the code is old C-style anyway. In C++ the rule was changed, so
> you generally can delay the declaration of the variable until you have
> its initial value available. That solves the problem.
I don't thik so :-). Not only old C - COde is affected by these
problems, also "new" C++ - code is affected, because it just hapen, if
you want or not, either if you are lazy or if you just miss one stupid
initialisazion :-(.
> Also, in non-broken code, zero initializing all variables at the point
> of declaration and then assigning them later, would make the old C
> code run slower when compiled as C++. Not the best marketing trick!
Sorry, in any case I have to write somehting before I read it or not?
And the biggest Problems ar not the loop - counters for for - loops.
What I mean are struct - mebers for example, why I'm forced to do the
inistalisazion for private - members to zero (in case of an int)?
>> Even if I do that, I can run into problems, think of
>> std::list::front.
>> If the list is empyt everythign can happen, again the question, why
>> not
>> pakking the code into the method. Why should the user must care
>> about
>> the inetrnal state of the list, ind this example.
>
> Why should the programmer be bothered with the correctness of his
> program? :-)
Why not :-)?
>
>> he typemixing is another problem, that is solved by specific
>> compilers,
>> they warn you at least, why not forbis some things, you all have
>> called
>> stupid,if they are soo stupid?
>
> This is all part of the C inheritance. The combined blessing and curse
> of C++.
:-)
> There are several other languages designed to avoid these problems.
> Can you name one? Why not? Were they not successful?
Which type of language to you mean? As powerful as C++ or some other
language? :-).
Best regards,
Nicolas
P.S. Yes I like C++ :-)
--
| Nicolas Pavlidis | Elvis Presly: |
| Student of SE & KM | "Into the goto" |
| pavnic@sbox.tugraz.at | ICQ #320057056 |
| ------------University of Technology, Graz-----------------
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "SuperKoko" <tabkannaz@yahoo.fr>
Date: Tue, 18 Apr 2006 18:17:39 CST Raw View
Nicolas Pavlidis wrote:
> Sorry, in any case I have to write somehting before I read it or not?
> And the biggest Problems ar not the loop - counters for for - loops.
> What I mean are struct - mebers for example, why I'm forced to do the
> inistalisazion for private - members to zero (in case of an int)?
>
It could be possible to specify that all POD types are
default-initialized (default-initialization of POD types is
zero-initialization) if not explicitly constructed in the constructor's
initializer list.
And, why not default-construction for automatic variables?
As far as I know, compilers could optimize the initialization if the
initial value is "never used".
Similarly, in constructors, if the value is assigned in the constructor
body, the compiler could optimize the initialization.
Overhead would be very small (zero-overhead most of the time), except
for POD arrays and big POD structures.
For arrays, the overhead could be very significant.
The standard could specify that arrays of POD types are not
default-initialized.
Or perhaps that builtin types are default-initialized, but not compound
types (POD-struct/union/arrays).
That would look weird and ugly, but that is probably an acceptable
compromise.
> >> Even if I do that, I can run into problems, think of
> >> std::list::front.
> >> If the list is empyt everythign can happen, again the question, why
> >> not
> >> pakking the code into the method. Why should the user must care
> >> about
> >> the inetrnal state of the list, ind this example.
> >
> > Why should the programmer be bothered with the correctness of his
> > program? :-)
>
> Why not :-)?
That is a pure logical error, so runtime diagnosis is a plus, mainly in
debug mode, but not a requirement.
If you want, you can use a "debug" STL, having numerous bound
checkings, consistency checkings, etc., in order to get exceptions
instead of an undefined behaviour.
Overhead would be more or less serious, but if speed don't matter, you
can use a STL with "debug" checks in release mode.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: johnchx2@yahoo.com
Date: Tue, 18 Apr 2006 22:56:49 CST Raw View
Nicolas Pavlidis wrote:
> Even if I do that, I can run into problems, think of std::list::front.
> If the list is empyt everythign can happen, again the question, why not
> pakking the code into the method. Why should the user must care about
> the inetrnal state of the list, ind this example.
This question highlights one of the sources of undefined behavior in
C++ that may not have been mentioned yet in this thread: low-level
components which delegate error checking to higher level components for
efficiency reasons...because higher-level code should know when it is
safe *not* to check.
Consider a (made up) function like:
void fill_list(std::list<int>& li, int n);
which is defined to append n random integers to the list, or, if it
cannot, to throw an exception. In my code:
std::list<int> ints;
fill_list( ints, 10 );
int i = ints.front(); // line A
I *know* that ints *cannot* be empty when line A is executed (assuming
fill_list() fulfills its contract), so there is no need to test
ints.size() > 0, to verify this. But if front() built in an automatic
test, I'd have to pay the performance penalty anyway.
Of course a language (or library) designer might prefer a different
trade-off: a small performance hit in return for more robustness. C++
doesn't take this path, in part because it is possible to build a safe,
slow component on top of a fast, unsafe one, but not vice versa. In
other words, you can easily implement a "checking list" in terms of a
std::list, but you can't go the other direction.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "ThosRTanner" <ttanner2@bloomberg.net>
Date: Wed, 19 Apr 2006 09:54:41 CST Raw View
"Bo Persson" wrote:
> "ThosRTanner" <ttanner2@bloomberg.net> skrev i meddelandet
> news:1145261505.465774.326140@z34g2000cwc.googlegroups.com...
> >
> >
> > I think to that it would be worth adding instances where behaviour
> > isn't undefined but at best unhelpful and arguably completely
> > stupid,
> > such as:
> >
> > Inheriting from a class with a non-virtual destructor - this is well
> > defined, but can break code in a really nasty way.
>
> It's nasty only if you delete an object through a pointer to base
> class. I never ever do.
>
> Should my code suffer from other's stupidity? :-)
In what way would your code suffer? I'm glad that you can ensure that
you never do something like that, but other people do. And other people
might end up maintaining your code.
> >
> > Incorrectly overriding virtual functions (again, the behaviour is
> > well
> > defined, but results in a source of almost endless confusion)
>
> How is the compiler to know when a function is hiding, instead of
> overriding, a base class function (without breaking existing code) ?
Some compilers at least know enough to give warnings.
Again, it is a dangerous thing to do. All sorts of nastiness can ensue
if you change the base class for starters. Quite a lot of nastiness can
happen if you define something as int in the base class and unsigned in
the derived class (typos happen).
> >
> > <bang drum> throwing an exception that isn't in a clients throw()
> > spec
> > and isn't caught by the client</bang>
>
> The compiler is allowed, but not required, to warn about this. In the
> general case, it is not possible to detect all problems.
>
> void g();
>
> void f() throw(std::bad_alloc)
> {
> g();
> }
>
> Is this code ok?
No, it is not. You have implicitly stated that g() can throw anything
(because that is what the absence of a throw spec means). Therefore
this is a valid implementation of g
void g()
{
throw "This will crash";
}
This will crash your program. A trivial change in g, or perhaps
something that g calls, can result in program crashes that could have
been detected at compile time.
>
> > All of these can be detected by the compiler.
>
> That's the problem. They can *sometimes* be detected by the compiler.
> What happens when they cannot?
Please provide an instance where a conformant compiler cannot detect
with 100% accuracy whether the throw spec for a function being called
is "looser" than the throw spec for the calling function.
All functions have throw specifications (a lack of throw spec means you
can throw anything).
A function must have a prototype in order for it to be used.
A compiler can therefore trivially determine if the throw specification
of the called function is looser than the throw specification of the
calling function.
If you mean the compiler can't detect whether or not the called
function does actually throw something specified in its throw
specification, well, I have to say - so what? You can always correct
the throw spec, or put a try/catch block. It's a bit like saying "the
compiler can't tell if the function will return an error code, so I'll
just ignore the value". Fine most of the time, but when it goes wrong,
it goes badly wrong.
OK, I do accept that people might want a method of indicating this to
get round the fact that existing broken code happily compiles because
the worst "never happens" (until it does happen, resulting in hours of
debugging and head scratching), and would be happy to see something
like 'throw static(<list>)' which would be checked by the compiler.
But I'd still like destructors (and extern "C") to be implicitly "throw
static ()"
>
> Bo Persson
>
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "ThosRTanner" <ttanner2@bloomberg.net>
Date: Wed, 19 Apr 2006 09:54:55 CST Raw View
SuperKoko wrote:
> ThosRTanner wrote:
>
> > I think to that it would be worth adding instances where behaviour
> > isn't undefined but at best unhelpful and arguably completely stupid,
> > such as:
> >
> > Inheriting from a class with a non-virtual destructor - this is well
> > defined, but can break code in a really nasty way.
>
> This can be solved by compiler warnings (GCC emit a warning when
> defining a class containing virtual methods, but no virtual
> destructor).
One day I'd like to work in an organisation that took compiler warnings
seriously. The usual rule is "if it doesn't stop the build, it isn't
worth worrying about".
> This could also be solved by specifying that any class having one or
> more virtual method has implicitly, a virtual destructor (overhead
> would be very small).
On the other hand, you might not want your class to be inherited from.
Not having a virtual destructor would be a way of ensuring that.
>
> >
> > Incorrectly overriding virtual functions (again, the behaviour is well
> > defined, but results in a source of almost endless confusion)
>
> A few compilers provide an __override keyword that solves that problem.
>
> class Base
> {
> public:
> virtual void func(long x);
> };
> class Derived:public Base
> {
> public:
> virtual void func(int x) __override; // error : must override a virtual
> method of a base class.
> };
>
> If you want, you can propose such addition to C++0x
>
> > <bang drum> throwing an exception that isn't in a clients throw() spec
> > and isn't caught by the client</bang>
> IMHO, that could not be sensibly specified in the standard.
Well it could. Currently it's specified as a runtime check, but the
information is there for it to be a compile time check.
But people don't use throw specifications because the runtime check
makes them unusable in systems where abnormal termination is not an
option.
> But, surely, compilers could emit warnings when the error is
> diagnosable.
Well, if the compilers checked exception specs at compile time, they
could produce more efficient code, coz they wouldn't have to catch all
exceptions and determine whether or not they needed to terminate.
Anyway, as stated above, the general rule appears to be that compiler
warnings are for wimps.
> Similarly, misuse of the assignment in conditions:
>
> if (a=b){
> }
>
> Is detected by many compilers.
And there is an unambigous rewrite of that - in fact, there are several
ditto ditto.
>
> should the standard define a minimal set of warning messages? Perhaps.
It should require diagnostics for all places where behaviour is
undefined, certainly.
But then - if it doesn't stop the build, it's not important.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: aon.912719634@aon.at (Nicolas Pavlidis)
Date: Wed, 19 Apr 2006 16:20:16 GMT Raw View
johnchx2@yahoo.com wrote:
> Nicolas Pavlidis wrote:
>
>> Even if I do that, I can run into problems, think of std::list::front.
>> If the list is empyt everythign can happen, again the question, why not
>> pakking the code into the method. Why should the user must care about
>> the inetrnal state of the list, ind this example.
>
> This question highlights one of the sources of undefined behavior in
> C++ that may not have been mentioned yet in this thread: low-level
> components which delegate error checking to higher level components for
> efficiency reasons...because higher-level code should know when it is
> safe *not* to check.
>
> Consider a (made up) function like:
>
> void fill_list(std::list<int>& li, int n);
>
> which is defined to append n random integers to the list, or, if it
> cannot, to throw an exception. In my code:
>
> std::list<int> ints;
> fill_list( ints, 10 );
> int i = ints.front(); // line A
>
> I *know* that ints *cannot* be empty when line A is executed (assuming
> fill_list() fulfills its contract), so there is no need to test
> ints.size() > 0, to verify this. But if front() built in an automatic
> test, I'd have to pay the performance penalty anyway.
In this case you know, that the list isn't empty, but there are also
cases where you can not be so sure, if you use some external librray for
example. And IMHO it''s not that preformance leak, if I say:
if(size != 0) {// dosomehting}
or is this code soo slow?
Or I can use templates, to specifiy if I want a check or not, thats the
next posibility.
Or I do the check only in the debug build.
There a lot posibilities to solve this problem, IMHO.
Best regards,
Nicolas
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "SuperKoko" <tabkannaz@yahoo.fr>
Date: Wed, 19 Apr 2006 12:18:14 CST Raw View
ThosRTanner wrote:
> All functions have throw specifications (a lack of throw spec means you
> can throw anything).
>
> A function must have a prototype in order for it to be used.
>
> A compiler can therefore trivially determine if the throw specification
> of the called function is looser than the throw specification of the
> calling function.
>
Okay, I'll do a proposal:
The first problem here, is that the standard doesn't require
exception-specification-correctness... but we'll see that it is not a
real problem.
So, it is perfectly valid to call a function having a looser exception
specification from a function having a stronger exception specification
without explicitly catching it, as long as the called function never
throws (and if it throws, the compiler must catch it as an unexpected
exception).
What means that, you can write that in a shared library (so the
compiler can't know the code of that library):
// in the shared library (whose code is not accessible)
int g(int a,int b)
{
return a+b; // in fact, it can't throw, even if the exception
specification says that it can.
// that is not a problem, but...
}
// in the compiled module:
int g(int a,int b);
int f() throw()
{
g(5,3); // that is compliant C++ code.
}
You see that the compiler have to put implicit try/catch blocks,
because it can't whether g can throw or not.
That has a performance penalty : I claim that actually,
exception-specification incorrect programs have performance penalties
compared to programs using absolutely no exception-specification (which
is ... the worst level of exception-specification incorrectness).
Actually, here is what current compilers may do, in order to avoid any
penalty in exception-specification correct programs:
Let f be a function having an exception specification that allows a
number of exceptions to escape (from zero to infinite).
The compiler can diagnose perfectly (without looking in the code of
called functions) whether the function may let escape an unexpected
exception.
If that is not the case, the function is, indeed,
exception-specification correct:
For example:
void f(); // might throw
void g()throw()
void h() throw() // exception-specification correct function : zero
overhead
{
try
{
f();
}
catch(...) {}
g();
}
void i() throw() // exception-specification incorrect function : the
compiler will put implicit try/catch blocks.
{
f();
}
1) There absolutely no overhead if a program is perfectly
exception-specification correct.
2) The compiler can perfectly detect at compile-time whether a function
is exception-specification correct or not, including the trivial
example:
void f() throw()
{
throw "he he!";
}
I'd like that C++ requires exception-specification correctness.
C++ requires const-correctness! Why not exception-specification
correctness?
In fact, if the standard required a statically provable
exception-specification correctness, there would be a huge benefit : It
would be impossible to have an unexpected exception!
The compiler would make a compile-time error!
In fact, here is what is actually possible : compilers could emit a
warning message when a function is not exception-specification correct.
If the standard accepts ths proposal... it would become an error.
If we do an analogy with const-correctness, we see that it would be
stupid that the compiler allow a const member function to call a
non-const member function without even a warning... and a runtime error
if... the actual data is modified...
> If you mean the compiler can't detect whether or not the called
> function does actually throw something specified in its throw
> specification, well, I have to say - so what? You can always correct
> the throw spec, or put a try/catch block. It's a bit like saying "the
> compiler can't tell if the function will return an error code, so I'll
> just ignore the value". Fine most of the time, but when it goes wrong,
> it goes badly wrong.
You are right... The compiler can detect perfectly at compile time
>
> OK, I do accept that people might want a method of indicating this to
> get round the fact that existing broken code happily compiles because
> the worst "never happens" (until it does happen, resulting in hours of
> debugging and head scratching), and would be happy to see something
> like 'throw static(<list>)' which would be checked by the compiler.
>
> But I'd still like destructors (and extern "C") to be implicitly "throw
> static ()"
I agree
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: johnchx2@yahoo.com
Date: Wed, 19 Apr 2006 14:17:08 CST Raw View
Nicolas Pavlidis wrote:
> In this case you know, that the list isn't empty, but there are also
> cases where you can not be so sure, if you use some external librray for
> example.
Of course. And only you, the caller, know which situation you're in.
> And IMHO it''s not that preformance leak, if I say:
>
> if(size != 0) {// dosomehting}
>
> or is this code soo slow?
Not at all, if the check is needed in this context.
Perhaps I misunderstood your post...when you wrote:
>> If the list is empyt everythign can happen, again the question, why
>> not pakking the code into the method. Why should the user must
>> care about the inetrnal state of the list, ind this example.
I thought that you were asking why the std::list<T>::front() method
isn't required to check for the empty-list condition for you. My point
was that the library implemention, in general, can't tell whether the
check is necessary or redundant.
>
> Or I can use templates, to specifiy if I want a check or not, thats the
> next posibility.
>
> Or I do the check only in the debug build.
>
> There a lot posibilities to solve this problem, IMHO.
>
Yes, I agree. But none of them require a change to the standard's
definition of std::list itself.
(There actually is a precedent for adding a formally redundant
error-checked operation in a standard library container:
std::vector<T>::at(). But I don't get the impression that there's a
lot of support for adding more of the same.)
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Bo Persson" <bop@gmb.dk>
Date: Wed, 19 Apr 2006 22:59:21 CST Raw View
"ThosRTanner" <ttanner2@bloomberg.net> skrev i meddelandet
news:1145436601.054028.10610@e56g2000cwe.googlegroups.com...
>
> "Bo Persson" wrote:
>> "ThosRTanner" <ttanner2@bloomberg.net> skrev i meddelandet
>> news:1145261505.465774.326140@z34g2000cwc.googlegroups.com...
>> >
>> >
>> > I think to that it would be worth adding instances where
>> > behaviour
>> > isn't undefined but at best unhelpful and arguably completely
>> > stupid,
>> > such as:
>> >
>> > Inheriting from a class with a non-virtual destructor - this is
>> > well
>> > defined, but can break code in a really nasty way.
>>
>> It's nasty only if you delete an object through a pointer to base
>> class. I never ever do.
>>
>> Should my code suffer from other's stupidity? :-)
> In what way would your code suffer? I'm glad that you can ensure
> that
> you never do something like that, but other people do. And other
> people
> might end up maintaining your code.
My code would suffer from always having virtual destructors, even when
I have no use for it. If you don't delete objects through a pointer to
base class, there is no problem.
>> >
>> > <bang drum> throwing an exception that isn't in a clients throw()
>> > spec
>> > and isn't caught by the client</bang>
>>
>> The compiler is allowed, but not required, to warn about this. In
>> the
>> general case, it is not possible to detect all problems.
>>
>> void g();
>>
>> void f() throw(std::bad_alloc)
>> {
>> g();
>> }
>>
>> Is this code ok?
> No, it is not. You have implicitly stated that g() can throw
> anything
> (because that is what the absence of a throw spec means). Therefore
> this is a valid implementation of g
>
> void g()
> {
> throw "This will crash";
> }
>
> This will crash your program. A trivial change in g, or perhaps
> something that g calls, can result in program crashes that could
> have
> been detected at compile time.
>
>>
>> > All of these can be detected by the compiler.
>>
>> That's the problem. They can *sometimes* be detected by the
>> compiler.
>> What happens when they cannot?
> Please provide an instance where a conformant compiler cannot detect
> with 100% accuracy whether the throw spec for a function being
> called
> is "looser" than the throw spec for the calling function.
Try this one:
void g(int x)
{
if (x > 5)
throw "parameter error";
}
void f(int x) throw()
{
if (x < 5)
g(x);
}
Will that compile?
Or
template<class T>
void f() throw()
{
T::g();
}
>
> All functions have throw specifications (a lack of throw spec means
> you
> can throw anything).
Which includes most of the standard library. Should you not be allowed
to use that?
Bo Persson
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: aon.912719634@aon.at (Nicolas Pavlidis)
Date: Thu, 20 Apr 2006 04:01:48 GMT Raw View
johnchx2@yahoo.com wrote:
> Nicolas Pavlidis wrote:
>
>> In this case you know, that the list isn't empty, but there are also
>> cases where you can not be so sure, if you use some external librray for
>> example.
>
> Of course. And only you, the caller, know which situation you're in.
>
>> And IMHO it''s not that preformance leak, if I say:
>>
>> if(size != 0) {// dosomehting}
>>
>> or is this code soo slow?
>
> Not at all, if the check is needed in this context.
>
> Perhaps I misunderstood your post...when you wrote:
No you didn't.
>>> If the list is empyt everythign can happen, again the question, why
>>> not pakking the code into the method. Why should the user must
>>> care about the inetrnal state of the list, ind this example.
>
> I thought that you were asking why the std::list<T>::front() method
> isn't required to check for the empty-list condition for you. My point
> was that the library implemention, in general, can't tell whether the
> check is necessary or redundant.
My Opiniton was to do the check inside the std::List::front() method,
because is does not slow down the list, as you agreed :-).
>
>> Or I can use templates, to specifiy if I want a check or not, thats the
>> next posibility.
>>
>> Or I do the check only in the debug build.
>>
>> There a lot posibilities to solve this problem, IMHO.
>>
>
> Yes, I agree. But none of them require a change to the standard's
> definition of std::list itself.
>
> (There actually is a precedent for adding a formally redundant
> error-checked operation in a standard library container:
> std::vector<T>::at(). But I don't get the impression that there's a
> lot of support for adding more of the same.)
I think the best solution was taken, for the iostream - implementation,
here only some flags are set in some error conditions and if I want I
can turn on exceptions for iostreams, and if not I can call the
appreciate functions or can still ignore the errors.
This is IMHO the most general way, because I can really decide what to do.
Maybe this will be more overhead than simply comparing size to non zero,
but it is general.
Comparing to non zero is not general but more efficient and secure,
doing nothing is only efficient, but that not enough IMHO, but it sesms
to me that I'm a bit alone with my opinion :-).
Best regards,
Nicolas
--
| Nicolas Pavlidis | Elvis Presly: |
| Student of SE & KM | "Into the goto" |
| pavnic@sbox.tugraz.at | ICQ #320057056 |
| ------------University of Technology, Graz-----------------
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: brangdon@cix.co.uk (Dave Harris)
Date: Thu, 20 Apr 2006 04:01:16 GMT Raw View
===================================== MODERATOR'S COMMENT:
(Posts approved for the group are unmodified from how they are
submitted.)
===================================== END OF MODERATOR'S COMMENT
==============
This is a resubmission of an article which was previously rejected with
the following note:
This would make more sense if you had cin >> x in place of each
cin << x -- I'm rejecting it as a courtesy to you, to give you a chance
to resubmit. If you prefer to post as is, please resubmit it anyway --
having rejected it, I can't then approve it. Sorry if you'd have
preferred it just to be approved! -- James
I don't understand the note. My article did not contain "cin" or "cin <<
x". It did contain "cout << x", and it made sense like that. I've added a
comment to make it clearer that the undefined behaviour is intentional.
Feel free to delete this explanation before sending it to the group.
==============
bop@gmb.dk ("Bo Persson") wrote (abridged):
> In C++ the rule was changed, so
> you generally can delay the declaration of the variable until you have
> its initial value available. That solves the problem.
Not really. There remain cases like:
struct Demo {
int x;
Demo() {
cout << x; // Undefined behaviour.
}
};
> Also, in non-broken code, zero initializing all variables at the point
> of declaration and then assigning them later, would make the old C
> code run slower when compiled as C++. Not the best marketing trick!
That would depend on the optimiser. Any modern optimiser should eliminate
redundant assignments like:
int x = 0;
x = 0;
cout << x;
That said, I think there would need to be a way to request the
uninitalised variable where performance matters more than speed. For
example:
struct Demo {
int x, y;
Demo() : y(?) {
// Here x is 0, and y uninitialised.
}
};
The programmer should have the choice, and the default should be safety
rather than performance.
-- Dave Harris, Nottingham, UK.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: aon.912719634@aon.at (Nicolas Pavlidis)
Date: Thu, 20 Apr 2006 15:17:59 GMT Raw View
ThosRTanner wrote:
> SuperKoko wrote:
> But people don't use throw specifications because the runtime check
> makes them unusable in systems where abnormal termination is not an
> option.
There is a great problem with exception - spec, and it is named
templates :-).
You can not react to exception specifications in templates, so it is
better IMHO to leave them out, if you use templates.
>> But, surely, compilers could emit warnings when the error is
>> diagnosable.
>
> Well, if the compilers checked exception specs at compile time, they
> could produce more efficient code, coz they wouldn't have to catch all
> exceptions and determine whether or not they needed to terminate.
> Anyway, as stated above, the general rule appears to be that compiler
> warnings are for wimps.
>
>> Similarly, misuse of the assignment in conditions:
>>
>> if (a=b){
>> }
>>
>> Is detected by many compilers.
> And there is an unambigous rewrite of that - in fact, there are several
> ditto ditto.
This thing will be recognized, at least by the customer :-).
Best Regards,
Nicolas
--
| Nicolas Pavlidis | Elvis Presly: |
| Student of SE & KM | "Into the goto" |
| pavnic@sbox.tugraz.at | ICQ #320057056 |
| ------------University of Technology, Graz-----------------
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: kuyper@wizard.net
Date: Thu, 20 Apr 2006 10:18:39 CST Raw View
Bo Persson wrote:
> "ThosRTanner" <ttanner2@bloomberg.net> skrev i meddelandet
> news:1145436601.054028.10610@e56g2000cwe.googlegroups.com...
> >
> > "Bo Persson" wrote:
.
> >> > All of these can be detected by the compiler.
> >>
> >> That's the problem. They can *sometimes* be detected by the
> >> compiler.
> >> What happens when they cannot?
> > Please provide an instance where a conformant compiler cannot detect
> > with 100% accuracy whether the throw spec for a function being
> > called
> > is "looser" than the throw spec for the calling function.
>
> Try this one:
>
> void g(int x)
> {
> if (x > 5)
> throw "parameter error";
> }
>
> void f(int x) throw()
> {
> if (x < 5)
> g(x);
> }
>
> Will that compile?
The throw spec for g() allows it to throw anything, which is
unambiguously looser than the throw spec for f(), which is not allowed
to throw anything, and this fact is trivially detectable by the
compiler at the point where g() is called. How does this count as a
response to a challenge to provide 'an instance where a conformant
compiler cannot detect with 100% accuracy whether the throw spec for a
function being called is "looser" than the throw spec for the calling
function.' ?
I suspect that you're misreading the proposal. It's the throw
specification of the called function that is being compared with the
throw specification of the calling function. It doesn't matter whether
or not the called function actually throws an exception inconsistent
with the calling function's specification. All that matters is whether
the called function has a specification that would allow it to throw
such an exception.
> template<class T>
> void f() throw()
> {
> T::g();
> }
Whether or not the throw specification of g() is looser than that of
f() can not be determined at the time of the template definition, but
it's also true that you can't even determine whether or not g()
actually exists at that point. Both the existence of g(), and it's
possession of a looser throw specification f()'s, can trivially be
detected with 100% accuracy at the point of instantiation of f<T>;
assuming that the instantiation is legal (which requires that the
definition of T be in visible).
> > All functions have throw specifications (a lack of throw spec means
> > you
> > can throw anything).
>
> Which includes most of the standard library. Should you not be allowed
> to use that?
The proposed change would still allow you to call such functions; it
would just require you to either catch all of the exceptions that might
be thrown by such a function, or to give your own function a completely
open throw spec.
There are many valid arguments that could be made against this
proposal, such as the fact that it would break a lot of existing code,
but the infeasibility of implementation isn't one of them.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Seungbeom Kim <musiphil@bawi.org>
Date: 20 Apr 2006 15:30:05 GMT Raw View
Dave Harris wrote:
> That said, I think there would need to be a way to request the
> uninitalised variable where performance matters more than speed. For
> example:
>
> struct Demo {
> int x, y;
> Demo() : y(?) {
> // Here x is 0, and y uninitialised.
> }
> };
>
> The programmer should have the choice, and the default should be safety
> rather than performance.
You do:
struct Demo {
int x, y;
Demo() : x() {
// Here x is value-initialized to 0, and y is uninitialized
}
};
See 12.6.2 for details.
--
Seungbeom Kim
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: SeeWebsiteForEmail@erdani.org ("Andrei Alexandrescu (See Website For Email)")
Date: Thu, 20 Apr 2006 16:30:48 GMT Raw View
Dave Harris wrote:
> That said, I think there would need to be a way to request the
> uninitalised variable where performance matters more than speed. For
> example:
>
> struct Demo {
> int x, y;
> Demo() : y(?) {
> // Here x is 0, and y uninitialised.
> }
> };
>
> The programmer should have the choice, and the default should be safety
> rather than performance.
That's a great idea. I aired it, too, with little echo, sigh.
Andrei
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: brangdon@cix.co.uk (Dave Harris)
Date: Fri, 21 Apr 2006 05:50:45 GMT Raw View
musiphil@bawi.org (Seungbeom Kim) wrote (abridged):
> > The programmer should have the choice, and the default should be
> > safety rather than performance.
>
> You do:
Your code shows programmers have the choice, but not that the default is
safety rather than performance. The point of my post is to suggest how we
might gain safety-by-default without losing the choice.
-- Dave Harris, Nottingham, UK.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Antti Virtanen <virtanea@tilhi.cs.tut.fi>
Date: Fri, 21 Apr 2006 01:19:48 CST Raw View
On 2006-04-19, SuperKoko <tabkannaz@yahoo.fr> wrote:
>> A compiler can therefore trivially determine if the throw specification
>> of the called function is looser than the throw specification of the
>> calling function.
>>
> Okay, I'll do a proposal:
> The first problem here, is that the standard doesn't require
> exception-specification-correctness... but we'll see that it is not a
> real problem.
Just yesterday I spent some time figuring out why my program crashes.
I had a catch for the exceptions specified in the library's documentation but
it happened to throw also other exceptions.
I would appreciate a compiler (and source code) to contain and check
exception specifications instead of having to catch(...) just to be on the
safe side every time I use code written by someone else. Java enforces this
and it's not a problem. IMHO it seems to produce safer code since the
programmers are forced to think about things that can go wrong, though
you'll find plenty of comments like "// this never happens".
> // in the shared library (whose code is not accessible)
> int g(int a,int b)
> {
> return a+b; // in fact, it can't throw, even if the exception
> specification says that it can.
> // that is not a problem, but...
> }
>
> // in the compiled module:
> int g(int a,int b);
>
> int f() throw()
> {
> g(5,3); // that is compliant C++ code.
> }
>
> You see that the compiler have to put implicit try/catch blocks,
> because it can't whether g can throw or not.
True. I suppose we can't create shared libraries where function signatures
include the exception specifications.
However, at the present time the programmer writing f() can't know
whether g throws or not. He can read the documentation, sure, but
to provide a guarantee that f can't throw, he has to put that
try/catch block in manually. This carries the same performance
penalty as a try/catch generated by the compiler.
> I'd like that C++ requires exception-specification correctness.
> C++ requires const-correctness! Why not exception-specification
> correctness?
I don't know, but it doesn't make much sense to me.
> In fact, here is what is actually possible : compilers could emit a
> warning message when a function is not exception-specification correct.
> If the standard accepts ths proposal... it would become an error.
Emitting warnings doesn't break existing code nor does it have a
runtime performance penalty, which are the two common arguments
used to oppose any changes in C++ standard.
>> But I'd still like destructors (and extern "C") to be implicitly "throw
>> static ()"
> I agree
Me too. Afaik throwing destructors are broken code anyway.
This issue regarding destructors has been discussed years ago:
http://anubis.dkuug.dk/jtc1/sc22/wg21/docs/cwg_active.html#219
--
// Antti Virtanen -//- http://www.students.tut.fi/~virtanea
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "ThosRTanner" <ttanner2@bloomberg.net>
Date: Fri, 21 Apr 2006 08:22:47 CST Raw View
Nicolas Pavlidis wrote:
> ThosRTanner wrote:
> > SuperKoko wrote:
> > But people don't use throw specifications because the runtime check
> > makes them unusable in systems where abnormal termination is not an
> > option.
>
> There is a great problem with exception - spec, and it is named
> templates :-).
>
> You can not react to exception specifications in templates, so it is
> better IMHO to leave them out, if you use templates.
What I was suggesting doesn't stop you doing that. But at least you
would get a compiler error rather than a runtime error if you did this.
>
> >> But, surely, compilers could emit warnings when the error is
> >> diagnosable.
> >
> > Well, if the compilers checked exception specs at compile time, they
> > could produce more efficient code, coz they wouldn't have to catch all
> > exceptions and determine whether or not they needed to terminate.
>
> > Anyway, as stated above, the general rule appears to be that compiler
> > warnings are for wimps.
> >
> >> Similarly, misuse of the assignment in conditions:
> >>
> >> if (a=b){
> >> }
> >>
> >> Is detected by many compilers.
> > And there is an unambigous rewrite of that - in fact, there are several
> > ditto ditto.
>
> This thing will be recognized, at least by the customer :-).
>
> Best Regards,
> Nicolas
>
> --
> | Nicolas Pavlidis | Elvis Presly: |
> | Student of SE & KM | "Into the goto" |
> | pavnic@sbox.tugraz.at | ICQ #320057056 |
> | ------------University of Technology, Graz-----------------
>
> ---
> [ comp.std.c++ is moderated. To submit articles, try just posting with ]
> [ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
> [ --- Please see the FAQ before posting. --- ]
> [ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "ThosRTanner" <ttanner2@bloomberg.net>
Date: Fri, 21 Apr 2006 08:22:58 CST Raw View
kuyper@wizard.net wrote:
> Bo Persson wrote:
> > "ThosRTanner" <ttanner2@bloomberg.net> skrev i meddelandet
> > news:1145436601.054028.10610@e56g2000cwe.googlegroups.com...
> > >
> > > "Bo Persson" wrote:
> .
> > >> > All of these can be detected by the compiler.
> > >>
> > >> That's the problem. They can *sometimes* be detected by the
> > >> compiler.
> > >> What happens when they cannot?
> > > Please provide an instance where a conformant compiler cannot detect
> > > with 100% accuracy whether the throw spec for a function being
> > > called
> > > is "looser" than the throw spec for the calling function.
> >
> > Try this one:
> >
> > void g(int x)
> > {
> > if (x > 5)
> > throw "parameter error";
> > }
> >
> > void f(int x) throw()
> > {
> > if (x < 5)
> > g(x);
> > }
> >
> > Will that compile?
>
> The throw spec for g() allows it to throw anything, which is
> unambiguously looser than the throw spec for f(), which is not allowed
> to throw anything, and this fact is trivially detectable by the
> compiler at the point where g() is called. How does this count as a
> response to a challenge to provide 'an instance where a conformant
> compiler cannot detect with 100% accuracy whether the throw spec for a
> function being called is "looser" than the throw spec for the calling
> function.' ?
>
> I suspect that you're misreading the proposal. It's the throw
> specification of the called function that is being compared with the
> throw specification of the calling function. It doesn't matter whether
> or not the called function actually throws an exception inconsistent
> with the calling function's specification. All that matters is whether
> the called function has a specification that would allow it to throw
> such an exception.
>
> > template<class T>
> > void f() throw()
> > {
> > T::g();
> > }
>
> Whether or not the throw specification of g() is looser than that of
> f() can not be determined at the time of the template definition, but
> it's also true that you can't even determine whether or not g()
> actually exists at that point. Both the existence of g(), and it's
> possession of a looser throw specification f()'s, can trivially be
> detected with 100% accuracy at the point of instantiation of f<T>;
> assuming that the instantiation is legal (which requires that the
> definition of T be in visible).
>
> > > All functions have throw specifications (a lack of throw spec means
> > > you
> > > can throw anything).
> >
> > Which includes most of the standard library. Should you not be allowed
> > to use that?
>
> The proposed change would still allow you to call such functions; it
> would just require you to either catch all of the exceptions that might
> be thrown by such a function, or to give your own function a completely
> open throw spec.
>
> There are many valid arguments that could be made against this
> proposal, such as the fact that it would break a lot of existing code,
> but the infeasibility of implementation isn't one of them.
That's why I suggested adding 'static' or some such keyword after the
throw keyword in the specifcation. That would break no existing code.
I admit having implicit 'throw static ()' for destructors and extern
"C" declarations probably would break existing code, but extern "C"
actually needs this behaviour (real C functions can't throw anything,
and throwing an exception into a C caller is very dangerous), and also
the usual technique of wrapping C headers:
#ifdef __cplusplus
extern "C" {
#endif
void func1(int arg1, etc);
wouldn't work very well without the implicit throw static ()
As for destructors - I'd accept an obligatory compiler warning and an
obligatory compiler option!
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: aon.912719634@aon.at (Nicolas Pavlidis)
Date: Fri, 21 Apr 2006 15:40:26 GMT Raw View
ThosRTanner wrote:
> Nicolas Pavlidis wrote:
>> ThosRTanner wrote:
>>> SuperKoko wrote:
>>> But people don't use throw specifications because the runtime check
>>> makes them unusable in systems where abnormal termination is not an
>>> option.
>> There is a great problem with exception - spec, and it is named
>> templates :-).
>>
>> You can not react to exception specifications in templates, so it is
>> better IMHO to leave them out, if you use templates.
>
> What I was suggesting doesn't stop you doing that. But at least you
> would get a compiler error rather than a runtime error if you did this.
We've some kind of a misunderstanding.
I made use of throw - spec, until I found out that they are incompatible
to templates, so I left them out in template code.
In non template code I leave them out because of the runtime check.
If the compiler would check this it would be fine, but this does not
solve the template - problem, which was the intend of my post.
--
| Nicolas Pavlidis | Elvis Presly: |
| Student of SE & KM | "Into the goto" |
| pavnic@sbox.tugraz.at | ICQ #320057056 |
| ------------University of Technology, Graz-----------------
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "ThosRTanner" <ttanner2@bloomberg.net>
Date: 21 Apr 2006 16:30:01 GMT Raw View
Nicolas Pavlidis wrote:
> ThosRTanner wrote:
> > Nicolas Pavlidis wrote:
> >> ThosRTanner wrote:
> >>> SuperKoko wrote:
> >>> But people don't use throw specifications because the runtime check
> >>> makes them unusable in systems where abnormal termination is not an
> >>> option.
> >> There is a great problem with exception - spec, and it is named
> >> templates :-).
> >>
> >> You can not react to exception specifications in templates, so it is
> >> better IMHO to leave them out, if you use templates.
> >
> > What I was suggesting doesn't stop you doing that. But at least you
> > would get a compiler error rather than a runtime error if you did this.
>
>
> We've some kind of a misunderstanding.
>
> I made use of throw - spec, until I found out that they are incompatible
> to templates, so I left them out in template code.
>
> In non template code I leave them out because of the runtime check.
>
> If the compiler would check this it would be fine, but this does not
> solve the template - problem, which was the intend of my post.
If the language won't let you write
template <class T> void fred() throw(My_Exception<T>) { ... }
then you can't. That's a language restriction, and quite outside the
scope of whether or not the compiler should do a run time or compile
time check. If it has enough information to do one, it has enough to do
the other.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: bart@ingen.ddns.info (Bart van Ingen Schenau)
Date: Fri, 21 Apr 2006 20:02:39 GMT Raw View
ThosRTanner wrote:
>
> Nicolas Pavlidis wrote:
>> ThosRTanner wrote:
>> > Nicolas Pavlidis wrote:
>> >> ThosRTanner wrote:
>> >>> SuperKoko wrote:
>> >>> But people don't use throw specifications because the runtime
>> >>> check makes them unusable in systems where abnormal termination
>> >>> is not an option.
>> >> There is a great problem with exception - spec, and it is named
>> >> templates :-).
>> >>
>> >> You can not react to exception specifications in templates, so it
>> >> is better IMHO to leave them out, if you use templates.
>> >
>> > What I was suggesting doesn't stop you doing that. But at least you
>> > would get a compiler error rather than a runtime error if you did
>> > this.
>>
>>
>> We've some kind of a misunderstanding.
>>
>> I made use of throw - spec, until I found out that they are
>> incompatible to templates, so I left them out in template code.
>>
>> In non template code I leave them out because of the runtime check.
>>
>> If the compiler would check this it would be fine, but this does not
>> solve the template - problem, which was the intend of my post.
>
> If the language won't let you write
>
> template <class T> void fred() throw(My_Exception<T>) { ... }
>
> then you can't. That's a language restriction, and quite outside the
> scope of whether or not the compiler should do a run time or compile
> time check. If it has enough information to do one, it has enough to
> do the other.
The problem with exception specifications for templates is that you can
not know what you should put in the exception specification.
Consider, for example, the following:
// file: template.h
// provided as part of a 3rd party library
template <class T>
T* createDefault() throw(std::bad_alloc /*, and what else? */)
{
return new T();
}
// file my_source.cpp
#include "template.h"
struct A
{
A() throw() {}
};
struct B
{
B() throw(int) {}
};
void foo() throw(std::bad_alloc)
{
A* a = createDefault<A>();
try
{
B* b = createDefault<B>();
}
catch(int)
{
}
}
Assuming a compile-time check of the exception specifications, what
should the exception specification on createDefault<>() have been to
avoid a compiler diagnostic.
Bart v Ingen Schenau
--
a.c.l.l.c-c++ FAQ: http://www.comeaucomputing.com/learn/faq
c.l.c FAQ: http://www.eskimo.com/~scs/C-faq/top.html
c.l.c++ FAQ: http://www.parashift.com/c++-faq-lite/
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: bop@gmb.dk ("Bo Persson")
Date: Fri, 21 Apr 2006 20:13:18 GMT Raw View
"Antti Virtanen" <virtanea@tilhi.cs.tut.fi> skrev i meddelandet
news:slrne4gte5.626.virtanea@tilhi.cs.tut.fi...
> On 2006-04-19, SuperKoko <tabkannaz@yahoo.fr> wrote:
>
> I would appreciate a compiler (and source code) to contain and check
> exception specifications instead of having to catch(...) just to be
> on the
> safe side every time I use code written by someone else. Java
> enforces this
> and it's not a problem.
So, why isn't everyone just using Java? :-)
>>
>> // in the compiled module:
>> int g(int a,int b);
>>
>> int f() throw()
>> {
>> g(5,3); // that is compliant C++ code.
>> }
>>
>
> However, at the present time the programmer writing f() can't know
> whether g throws or not. He can read the documentation, sure, but
> to provide a guarantee that f can't throw, he has to put that
> try/catch block in manually.
What if the documentation says that g() never throws, as long as a >
b? That makes g(5, 3) absolutely safe.
>This carries the same performance
> penalty as a try/catch generated by the compiler.
Which might very well be zero, as long as no exception is thrown.
>
>> I'd like that C++ requires exception-specification correctness.
>> C++ requires const-correctness! Why not exception-specification
>> correctness?
Perhaps because it is much harder to define the preconditions?
How do you declare a function that throws for illegal parameters only?
int g(int a,int b) throw(bad_parameters when a < b) or what?
I, as the programmer, can tell that g(5, 3) is statically safe. The
compiler has a much harder time!
Bo Persson
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "SuperKoko" <tabkannaz@yahoo.fr>
Date: Fri, 21 Apr 2006 15:15:59 CST Raw View
Nicolas Pavlidis wrote:
> There is a great problem with exception - spec, and it is named
> templates :-).
>
In fact, even with templates, the notion of "exception-specification" i
gave, remains, is well-defined, and easily checkable by compilers (at
the point of template instanciation)...
However, I agree that it is extremely inconvenient with templates (and
all callbacks mechanisms).
The greater problem is templates, because, in a template definition,
the exception-specification is static (i.e. It can't depend on the
template parameters):
Thus, for example, one could expect std::for_each not to throw when
used with non-throwing operations. But we can't define std::for_each
with a no-throw exception specification, otherwise it would not be
usable with throwing operations...
For templates, there is a solution... Induced exception-specifications.
With a special syntax (or perhaps no special syntax at all), there
should be a mean to say that the compiler must define the exception
specification of the template function, based on operations done inside
the body (basically, that is the union of exceptions that can be thrown
by operations inside the body of the function, except if an exception
is specifically caught with a try/catch block).
With that definition of "induced exception specification", we can
define an exception-specification-correct function as being a function
whose effective exception-specification is looser or equal to the
induced exception-specification.
And, there should be a special syntax (or better, the absence of
explicit exception-specification which is, nowaydays, equivalent to an
exception specification that includes all exceptions in the world)
indicating to the compiler that he must use, as exception-specification
for this function, the induced exception-specification.
That is only possible for templates and inline functions, since the
compiler needs the effective function definition in order to compute
the induced exception-specification.
For non-template, non-inline functions, the compiler would deem that
the induced exception-specification is equal the infinite set of
exceptions.
That would solve the problem of all template containers & functions
that can throw or not, depending on the operations inside them.
So, for example, it would be valid to call std::for_each with
non-throwing operations, in a non-throwing function, without needing
any try/catch block.
That solves most problems, but it does not solve some runtime problems,
such as callbacks:
For instance:
class A
{
public:
void f1() throw();
void f2() const;
};
void Execute10TimesAMember(A&, void (A::*f)())
{
for(unsigned i=0;i<10;++i) a.f();
}
int func() throw()
{
A a;
Execute10TimesAMember(a,&A::f1);
try {
Execute10TimesAMember(a,&A::f2);
Execute10TimesAMember(a,(condition)?&A::f1 : &A::f2); // really
dynamic!
}
catch(...) {
// some code here
}}
Execute10TimesAMember(a,&A::f1) will be deemed as a possibly throwing
function, even if actually
We can't do much against that.
Here are the possibilities :
1) Require the user to provide a try/catch block.
The user, will typically make an assertion failure in the catch
block... Or, recover properly (if it is possible).
2) Allow a special syntax to break the system, with a special
"HeyItReallyCantThrowStupidCompiler" (a better keyword would be chosed)
code block:
int func() throw()
{
A a;
HeyItReallyCantThrowStupidCompiler {
Execute10TimesAMember(a,&A::f1);
}
try {
Execute10TimesAMember(a,&A::f2);
Execute10TimesAMember(a,(condition)?&A::f1 : &A::f2); // really
dynamic!
}
catch(...) {
// some code here
}
}
An exception escaping of a HeyItReallyCantThrowStupidCompiler would
have undefined behaviour.
The main purpose of this syntax is performance...
In fact, since this code will not be too frequent, IMHO, the first
option is better.
3) Allow the HeyItReallyCantThrowStupidCompiler blocks, but catch
runtime exceptions, and use the classical mechanism of reporting
"unexpected exceptions".
IMHO, that's a bad idea, since it has not better performances than
option (1), but add a new keyword, and is less flexible than option
(1), because it is not possible to recover from an unexpected
exception.
That issue with callbacks may seem unacceptable (any form of callbacks,
the C++ style of passing a pointer to an abstract base class as
"callback" as also the problem), but in fact, it is not different from
cv qualifiers problems..
Look at my example:
class A
{
public:
void f1() throw();
void f2() const;
};
void Execute10TimesAMember(A&, void (A::*f)())
{
for(unsigned i=0;i<10;++i) a.f();
}
Sensibly, one could write a function:
void Execute10TimesA_F2(const A& a)
{
Execute10TimesAMember(a, A::f2);
}
But, it does not compile, because of const-correctness.
A const_cast is needed in order to avoid that.
It would be possible explore the possibilities of having cv-qualifiers
template parameters... But that's not the matter here, and I doubt that
it worth the complication.
Conclusion :
Actually, without changing at all the language, a compiler should be
able to check for exception-specification correctness (with the new
definition, where the compiler computes the induced
exception-specification of inline functions and templates at the place
of their instanciation), and generate implicit try/catch blocks, only
where this exception-specification correctness is broken, and emit a
warning at this position.
Like that, instead of constantly being afraid of potential unexpected
exceptions, the programmer would simply be very careful in the seldom
positions where this warning message appears, and probably, add a
try/catch block... at least if the exception is recoverable.
There may be possible to specify that this warning be replaced by an
error, however:
1) Benefits for good programmers that read warning messages, are very
small... Personally, I treat almost identically warning message and an
error messages.
2) Even if that does not change the syntax of the language, it breaks
some code (not much, since there is not much code that uses
exception-specifications other than the infinite-exception-set, and
those programs will still compile).
Nevertheless, it is not a dangerous change : there is no silent
change...
3) It complicates compiler implementations... In fact, for templates,
if definition follows the first instanciation, it is not possible to
implement a single-pass compiler.
That problem already exists with inline functions, since this code is
valid:
inline void f(); //note that the "inline" keyword is required here (see
defect report #317)
void func() {f();}
void f(){}
Another solution, would be to deem that, at the first function usage,
if the definition is not available, the induced exception-specification
is the infinite-set-of-exceptions (and it remains as is, even after the
definition)... but that's so ugly, and I am sure that there are lot of
issues. So... forget it.
But, the question is:
Knowing that benefits are small, does it worth a language change?
IMHO, it should not be a proposal for C++0x...
But this proposal could be proposed to compiler implementers.
I think that it could be included in the GCC compiler (the warning
message could be turned on or off).
That is great for both compiler optimization and program correctness.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: kuyper@wizard.net
Date: 21 Apr 2006 22:20:01 GMT Raw View
SuperKoko wrote:
> Nicolas Pavlidis wrote:
>
> > There is a great problem with exception - spec, and it is named
> > templates :-).
> >
>
> In fact, even with templates, the notion of "exception-specification" i
> gave, remains, is well-defined, and easily checkable by compilers (at
> the point of template instanciation)...
>
> However, I agree that it is extremely inconvenient with templates (and
> all callbacks mechanisms).
>
> The greater problem is templates, because, in a template definition,
> the exception-specification is static (i.e. It can't depend on the
> template parameters):
Is that so? My C++ books are at home, so I can't check right now, but I
would have thought that having a throw specification that's template
type-dependent was legal. To take an absurdly simple case:
template <typename T> void throwwrap(T t) throw(T) { throw(t); }
int main() {throwwrap(1);}
My understanding is that the real problem is that a template function's
throw specification can't easily be adapted to automatically cover the
throw specifications of the template-argument dependent functions that
it calls.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Ben Hutchings <ben-public-nospam@decadentplace.org.uk>
Date: Sat, 22 Apr 2006 11:17:42 CST Raw View
James Kanze <kanze.james@neuf.fr> wrote:
<snip>
> Do you write real applications, or just demonstration programs
> for training purposes. Real applications DO validate input.
<snip>
The steady stream of security advisories for common software shows
that this is not the case even where validation is security-critical,
let alone where it is only protecting the user from himself.
Clearly we need to catch invalid input, but the language should
support that. At the machine code level, it's faster to test for an
overflow condition after an operation than to work out beforehand
whether overflow is going to happen, and there's less risk of getting
the test wrong; however such condition codes are not exposed to the C
or C++ programmer despite their ubiquity (AFAIK). (However, if one
check can cover many following operations, that's faster than checking
each of them, so it's hard to generalise about what's preferable.)
Ben.
--
Ben Hutchings
The obvious mathematical breakthrough [to break modern encryption] would be
development of an easy way to factor large prime numbers. - Bill Gates
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: aon.912719634@aon.at (Nicolas Pavlidis)
Date: Sun, 23 Apr 2006 04:45:23 GMT Raw View
SuperKoko wrote:
> Nicolas Pavlidis wrote:
>
>> There is a great problem with exception - spec, and it is named
>> templates :-).
>>
>
> In fact, even with templates, the notion of "exception-specification" i
> gave, remains, is well-defined, and easily checkable by compilers (at
> the point of template instanciation)...
Partly yes. The problem with this approach is, that in this case, you
can not instantiate a template with an incomplete type. Because the
compiler needs to know much more the e.g. a forward declaration gives.
Maybe the same problem raises, if you work with pointers as type parameter.
> However, I agree that it is extremely inconvenient with templates (and
> all callbacks mechanisms).
>
> The greater problem is templates, because, in a template definition,
> the exception-specification is static (i.e. It can't depend on the
> template parameters):
ACK, but this would be resolvable with some kind of a static typeid or
something else.
> Thus, for example, one could expect std::for_each not to throw when
> used with non-throwing operations. But we can't define std::for_each
> with a no-throw exception specification, otherwise it would not be
> usable with throwing operations...
>
> For templates, there is a solution... Induced exception-specifications.
> With a special syntax (or perhaps no special syntax at all), there
> should be a mean to say that the compiler must define the exception
> specification of the template function, based on operations done inside
> the body (basically, that is the union of exceptions that can be thrown
> by operations inside the body of the function, except if an exception
> is specifically caught with a try/catch block).
One problem remains here, C++ does not force one to do an exception -
specification, so this leads to the same problem (as my Idea of a static
typeid operator does), that not all exceptions can be caught, if they
are not specified. So you still have to leave them out, or reduce the
freedom a C++ - programmer has.
What is [possible IMHO is to make typeid (the one which exsists), or
better the typeinfo - clas more powerful, so that it can provide more
information than just (the non standardized) class name of some type.
For example, that I can call it in a catch(...) - Block, and ask it to
give me the last (unknown) exception that was thrown, to react to this.
[...]
> But, the question is:
> Knowing that benefits are small, does it worth a language change?
Maybe I see everything to simple, but an imporvemnt of typeid would not
break aold code, but can make new code safe if it's used. And beside
this fact, it would solve a number of other problems, such as non
standardized classnames ans so on.
The language chage would be only in typeid and would not affect exsiting
code IMHO.
Best regards,
Nicolas
--
| Nicolas Pavlidis | Elvis Presly: |
| Student of SE & KM | "Into the goto" |
| pavnic@sbox.tugraz.at | ICQ #320057056 |
| ------------University of Technology, Graz-----------------
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: David Abrahams <dave@boost-consulting.com>
Date: Sun, 23 Apr 2006 01:39:45 CST Raw View
kuyper@wizard.net writes:
> My understanding is that the real problem is that a template
> function's throw specification can't easily be adapted to
> automatically cover the throw specifications of the
> template-argument dependent functions that it calls.
No, the real problem is that in general, exception specifications
(whether statically or dynamically checked) cause an undesirable
coupling with code that shouldn't be coupled, because the code doesn't
really care what exception types are passing through it, and nobody
thinks it's worth extending the language to better support a concept
that is, for most applications, worse than useless.
Statically checked no-throw specifications would probably be a good
idea, though.
--
Dave Abrahams
Boost Consulting
www.boost-consulting.com
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: usenet@aristeia.com (Scott Meyers)
Date: Fri, 14 Apr 2006 07:33:13 GMT Raw View
What work has been done on eliminating undefined behavior in C or C++? Off the
top of my head, some undefined behavior is due to "stupid user behavior," e.g.,
indexing beyond the end of an array, dereferencing a null pointer, etc. Other
undefined behavior -- or maybe it's unspecified or implementation-defined
behavior, I can never keep those terms straight -- arises from flexibility for
implementers, e.g., order of evaluating arguments for function calls, order of
expression evaluation between sequence points, order of initialization of
non-local statics defined in separate translation units. I'd be interested in
any work that's been done on identifying these kinds of behavior (beyond
grepping the standards for "undefined", "unspecified", etc.) and suggesting ways
to eliminate or avoid them, ideally without any obvious runtime cost. (Checking
array bounds would fail the "no obvious runtime cost" test, while defining
function argument evaluation order would pass, because, well, because it's not
obvious to me that imposing a required order would necessarily incur a runtime
penalty :-})
My motivation for the question is that it's my understanding that
safety-critical applications typically identify language subsets to reduce risk,
and they then typically impose usage rules on the features remaining in the
subsets to further reduce risk. One form of risk is unpredictable program
behavior, and such behavior can arise from the use of language constructs with
undefined/unspecified/implementation-defined behavior, so I'm wondering what
work has been done on identifying such constructs and dealing with them.
Thanks,
Scott
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: NULL@NULL.NULL ("Tom s")
Date: Fri, 14 Apr 2006 15:52:01 GMT Raw View
Scott Meyers posted:
> What work has been done on eliminating undefined behavior in C or C++?=20
> Off the top of my head, some undefined behavior is due to "stupid user
> behavior," e.g., indexing beyond the end of an array, dereferencing a
> null pointer, etc.
I agree. Stuff like the following is just "stupid":
int *p;
*p =3D 5; //Undefined Behaviour
> Other undefined behavior -- or maybe it's
> unspecified or implementation-defined behavior, I can never keep those
> terms straight -- arises from flexibility for implementers, e.g., order
> of evaluating arguments for function calls, order of expression
> evaluation between sequence points, order of initialization of=20
> non-local statics defined in separate translation units.
You're either writing:
a) Portable 100% Standard-compliant Code
b) Platform-specific code / Implementation-specific code
If you're writing portable code, then the solution is simple: Don't allow=
=20
situations that could, depending on the implementation, result in Undefin=
ed=20
Behaviour. Here's an example:
char p =3D 120;
char +=3D 100; //Could cause Undefined Behaviour
This has no place in portable code.
On the other hand, if you're programming for a platform where you know th=
at=20
"char" is unsigned, then you are free to do the above.
> I'd be
> interested in any work that's been done on identifying these kinds of
> behavior (beyond grepping the standards for "undefined", "unspecified",
> etc.) and suggesting ways to eliminate or avoid them, ideally without
> any obvious runtime cost.
Basically you just have to know the C++ language; you've to know things=20
like:
a) It's undefined behaviour for a signed integral type to overflow.
b) It's undefined behaviour to check an intrinsic variable's value if it =
was=20
never initialised.
> (Checking array bounds would fail the "no
> obvious runtime cost" test, while defining function argument evaluation
> order would pass, because, well, because it's not obvious to me that
> imposing a required order would necessarily incur a runtime penalty
> :-})=20
We already have the C++ programming language. Unless we change it with=20
future Standards, we can simply work around any problems. Instead of=20
writing:
j =3D f() + g();
Simply write:
j =3D f(); j +=3D g();
=20
> My motivation for the question is that it's my understanding that=20
> safety-critical applications typically identify language subsets to
> reduce risk, and they then typically impose usage rules on the features
> remaining in the subsets to further reduce risk. One form of risk is
> unpredictable program behavior, and such behavior can arise from the
> use of language constructs with=20
> undefined/unspecified/implementation-defined behavior, so I'm wondering
> what work has been done on identifying such constructs and dealing with
> them.
None that I'm aware of -- because none is needed. Here's an example of ho=
w I=20
write portable code:
I invariably use Windows XP, so all my programs are Win32. On Win32, the=20
built-in types are as follows:
char =3D 8 bits
short =3D 16 bits
int =3D 32 bits
long =3D 32 bits
On my platform, I can safely store a 32-bit number in an "int". If I'm=20
writing portable code however, I won't. Why? Because the Standard says an=
=20
"int" can be 16-Bit. Thankfully, it also says that a "long" must be at le=
ast=20
32-Bit, so I use a "long" when I need to store a 32-Bit number.
Also, there may be some systems where, if you don't initialise a local=20
variable, that it's value is 0, as follows:
void Func()
{
int k;
k +=3D 56;
//k's value is now 56 on some platforms
}
However, the Standard doesn't guarantee this, so I don't do it in portabl=
e=20
code (I wouldn't do it in platform-specific code either -- I'd opt to=20
explicitly set it to zero).
So at the end of the day, you're either writing portable code, or platfor=
m=20
specific code. For both, avoid undefined behaviour. For the former, avoid=
=20
implementation specific behaviour which may result in undefined behaviour=
=20
depending on flexibility given to the plaform/implementation.
-Tom=E1s
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Tom s" <NULL@NULL.NULL>
Date: 14 Apr 2006 16:10:06 GMT Raw View
> char p = 120;
>
> char += 100; //Could cause Undefined Behaviour
Meant write:
char p = 120;
p += 100; //Could cause Undefined Behaviour
-Tom s
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "ThosRTanner" <ttanner2@bloomberg.net>
Date: Fri, 14 Apr 2006 11:06:26 CST Raw View
Scott Meyers wrote:
> What work has been done on eliminating undefined behavior in C or C++? Off the
> top of my head, some undefined behavior is due to "stupid user behavior," e.g.,
> indexing beyond the end of an array, dereferencing a null pointer, etc. Other
> undefined behavior -- or maybe it's unspecified or implementation-defined
> behavior, I can never keep those terms straight -- arises from flexibility for
> implementers, e.g., order of evaluating arguments for function calls, order of
> expression evaluation between sequence points, order of initialization of
> non-local statics defined in separate translation units. I'd be interested in
> any work that's been done on identifying these kinds of behavior (beyond
> grepping the standards for "undefined", "unspecified", etc.) and suggesting ways
> to eliminate or avoid them, ideally without any obvious runtime cost. (Checking
> array bounds would fail the "no obvious runtime cost" test, while defining
> function argument evaluation order would pass, because, well, because it's not
> obvious to me that imposing a required order would necessarily incur a runtime
> penalty :-})
>
> My motivation for the question is that it's my understanding that
> safety-critical applications typically identify language subsets to reduce risk,
> and they then typically impose usage rules on the features remaining in the
> subsets to further reduce risk. One form of risk is unpredictable program
> behavior, and such behavior can arise from the use of language constructs with
> undefined/unspecified/implementation-defined behavior, so I'm wondering what
> work has been done on identifying such constructs and dealing with them.
Some of that work must have been done for the embedded C++ language
subset, though admittedly that is likely to go more on performance than
safety critical.
One of the risks in a language of C++ which is more difficult to
identify are those where the language specifies that certain behaviour
is very well defined but will cause your program to terminate - for
instance throwing an exception in a destructor which is being invoked
by an exception. I suspect you will find that in any safety critical
system (or indeed any system that is intended to run non-stop),
exceptions are banned because of the ease with which you can
unintentionally terminate your program. Off hand, these are:
1) Throwing an exception in the constructor of an exception
2) Throwing an exception in a destructor whilst processing an exception
(which means IIRC that there are some not entirely contrived constructs
where an error is guaranteed to cause a non-recoverable crash)
3) Throwing an exception which isn't in the exception specification of
some potentially arbitrarily distant client.
Of course, if you can't use exceptions, you can't really use the
default new - which means you have to code everything to explicitly
cope with memory exhaustion.
As to amount of work done to eliminate undefined behaviour - given the
howls of protest whenever anyone suggests a language change to
eliminate one or other aspect of undefined behaviour, it seems unlikely
without a proposal for a new language that would drop C (or indeed C++)
backward compatibility where it was necessary to avoid undefined /
unspecified / implementation defined / defined-but-deadly behaviour.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Pete Becker <petebecker@acm.org>
Date: Fri, 14 Apr 2006 12:25:56 CST Raw View
Scott Meyers wrote:
> Other undefined behavior -- or maybe it's
> unspecified or implementation-defined behavior, I can never keep those
> terms straight
Undefined behavior means that the language definition says nothing about
what happens if you do it (de-referencing a null pointer, for example).
For the other two, the behavior is more clearly bounded.
Implementation-defined means what it sounds like: the implementation
must document what it does (for example, whether char is signed or
unsigned). In contrast to unspecified, which means there is a choice
within a clear set of alternatives and the implementation is not
required to document what it does (for example, order of evaluation of
function arguments, which can change with different optimization
settings for the same compiler).
--
Pete Becker
Roundhouse Consulting, Ltd.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "SuperKoko" <tabkannaz@yahoo.fr>
Date: Fri, 14 Apr 2006 14:44:42 CST Raw View
Scott Meyers wrote:
> any work that's been done on identifying these kinds of behavior (beyond
> grepping the standards for "undefined", "unspecified", etc.) and suggesting ways
> to eliminate or avoid them, ideally without any obvious runtime cost. (Checking
> array bounds would fail the "no obvious runtime cost" test, while defining
> function argument evaluation order would pass, because, well, because it's not
> obvious to me that imposing a required order would necessarily incur a runtime
> penalty :-})
Assume that the order of evaluation were defined (for example left to
right), and a sequence point was included between two arguments
evaluation (which may already reduces the efficiency of strongly
optimizing compilers on superscalar micro-processors).
But, I'll deem a realistic example.
The 8088 CPU.
This CPU has no cache, so the optimization for speed is equivalent of
optimization for size.
Assume a compiler using the common __cdecl calling convention which
requires arguments to be pushed from right to left.
f(g(),h());
With a right-to-left evaluation order, a function call is done like the
followin:
call near ptr h
push ax
call near ptr g
push ax
call near ptr f
add sp,4
Total CPU cycles for the "function call" : "push ax" + "push ax" +
"call near ptr f"+ "add sp,4":
15+15+23+4 = 57
Push operations requires only 1 machine code byte, and a few CPU
cycles.
With a left-to-right evaluation order, the stack can only be sensibly
accessed with the bp register. sp can't be used, and bx, si and di use
the ds segment instead of the ss segment.
A segment prefix opcode could be used, but it is slow.
Moreover, each register is precious, so we mustn't lose bx, si or di.
At best, the compiler (if it is smart enough) can write to parameters
via the bp register like that:
sub sp,4
call near ptr g
mov [bp - ... ], ax
call near ptr h
mov [bp - ... ], ax
call near ptr f
add sp,4
CPU cycles for the "function call" :
4+(13+9)+(13+9)+23+4 = 75
The function call is significantly slower
But, if the compiler allows dynamic allocation on the stack (it is not
standard in C++, but it can be a sensible implementation of VLA in a C
compiler...) via an implementation-specific alloca function, then the
compiler can't know the quantity of space between bp and sp, and thus
must do that:
push bp
mov bp,sp
sub sp,4
call near ptr g
mov [bp-4],ax
call near ptr h
mov [bp-2],ax
call near ptr f
mov sp,bp
pop bp
CPU cycles for the "function call" :
15+2+4+(13+9)+(13+9)+23+2+12 = 102
Okay, for a two-arguments function, there is a better way:
call near ptr h
push ax
call near ptr g
pop cx
push ax
push cx
call near ptr f
add sp,4
CPU cycles for the "function call"
15+12+15+15+23+4 = 84
And, if a register variable is free (which is very seldom, since there
are only two register for register variables : si and di).
It can be reduced to:
call near ptr h
mov di,ax
call near ptr g
push ax
push di
call near ptr f
add sp,4
CPU cycles for the "function call"
2+15+15+23+4 = 59
But, the slow way is the only sensible implementation I see for a
function having more parameters (3 or more).
And, there are probably CPU for which that would be worst!
Calling sequences can already be very problematic for some CPU :
Look at http://cm.bell-labs.com/cm/cs/who/dmr/clcs.html
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: kanze.james@neuf.fr (James Kanze)
Date: Fri, 14 Apr 2006 21:30:33 GMT Raw View
NULL@NULL.NULL wrote:
> Scott Meyers posted:
> If you're writing portable code, then the solution is simple:
> Don't allow situations that could, depending on the
> implementation, result in Undefined Behaviour. Here's an
> example:
> char p =3D 120;
> char +=3D 100; //Could cause Undefined Behaviour
(I suppose you meant p +=3D 100, rather than char +=3D 100.)
Undefined, or implementation specified. I think in this case,
implementation specified: the semantics of the statement is:
read p, convert it to an int, add 100 to the int, then convert
the results back to char and store into p. The only ambiguous
part is converting the results (220) back into char -- a char
may not be (and typically isn't) capable of representing 220.
In that case, the results are implementation defined (and may
result in an implementation defined signal).
> This has no place in portable code.
Agreed. But I don't think that this is the sort of thing that
Scott was talking about.
> On the other hand, if you're programming for a platform where
> you know that "char" is unsigned, then you are free to do the
> above.
>> I'd be interested in any work that's been done on identifying
>> these kinds of behavior (beyond grepping the standards for
>> "undefined", "unspecified", etc.) and suggesting ways to
>> eliminate or avoid them, ideally without any obvious runtime
>> cost.
> Basically you just have to know the C++ language;
The problem is precisely that there are many cases where the C++
language doesn't specify what may happen.
> you've to know things like:
> a) It's undefined behaviour for a signed integral type to overflow.
It's undefined behavior for an arithmetic operation to overflow
if the type isn't unsigned. Given a value, however, it is only
implementation defined (and not undefined) what happens when
converting it to a narrower type.
> b) It's undefined behaviour to check an intrinsic variable's
> value if it was never initialised.
>> (Checking array bounds would fail the "no obvious runtime
>> cost" test, while defining function argument evaluation order
>> would pass, because, well, because it's not obvious to me
>> that imposing a required order would necessarily incur a
>> runtime penalty
>> :-})
> We already have the C++ programming language. Unless we change
> it with future Standards, we can simply work around any
> problems.
My impression was that Scott was asking about possible changes,
to eliminate unnecessary undefined behaviors.
Some are, IMHO, simply inexcusable: using a symbol with two
successive _ is undefined behavior, for example. Surely the
compiler can check for this, at no cost in performance. If a
dynamically resolved function call resolves to a pure virtual
function, it's also undefined behavior -- this could also surely
be specified to result in a call to terminate, or to some
special function which calls terminate by default.
Those are the easy ones. I have yet to see where rigorously
defining an order of evaluation would have a measurable impact
on real programs, and it is probably the worst problem we face
today in terms of undefined (or underspecified) behavior.
> Instead of writing:
> j =3D f() + g();
> Simply write:
> j =3D f(); j +=3D g();
Sure. Why not just do away with binary operators other than the
assignment operators completely?
>> My motivation for the question is that it's my understanding
>> that safety-critical applications typically identify language
>> subsets to reduce risk, and they then typically impose usage
>> rules on the features remaining in the subsets to further
>> reduce risk. One form of risk is unpredictable program
>> behavior, and such behavior can arise from the use of
>> language constructs with
>> undefined/unspecified/implementation-defined behavior, so I'm
>> wondering what work has been done on identifying such
>> constructs and dealing with them.
> None that I'm aware of -- because none is needed. Here's an
> example of how I write portable code:
> I invariably use Windows XP, so all my programs are Win32. On
> Win32, the built-in types are as follows:
> char =3D 8 bits
> short =3D 16 bits
> int =3D 32 bits
> long =3D 32 bits
> On my platform, I can safely store a 32-bit number in an
> "int". If I'm writing portable code however, I won't. Why?
> Because the Standard says an "int" can be 16-Bit. Thankfully,
> it also says that a "long" must be at least 32-Bit, so I use a
> "long" when I need to store a 32-Bit number.
But that's not the real problem. In most safety critical
systems, portability isn't an issue. The problem is undefined
behavior even in non-portable code, due to order of evaluation,
etc.
> Also, there may be some systems where, if you don't initialise
> a local variable, that it's value is 0, as follows:
> void Func()
> {
> int k;
> k +=3D 56;
> //k's value is now 56 on some platforms
> }
> However, the Standard doesn't guarantee this,
And it's pretty rare. I don't know of any compiler which does
it in production builds.
> so I don't do it in portable code (I wouldn't do it in
> platform-specific code either -- I'd opt to explicitly set it
> to zero).
You miss what I think is Scott's point. Let's say that by
accident or error, you do do it -- nobody's perfect, after all.
You then test your program, and get a certain result, which is
deemed correct. Your tests are only valid, however, if that
result is reproduceable.
> So at the end of the day, you're either writing portable code,
> or platform specific code. For both, avoid undefined
> behaviour. For the former, avoid implementation specific
> behaviour which may result in undefined behaviour depending on
> flexibility given to the plaform/implementation.
We all know that we should avoid undefined behavior. The
problem with undefined behavior, however, is that it isn't
always detectable. You make a mistake, and it doesn't have any
apparent symptoms. For the moment.
--=20
James Kanze kanze.james@neuf.fr
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France +33 (0)1 30 23 00 34
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: kanze.james@neuf.fr (James Kanze)
Date: Fri, 14 Apr 2006 21:30:35 GMT Raw View
Pete Becker wrote:
> Scott Meyers wrote:
>> Other undefined behavior -- or maybe it's unspecified or
>> implementation-defined behavior, I can never keep those terms
>> straight
> Undefined behavior means that the language definition says
> nothing about what happens if you do it (de-referencing a null
> pointer, for example). For the other two, the behavior is
> more clearly bounded. Implementation-defined means what it
> sounds like: the implementation must document what it does
> (for example, whether char is signed or unsigned). In contrast
> to unspecified, which means there is a choice within a clear
> set of alternatives and the implementation is not required to
> document what it does (for example, order of evaluation of
> function arguments, which can change with different
> optimization settings for the same compiler).
That's what the standard says (and I suspect that Scott knows
this). The problem Scott has is more likely knowing when each
of the above applies. (There's also the problem that although
the standard requires an implementation to document
implementation defined behavior, to my knowledge none do.)
There are two problems, really. The first is being able to look
at a piece of code, and know whether it is fully defined or not.
For example: "i =3D f() + g()". This should normally be fully
defined, but if f() and g() both have side effects, or if one
has side effects, and the result of the other depends on these
side effects, then it is unspecified. (And lets not forget the
classic "f( std::auto_ptr<T>( new T ), std::auto_ptr<T>( new T
) )", where it is unspecified whether the code leaks memory or
not.) The other is being able to have any confidence in the
tests you run -- tests are only valid if the results are
reproduceable, and in the presence of undefined or unspecified
behavior, by definition, they aren't. Taken to the extreme,
this means that there is no point in running any tests on a C++
program, because they don't mean anything anyways. (Of course,
in practice, it's not that bad. Not quite, anyway.)
--=20
James Kanze kanze.james@neuf.fr
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France +33 (0)1 30 23 00 34
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: kanze.james@neuf.fr (James Kanze)
Date: Fri, 14 Apr 2006 21:57:28 GMT Raw View
SuperKoko wrote:
> Scott Meyers wrote:
>> any work that's been done on identifying these kinds of
>> behavior (beyond grepping the standards for "undefined",
>> "unspecified", etc.) and suggesting ways to eliminate or
>> avoid them, ideally without any obvious runtime cost.
>> (Checking array bounds would fail the "no obvious runtime
>> cost" test, while defining function argument evaluation order
>> would pass, because, well, because it's not obvious to me
>> that imposing a required order would necessarily incur a
>> runtime penalty :-})
> Assume that the order of evaluation were defined (for example
> left to right), and a sequence point was included between two
> arguments evaluation (which may already reduces the efficiency
> of strongly optimizing compilers on superscalar
> micro-processors).
> But, I'll deem a realistic example.
> The 8088 CPU.
> This CPU has no cache, so the optimization for speed is equivalent of
> optimization for size.
Not really. Not all bytes in the instruction flow take the same
amount of time -- a multiply of AX with another register, and a
move between two registers, are both the same size, but the
multiply will (from memory) take over 50 times more time than
the move.
> Assume a compiler using the common __cdecl calling convention
> which requires arguments to be pushed from right to left.
I'm not sure what you mean by the __cdecl convention, but the
usual convention doesn't require anything to be pushed -- I
don't actually see how it could, since the called function has
no way of determining how the arguments got where they were.
> f(g(),h());
> With a right-to-left evaluation order, a function call is done like th=
e
> followin:
> call near ptr h
> push ax
> call near ptr g
> push ax
> call near ptr f
> add sp,4
Or (more likely):
sub sp,8 # (At the start of the function,
# adding enough for the parameters
# of all of the functions called.
call h
mov [bp+whatever],ax
call g
mov [bp+whatever],ax
call f
mov bp,sp # at the end of the function.
Don't forget that push is one of the more expensive
instructions.
> Total CPU cycles for the "function call" : "push ax" + "push ax" +
> "call near ptr f"+ "add sp,4":
> 15+15+23+4 =3D 57
I presume that the 15 is for the push. I'm no longer very sure
(it's been 25 years since I taught ASM 86), but I think that's
more than the mov [bp+x],ax.
> Push operations requires only 1 machine code byte, and a few
> CPU cycles.
If you consider 15 a few. I suspect that in most cases, my
strategy will be faster.
> With a left-to-right evaluation order, the stack can only be
> sensibly accessed with the bp register. sp can't be used, and
> bx, si and di use the ds segment instead of the ss segment. A
> segment prefix opcode could be used, but it is slow.
> Moreover, each register is precious, so we mustn't lose bx, si
> or di.
> At best, the compiler (if it is smart enough) can write to
> parameters via the bp register like that:
> sub sp,4
> call near ptr g
> mov [bp - ... ], ax
> call near ptr h
> mov [bp - ... ], ax
> call near ptr f
> add sp,4
> CPU cycles for the "function call" :
> 4+(13+9)+(13+9)+23+4 =3D 75
> The function call is significantly slower
With my strategy, there's no difference. But I wonder about
what you mean by significantly. The three calls take so much
time on the processor you're talking about that anything else is
just chicken feed.
> But, if the compiler allows dynamic allocation on the stack
> (it is not standard in C++, but it can be a sensible
> implementation of VLA in a C compiler...) via an
> implementation-specific alloca function, then the compiler
> can't know the quantity of space between bp and sp, and thus
> must do that:
> push bp
> mov bp,sp
> sub sp,4
> call near ptr g
> mov [bp-4],ax
> call near ptr h
> mov [bp-2],ax
> call near ptr f
> mov sp,bp
> pop bp
> CPU cycles for the "function call" :
> 15+2+4+(13+9)+(13+9)+23+2+12 =3D 102
> Okay, for a two-arguments function, there is a better way:
> call near ptr h
> push ax
> call near ptr g
> pop cx
> push ax
> push cx
> call near ptr f
> add sp,4
> CPU cycles for the "function call"
> 15+12+15+15+23+4 =3D 84
Except that normally, the push bp; mov bp,sp; sub sp,x and the
mov sp,bp; pop bp are there anyway.
> And, if a register variable is free (which is very seldom,
> since there are only two register for register variables : si
> and di). It can be reduced to:
> call near ptr h
> mov di,ax
> call near ptr g
> push ax
> push di
> call near ptr f
> add sp,4
> CPU cycles for the "function call"
> 2+15+15+23+4 =3D 59
> But, the slow way is the only sensible implementation I see
> for a function having more parameters (3 or more).
> And, there are probably CPU for which that would be worst!
I doubt that. The 8086 architecture is about the worst ever
invented for optimization.
But you seem to be making a lot of assumptions about the way the
compiler works. If the functions f() and g() are very small,
they can be made inline, and the compiler can do whatever it
likes with them. And if they aren't extremely trivial, any
difference in time here (which will never be more than a couple
of cycles) will be negligible compared to the time spent in the
functions.
> Calling sequences can already be very problematic for some CPU :
> Look at http://cm.bell-labs.com/cm/cs/who/dmr/clcs.html
Which was written when? Are you trying to say that there has
been no progress in the last 25 years?
--=20
James Kanze kanze.james@neuf.fr
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France +33 (0)1 30 23 00 34
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Michael.Karcher@writeme.com (Michael Karcher)
Date: 14 Apr 2006 22:10:02 GMT Raw View
James Kanze <kanze.james@neuf.fr> wrote:
> That's what the standard says (and I suspect that Scott knows
> this). The problem Scott has is more likely knowing when each
> of the above applies. (There's also the problem that although
> the standard requires an implementation to document
> implementation defined behavior, to my knowledge none do.)
Whats wrong with 'info gcc "C Implementation"'?
Michael Karcher
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: francis@robinton.demon.co.uk (Francis Glassborow)
Date: Sat, 15 Apr 2006 00:50:23 GMT Raw View
In article <e1p3fi$bj2$1@emma.aioe.org>, James Kanze
<kanze.james@neuf.fr> writes
>That's what the standard says (and I suspect that Scott knows
>this). The problem Scott has is more likely knowing when each
>of the above applies. (There's also the problem that although
>the standard requires an implementation to document
>implementation defined behavior, to my knowledge none do.)
The problem is that it seems that the choices for implementation defined
behaviour must always be ones that allow a program to continue
execution.
The problem with this is that it makes overflow of signed and floating
point values undefined behaviour. If we allowed an implementation to
specify that, for example, overflow of an int value results in the
program terminating we could then provide well-defined behaviour for all
the cases where the implementation does not go to that extreme.
When we couple unspecified order of evaluation with the undefined nature
of overflow of an int value we finish up with forcing very tortuous code
on programmers who do not need portability but do need behaviour
guarantees.
I am well aware that the elite can program round this form of undefined
behaviour but requiring such avoidance seriously reduces programmer
productivity and adds too many opportunities for buggy code.
I believe that Scott shares my concern for the need to make undefined
behaviour easier to avoid and less demanding of expertise.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: kuyper@wizard.net
Date: 15 Apr 2006 01:00:10 GMT Raw View
"Tom s" wrote:
>
> So at the end of the day, you're either writing portable code, or platform
> specific code. For both, avoid undefined behaviour.
Not quite. In the context of the C++ standard, "undefined behaviour"
means only that the C++ stanadard chooses not to provide any definition
for the behavior. That doesn't prevent a particular implementation from
providing it's own, unportable definition of the behavior. It's
commonplace for extensions to be implemented by defining
standard-undefined behavior. For instance, it's commonplace for an
implementation to implement an extension by requiring #inclusion of a
non-standard header file that defines or declares identifiers whose
names are reserved to the implementation, which has undefined behavior
according to 17.4.3.1p3. There's nothing wrong with code that's
intended to be non-portable relying on a particular implementation's
definition of behavior that is otherwise undefined.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: SeeWebsiteForEmail@erdani.org ("Andrei Alexandrescu (See Website For Email)")
Date: Sat, 15 Apr 2006 02:24:34 GMT Raw View
SuperKoko wrote:
> Assume that the order of evaluation were defined (for example left to
> right), and a sequence point was included between two arguments
> evaluation (which may already reduces the efficiency of strongly
> optimizing compilers on superscalar micro-processors).
>
> But, I'll deem a realistic example.
> The 8088 CPU.
> This CPU has no cache, so the optimization for speed is equivalent of
> optimization for size.
It is quite understood that right-to-left evaluation appears to be more
natural given a stack-based calling convention. However, I conjecture
that the performance differences are practically nonexistent on today's
architectures.
> At best, the compiler (if it is smart enough) can write to parameters
> via the bp register like that:
>
> sub sp,4
> call near ptr g
> mov [bp - ... ], ax
> call near ptr h
> mov [bp - ... ], ax
> call near ptr f
> add sp,4
>
> CPU cycles for the "function call" :
> 4+(13+9)+(13+9)+23+4 = 75
>
> The function call is significantly slower
It could be argued that the function call can be faster. Each PUSH will
decrement the stack register. PUSH looks small, but it's not; it entails
a fair amount of microcode, and since we restrict ourselves to 8088,
that microcode will execute slowly, too - only a tad faster than the
equivalent assembly code. Or is it just as slow? I don't remember.
In contrast, this other method computes the stack frame of f()
statically, and then writes to the stack without needing to manipulate
sp. As a consequence, there are less RAW dependencies and the potential
for parallelization is superior (a potential not illustrated by the
example at hand).
But, as I said: on today's architectures, the differences in speed are
immesureable.
> And, there are probably CPU for which that would be worst!
> Calling sequences can already be very problematic for some CPU :
> Look at http://cm.bell-labs.com/cm/cs/who/dmr/clcs.html
That document was written 25 years ago. I failed to find a newer one
that discusses the same issues.
Andrei
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Andrei Alexandrescu (See Website For Email)" <SeeWebsiteForEmail@erdani.org>
Date: Fri, 14 Apr 2006 21:29:25 CST Raw View
James Kanze wrote:
> We all know that we should avoid undefined behavior. The
> problem with undefined behavior, however, is that it isn't
> always detectable. You make a mistake, and it doesn't have any
> apparent symptoms. For the moment.
It's not detectable _efficiently_ and _with today's technology_. There
are less efficient languages with no undefined behavior at all. And IMHO
there's quite some gratuitous un*d behavior in C++.
Andrei
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: nagle@animats.com (John Nagle)
Date: Sat, 15 Apr 2006 06:08:21 GMT Raw View
Scott Meyers wrote:
> What work has been done on eliminating undefined behavior in C or C++?
> Off the top of my head, some undefined behavior is due to "stupid user
> behavior," e.g., indexing beyond the end of an array, dereferencing a
> null pointer, etc. Other undefined behavior -- or maybe it's
> unspecified or implementation-defined behavior, I can never keep those
> terms straight -- arises from flexibility for implementers, e.g., order
> of evaluating arguments for function calls, order of expression
> evaluation between sequence points, order of initialization of non-local
> statics defined in separate translation units. I'd be interested in any
> work that's been done on identifying these kinds of behavior (beyond
> grepping the standards for "undefined", "unspecified", etc.) and
> suggesting ways to eliminate or avoid them, ideally without any obvious
> runtime cost. (Checking array bounds would fail the "no obvious runtime
> cost" test, while defining function argument evaluation order would
> pass, because, well, because it's not obvious to me that imposing a
> required order would necessarily incur a runtime penalty :-})
There really is more than one type of undefined behavior, and
classifying the ones in the C++ specification might be useful.
A few obvious classes:
-- Violations of the storage model
Examples:
-- Indexing beyond the limits of an array
-- Use of an iterator after an invalidating operation on its collection
-- Violations of the construction model
Examples:
-- Access to an object before the constructor has completed or after
the destructor has run (includes references to uninitialized variables)
-- Casting into a type for which not all possible values are valid
(pointers, constructed objects)
-- Violations of the sequencing model
Examples:
-- An expression with calls to two functions with (interfering?) side effects
for which the evaluation rules do not explicitly determine the order
of evaluation
-- Violations of the concurrency model (insofar as C++ has one)
Examples:
-- Non-volatile data shared between two threads.
With a taxonomy like this, some issues become clearer. The last three
categories, for example, are potentially detectable at compile time.
The issues for those categories revolve around whether certain
formally undefined behavior should be permitted.
John Nagle
Animats
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Pete Becker <petebecker@acm.org>
Date: 15 Apr 2006 19:50:01 GMT Raw View
James Kanze wrote:
>
> That's what the standard says (and I suspect that Scott knows
> this).
It's important for people reading a discussion to know what the
technical terms mean. This is an area where many people are confused,
and it's not particularly important that you and I understand it or that
you think that Scott does.
--
Pete Becker
Roundhouse Consulting, Ltd.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: James Kanze <kanze.james@neuf.fr>
Date: Sat, 15 Apr 2006 14:49:41 CST Raw View
Francis Glassborow wrote:
> In article <e1p3fi$bj2$1@emma.aioe.org>, James Kanze
> <kanze.james@neuf.fr> writes
>> That's what the standard says (and I suspect that Scott knows
>> this). The problem Scott has is more likely knowing when
>> each of the above applies. (There's also the problem that
>> although the standard requires an implementation to document
>> implementation defined behavior, to my knowledge none do.)
> The problem is that it seems that the choices for
> implementation defined behaviour must always be ones that
> allow a program to continue execution.
There's at least one exception -- an implementation is allowed
to raise an implementation defined signal in the case of a
narrowing conversion that doesn't fit.
> The problem with this is that it makes overflow of signed and
> floating point values undefined behaviour. If we allowed an
> implementation to specify that, for example, overflow of an
> int value results in the program terminating we could then
> provide well-defined behaviour for all the cases where the
> implementation does not go to that extreme.
C99 explicitly does, at least for conversions (but not for
arithmetic operations -- I suspect that it was felt that this
could depend on optimization settings, and would be difficult,
if not impossible, to document). From what I understand, the
explicit authorization was considered a clarification, rather
than a change, of C90. So presumably, this is the intent of C++
as well.
> When we couple unspecified order of evaluation with the
> undefined nature of overflow of an int value we finish up with
> forcing very tortuous code on programmers who do not need
> portability but do need behaviour guarantees.
The unspecified order of evaluation can be a problem in a few
specific cases, but I can't see where the undefined behavior of
arithmetic overflow is a problem (in this regard -- it does
cause problems otherwise). Good programmers, regardless of the
language, don't write programs with integer overflow. The
"undefined behavior" aspect only comes into play with regards to
the reproducibility of tests, and in this case, it's not really
much of a problem in practice.
> I am well aware that the elite can program round this form of
> undefined behaviour but requiring such avoidance seriously
> reduces programmer productivity and adds too many
> opportunities for buggy code.
I don't understand the "elite" part -- code which might overflow
doesn't get through code review, and it's not that difficult to
avoid. (We are talking about integral arithmetic here.) And
it's something that has to be done, so I don't see how it
affects programmer productivity. (Typically, it's part of input
data validation. And a negligeable part, at that.)
> I believe that Scott shares my concern for the need to make
> undefined behaviour easier to avoid and less demanding of
> expertise.
There's no disagreement there. But the undefined behavior in
the case of integral overflow is probably one of the least
bothersome cases (and also one with the most execution time
implications -- unless there is special hardward to support it).
There's also agreement on my part that some intermediate forms
might be in order. I find it totally unacceptable that it be
"undefined behavior" if I use std::string without having
included <string> (but having included other standard headers).
In fact, either the code will compile and work, or I will get an
error at compile time, depending on whether one of the other
headers I included included <string>. No other behaviors are
really possible.
--
James Kanze kanze.james@neuf.fr
Conseils en informatique orient e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S mard, 78210 St.-Cyr-l' cole, France +33 (0)1 30 23 00 34
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: James Kanze <kanze.james@neuf.fr>
Date: Sat, 15 Apr 2006 23:39:22 CST Raw View
Francis Glassborow wrote:
> In article <e1p3fi$bj2$1@emma.aioe.org>, James Kanze
> <kanze.james@neuf.fr> writes
>> That's what the standard says (and I suspect that Scott knows
>> this). The problem Scott has is more likely knowing when
>> each of the above applies. (There's also the problem that
>> although the standard requires an implementation to document
>> implementation defined behavior, to my knowledge none do.)
> The problem is that it seems that the choices for
> implementation defined behaviour must always be ones that
> allow a program to continue execution.
There's at least one exception -- an implementation is allowed
to raise an implementation defined signal in the case of a
narrowing conversion that doesn't fit.
> The problem with this is that it makes overflow of signed and
> floating point values undefined behaviour. If we allowed an
> implementation to specify that, for example, overflow of an
> int value results in the program terminating we could then
> provide well-defined behaviour for all the cases where the
> implementation does not go to that extreme.
C99 explicitly does, at least for conversions (but not for
arithmetic operations -- I suspect that it was felt that this
could depend on optimization settings, and would be difficult,
if not impossible, to document). From what I understand, the
explicit authorization was considered a clarification, rather
than a change, of C90. So presumably, this is the intent of C++
as well.
> When we couple unspecified order of evaluation with the
> undefined nature of overflow of an int value we finish up with
> forcing very tortuous code on programmers who do not need
> portability but do need behaviour guarantees.
The unspecified order of evaluation can be a problem in a few
specific cases, but I can't see where the undefined behavior of
arithmetic overflow is a problem (in this regard -- it does
cause problems otherwise). Good programmers, regardless of the
language, don't write programs with integer overflow. The
"undefined behavior" aspect only comes into play with regards to
the reproducibility of tests, and in this case, it's not really
much of a problem in practice.
> I am well aware that the elite can program round this form of
> undefined behaviour but requiring such avoidance seriously
> reduces programmer productivity and adds too many
> opportunities for buggy code.
I don't understand the "elite" part -- code which might overflow
doesn't get through code review, and it's not that difficult to
avoid. (We are talking about integral arithmetic here.) And
it's something that has to be done, so I don't see how it
affects programmer productivity. (Typically, it's part of input
data validation. And a negligeable part, at that.)
> I believe that Scott shares my concern for the need to make
> undefined behaviour easier to avoid and less demanding of
> expertise.
There's no disagreement there. But the undefined behavior in
the case of integral overflow is probably one of the least
bothersome cases (and also one with the most execution time
implications -- unless there is special hardward to support it).
There's also agreement on my part that some intermediate forms
might be in order. I find it totally unacceptable that it be
"undefined behavior" if I use std::string without having
included <string> (but having included other standard headers).
In fact, either the code will compile and work, or I will get an
error at compile time, depending on whether one of the other
headers I included included <string>. No other behaviors are
really possible.
--
James Kanze kanze.james@neuf.fr
Conseils en informatique orient e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S mard, 78210 St.-Cyr-l' cole, France +33 (0)1 30 23 00 34
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "SuperKoko" <tabkannaz@yahoo.fr>
Date: 16 Apr 2006 04:50:05 GMT Raw View
"Andrei Alexandrescu See Website For Email a crit :
> It could be argued that the function call can be faster. Each PUSH will
> decrement the stack register. PUSH looks small, but it's not; it entails
> a fair amount of microcode, and since we restrict ourselves to 8088,
> that microcode will execute slowly, too - only a tad faster than the
> equivalent assembly code. Or is it just as slow? I don't remember.
>
You know, I exactly counted the CPU cycles needed!
And, modern compilers have dependencies on push operation, but not the
8088.
And, mov [bp - ...],ax is slower than push ( 22 cycles instead of 15
cycles).
There is also the problem of accessing the stack with bp... as I said,
with a three-parameters function, the compiler needs to save the old
bp, install a new bp, mov arguments, and then restore the old bp.
IMHO 102 cycles is significantly more than 57 cycles.
> In contrast, this other method computes the stack frame of f()
> statically, and then writes to the stack without needing to manipulate
> sp. As a consequence, there are less RAW dependencies and the potential
> for parallelization is superior (a potential not illustrated by the
> example at hand).
Only on some modern architectures.
But, C++ is a general purpose language meant to be portable on many
CPU, not only PowerPC and PC.
Anyway, I think that, for performance reasons, if the C++ standard
specifies all order of evaulation (especially for the order of side
effects), optimizing compilers will have compiler switches in order to
get the old behaviour.
On superscalar CPU, the compiler freedom of order of evaluation of side
effects reduces assembly instruction dependencies.
And, as soon as references/pointers are used, compilers have much
difficulty to make no-alias optimizations. Actually, they are at least
allowed to do no-alias optimizations between two sequence points.
However I think that there are many implementation-defined behaviour
that could be removed.
For example, the signedness of char.
I would opt for specifying that char is unsigned (even if it will need
backward compatibilities switches on some popular compilers), because
there are machines that have not a bijection between the values of bits
of a signed char, and the value of the signed char.
typeinfo.name() should be specified, instead of being unspecified!
Remove undefined behaviours of the "lexical conventions" chapter, and
replace them by ill-formed programs, and implementation-defined
behaviours.
To give an example:
2.8 Header names [lex.header]
header-name:
<h-char-sequence>
"q-char-sequence"
h-char-sequence:
h-char
h-char-sequence h-char
h-char:
any member of the source character set except
new-line and >
q-char-sequence:
q-char
q-char-sequence q-char
q-char:
any member of the source character set except
new-line and "
2 If either of the characters ' or \, or either of the
character
sequences /* or // appears in a q-char-sequence or a
h-char-sequence,
or the character " appears in a h-char-sequence, the behavior
is
undefined.7)
IMHO, that is really a gratuitous "undefined behaviour".
Another example:
3 In translation phase 6 (_lex.phases_), adjacent narrow string
literals
are concatenated and adjacent wide string literals are
concatenated.
If a narrow string literal token is adjacent to a wide string
literal
token, the behavior is undefined.
IMHO, it should yield a wide string litteral, or it could also be
"ill-formed".
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: James Dennett <jdennett@cox.net>
Date: Sun, 16 Apr 2006 11:57:42 CST Raw View
SuperKoko wrote:
>
> 3 In translation phase 6 (_lex.phases_), adjacent narrow string
> literals
> are concatenated and adjacent wide string literals are
> concatenated.
> If a narrow string literal token is adjacent to a wide string
> literal
> token, the behavior is undefined.
>
> IMHO, it should yield a wide string litteral, or it could also be
> "ill-formed".
That one has been addressed; C99 made it yield a wide
string literal, and IIRC that change has been adopted
into the working draft for C++.
-- James
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: kanze.james@neuf.fr (James Kanze)
Date: Sun, 16 Apr 2006 16:58:06 GMT Raw View
SuperKoko wrote:
> "Andrei Alexandrescu See Website For Email a =E9crit :
>> It could be argued that the function call can be faster. Each
>> PUSH will decrement the stack register. PUSH looks small, but
>> it's not; it entails a fair amount of microcode, and since we
>> restrict ourselves to 8088, that microcode will execute
>> slowly, too - only a tad faster than the equivalent assembly
>> code. Or is it just as slow? I don't remember.
> You know, I exactly counted the CPU cycles needed! And,
> modern compilers have dependencies on push operation, but not
> the 8088.
You didn't count the time spent in the function called.
> And, mov [bp - ...],ax is slower than push ( 22 cycles instead
> of 15 cycles).
And the call itself is how many? And how many are executed in
the function? And 6 over that number is?
> There is also the problem of accessing the stack with bp... as
> I said, with a three-parameters function, the compiler needs
> to save the old bp, install a new bp, mov arguments, and then
> restore the old bp. IMHO 102 cycles is significantly more
> than 57 cycles.
The compilers I've used that didn't inline trivial functions
didn't skip generating a stack frame in non-leaf functions
either. So a new bp gets installed, regardless.
>> In contrast, this other method computes the stack frame of
>> f() statically, and then writes to the stack without needing
>> to manipulate sp. As a consequence, there are less RAW
>> dependencies and the potential for parallelization is
>> superior (a potential not illustrated by the example at
>> hand).
> Only on some modern architectures.
> But, C++ is a general purpose language meant to be portable on
> many CPU, not only PowerPC and PC.
> Anyway, I think that, for performance reasons, if the C++
> standard specifies all order of evaulation (especially for the
> order of side effects), optimizing compilers will have
> compiler switches in order to get the old behaviour.
If the compiler vendors had documented it, or if they believe
that some users count on it.
> On superscalar CPU, the compiler freedom of order of
> evaluation of side effects reduces assembly instruction
> dependencies. And, as soon as references/pointers are used,
> compilers have much difficulty to make no-alias optimizations.
> Actually, they are at least allowed to do no-alias
> optimizations between two sequence points.
A compiler is allowed to do anything it wants, as long as the
observable behavior is respected. If there are no function
calls in the expressions, the compiler can see all of the side
effects, and can rearrange the order as it wishes. Even today,
the standard forbids interleaving functions, but if those
functions are inline, and the compiler can see that they have no
side effects, modern compilers do so.
--=20
James Kanze kanze.james@neuf.fr
Conseils en informatique orient=E9e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S=E9mard, 78210 St.-Cyr-l'=C9cole, France +33 (0)1 30 23 00 34
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: francis@robinton.demon.co.uk (Francis Glassborow)
Date: Sun, 16 Apr 2006 18:19:10 GMT Raw View
In article <e1q8q9$1se$1@emma.aioe.org>, James Kanze
<kanze.james@neuf.fr> writes
>The unspecified order of evaluation can be a problem in a few
>specific cases, but I can't see where the undefined behavior of
>arithmetic overflow is a problem (in this regard -- it does
>cause problems otherwise). Good programmers, regardless of the
>language, don't write programs with integer overflow. The
>"undefined behavior" aspect only comes into play with regards to
>the reproducibility of tests, and in this case, it's not really
>much of a problem in practice.
'Good programmers' == 'Elite programmers' ? :-)
And yes, I think you are right re the non-interaction of order of
evaluation and overflow because the order of evaluation of operators is
defined.
However I remain profoundly uncomfortable with the fact that this simple
program has undefined behaviour:
#include <iostream>
int main(){
int i(2);
int j(0);
std::cout << "Please input a number: ";
std::cin >> j;
if(std::cin.good(){
j *= 2;
}
else {
std::cout << "That was not accepted as a number. \n";
}
}
Yes I know how to clean that one up, and I know what assumptions I have
made which should be made explicit. However every time I do arithmetic I
have to go through the same thought and error trapping. IOWs the error
trapping becomes part of the normal code. That is not the C++ way,
suspect operations throw exceptions or work for some definition of
'work' which might not be the one the programmer expected.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: "Greg Herlihy" <greghe@pacbell.net>
Date: 16 Apr 2006 18:20:05 GMT Raw View
Scott Meyers wrote:
> What work has been done on eliminating undefined behavior in C or C++? Off the
> top of my head, some undefined behavior is due to "stupid user behavior," e.g.,
> indexing beyond the end of an array, dereferencing a null pointer, etc. Other
> undefined behavior -- or maybe it's unspecified or implementation-defined
> behavior, I can never keep those terms straight -- arises from flexibility for
> implementers, e.g., order of evaluating arguments for function calls, order of
> expression evaluation between sequence points, order of initialization of
> non-local statics defined in separate translation units. I'd be interested in
> any work that's been done on identifying these kinds of behavior (beyond
> grepping the standards for "undefined", "unspecified", etc.) and suggesting ways
> to eliminate or avoid them, ideally without any obvious runtime cost. (Checking
> array bounds would fail the "no obvious runtime cost" test, while defining
> function argument evaluation order would pass, because, well, because it's not
> obvious to me that imposing a required order would necessarily incur a runtime
> penalty :-})
>
> My motivation for the question is that it's my understanding that
> safety-critical applications typically identify language subsets to reduce risk,
> and they then typically impose usage rules on the features remaining in the
> subsets to further reduce risk. One form of risk is unpredictable program
> behavior, and such behavior can arise from the use of language constructs with
> undefined/unspecified/implementation-defined behavior, so I'm wondering what
> work has been done on identifying such constructs and dealing with them.
A good starting point for investigating C++'s undefined (et al.)
behaviors would be this paper: "An investigation of the unpredictable
features of the C++ language." The authors actually take a complete
inventory of every unpredictable behavior mentioned in the Standard,
including both the C++ language and the Standard Library.
Here are the grand totals from the paper's tally:
Behavior Language Library Total
Undefined 77 29 106
Unspecified 25 25 50
Indeterminate 5 0 5
Implementation 58 23 81
No diagnostic 18 0 18
Here's a link to the paper:
http://www.aitcnet.org/isai/Resources/C++%20language%20issues%20(unlimited%20and%20signed).pdf
Well, I guess we can all be heartened by the fact that the Library has
no indeterminate behavior.
Greg
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: aon.912719634@aon.at (Nicolas Pavlidis)
Date: Sun, 16 Apr 2006 21:41:55 GMT Raw View
Hi!
Again a disclaimer: I'm far away from beeing an expert, but I whant to
take this thread to note some things, which seem anoying to me.
NULL@NULL.NULL wrote:
> Scott Meyers posted:
>
>> What work has been done on eliminating undefined behavior in C or C++?
>> Off the top of my head, some undefined behavior is due to "stupid user
>> behavior," e.g., indexing beyond the end of an array, dereferencing a
>> null pointer, etc.
>
>
> I agree. Stuff like the following is just "stupid":
>
> int *p;
>
> *p = 5; //Undefined Behaviour
>
This is one thing, and in that case its really stupid, but, the same
behaviour can happen across longer code, with some function - calls
inside, AFAIK this code is also undefined:
int my_int; // missing initialisation
cout << my_int << endl;
Why is it impossible to let the compiler do the initialisation for me, I
have to do it anyway, either in my code, where I can leave it otu, or,
on the secure side, the compiler does tyhis thing for me.
>> Other undefined behavior -- or maybe it's
>> unspecified or implementation-defined behavior, I can never keep those
>> terms straight -- arises from flexibility for implementers, e.g., order
>> of evaluating arguments for function calls, order of expression
>> evaluation between sequence points, order of initialization of
>> non-local statics defined in separate translation units.
>
> You're either writing:
>
> a) Portable 100% Standard-compliant Code
Even if I do that, I can run into problems, think of std::list::front.
If the list is empyt everythign can happen, again the question, why not
pakking the code into the method. Why should the user must care about
the inetrnal state of the list, ind this example.
he typemixing is another problem, that is solved by specific compilers,
they warn you at least, why not forbis some things, you all have called
stupid,if they are soo stupid?
So long, thanks for reading :-).
Best regards,
Nicolas
--
| Nicolas Pavlidis | Elvis Presly: |
| Student of SE & KM | "Into the goto" |
| pavnic@sbox.tugraz.at | ICQ #320057056 |
| ------------University of Technology, Graz-----------------
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]
Author: Pete Becker <petebecker@acm.org>
Date: Sun, 16 Apr 2006 18:19:06 CST Raw View
Francis Glassborow wrote:
>
> Yes I know how to clean that one up, and I know what assumptions I have
> made which should be made explicit.
Any program that doesn't validate its input is fundamentally broken. It
is, indeed, "the C++ way" to not impose overhead to give the illusion
that broken programs somehow work. It is not a service to programmers to
encourage them to write broken code.
> However every time I do arithmetic I
> have to go through the same thought and error trapping.
No, you don't. Once you've validated the input you don't have to keep
rechecking it.
> IOWs the error
> trapping becomes part of the normal code.
Input validation ought to be part of the normal code. Anything else is
too little, too late.
--
Pete Becker
Roundhouse Consulting, Ltd.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]