Topic: Checking for this==NULL


Author: AllanW@my-dejanews.com
Date: 1998/07/22
Raw View
In article <6p2ek6$mdq$1@nnrp1.dejanews.com>,
  jkanze@otelo.ibmmail.com wrote:
> In article <6p03df$aac$1@nnrp1.dejanews.com>,
>   AllanW@my-dejanews.com wrote:
> > Neither of the assertions is guaranteed to fire.
>
> Both are guaranteed to fire.  Think about it for a moment; instead of
> an assert, replace it with an if -- do one thing if the pointer is null,
> another if not.  This has to work, or you couldn't use null pointers
> for anything.

Yes, I realized it about 5 minutes after I wrote it.  There's a
followup post around, somewhere.

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Al Stevens" <alstevens@midifitz.com>
Date: 1998/07/22
Raw View
>> if (typeid(*this) == typeid(Class))
>
>This would fail if class Der was derived from class Class.

How about dynamic_cast instead?

> Even if this
>didn't happen, it might defeat the whole purpose; having the compiler
>agree that this should be a Class object doesn't guarantee that the
>object is valid.


It's not the compiler doing the agreeing, it's happening at runtime when
this is dereferenced. In any event, it gets you one test closer than simply
testing for non-NULL.



[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: christian.bau@isltd.insignia.com (Christian Bau)
Date: 1998/07/22
Raw View
In article <35B4B928.41C6@wizard.net>, James Kuyper <kuyper@wizard.net> wrote:

> Do you know of any technique that can be used to detect all invalid
> pointers?

Not in Standard C or Standard C++.
As soon as you even look at an invalid pointer you get undefined behavior.



[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: AllanW@my-dejanews.com
Date: 1998/07/22
Raw View
In article <35b4db4a.0@newsread.exodus.net>,
  "Matt Seitz" <mseitz@meridian-data.com> wrote:
> Perhaps a way to get the benefit without the harm is to add a comment to the
> test.  By telling the programmer that this test may pass, even if it is
> being called with a NULL pointer, the programmer will not be lulled into a
> false sense of security.

I have always advocated that.

Of course, this moves the problem, rather than erasing it.

Originally, we had to educate the programmers that debug the code.
"If you see assert(this) in the code, and the assert isn't firing,
don't assume that the NULL pointer problem isn't happening."

Now, we ALSO have to educate the programmers that write the assertion.
"If you assert(NULL!=this) in your code, be sure to put in a comment
so that other programmers don't think it's 100% effective."  (We
assume that they already know that the assert isn't 100% effective;
we're educating them on the importance of the comment.)

This leads us very close to that excellent state, where all
programmers know the value and limits of all the tools at their
disposal, including various assert statements.  While they never
assume they're more powerful than they are, they also use them
to maximum results...  Hey, we am all perfeshinals here, most of
us kollidge-edjikated; we kin handle these here new-fangled
concepts without battin' an i-brow!

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Christopher Eltschka <celtschk@physik.tu-muenchen.de>
Date: 1998/07/21
Raw View
James Kuyper wrote:
>
> David Abrahams wrote:
> >
> > On 16 Jul 98 03:11:20 GMT, AllanW@my-dejanews.com wrote:
> ...
> > >Absolutely.  Once I've asserted that this!=NULL, I can be ABSOLUTELY
> > >CERTAIN that this particular problem hasn't occured.
> >
> > Wrong-o. See above.
>
> If you verify that "this!=NULL", then the particular problem caused by
> the possibility that "this==NULL" hasn't occured. If you make the
> mistake of inferring some larger degree of protection, that's your
> fault, not a drawback of the technique itself. As has been pointed out,
> under some (perhaps most) implementations, it is possible to call a
> non-virtual member function using a NULL this. The test catches one of
> many possible problems. The fact that there are many other problems that
> it doesn't catch is not an argument against making the test.
>
> If a given medicine protects against the common cold, would you refuse
> to take it just because it doesn't protect you against anything else?

Would you buy a medicine which *may* protect you against the common
cold, depending on facts which you normally don't consider, say the
number of Adenosines on your Chromosome 5?
This is what he told you: The this!=NULL check doesn't reliably
prevent you from calling member functions with NULL pointers.
Indeed, there are a lot of situations where it probably will not
detect that fault. But since it *looks* as if it catched that
error reliably, you might end up thinking that error didn't occur.
Also note that looking at this in the debugger doesn't help here,
you really have to look at the pointer the member was called upon.
It's like buying an insurance which protects you against car
accidents, but only if you drive on certain roads (where the type of
road may even depend on the state you are driving in, and in general
may not be trivial to find out). Would you buy that insurance?


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: AllanW@my-dejanews.com
Date: 1998/07/21
Raw View
In article <6p03df$aac$1@nnrp1.dejanews.com>,
  AllanW@my-dejanews.com wrote:
> In article <6oncha$t1h@news3.euro.net>,
>   "Martijn Lievaart" <Invalid@against.spam.nl> wrote:
> > So what will this do?
> >
> > struct A { int x; };
> > struct B { int x; };
> > struct C : A, B {};
> > void a(A *p) { assert(p != NULL); }
> > void b(B *p) { assert(p != NULL); }
> >
> > int main()
> > {
> >   C* c = 0;
> >   a(c);
> >   b(c);
> > }
> >
> > If I understand correctly than either of the assertions may or may not
fire?
> > This would make these asserts as worthless as the assert(this!=NULL) case
> > and the whole idea of testing on NULL wouldn't work. This wich would
further
> > strengthen your last argument. I may even put passing NULL ptrs in the way
> > you described above on my "don't ever do this" list (Even more effictive
C++
> > ;^>). Please prove me wrong.
>
> Neither of the assertions is guaranteed to fire.  However, they are
> NOT worthless -- because chances are excellent that at least one of
> them WILL fire.  And tracking down the cause of a failed assert is
> much easier than tracking down the cause of a segment violation, or
> whatever else might happen with invalid use of a null pointer.

Forget I said that.  I was thinking of calling member functions,
not calling global functions.

This program is different, and both assert()s WILL trigger, because it's
legal in C++ to call a function and pass a null pointer to it.  In that
case, the standard conversion applies.  But the standard conversion is
more complicated than you might think.

A complete C object contains an A sub-object and a B sub-object.  So
to convert from a C* to an A* requires adding the offset of the A
sub-object, and converting from a C* to a B* requires adding the
offset of the B sub-object.  There are two exceptions.

The first exception occurs when the C* is NULL.  (Note that this
does not need to be represented by a bit pattern of all zeroes.
Any unique value is allowed by the language).  The language does
say that converting a NULL C* to a NULL B* will still result in
the value NULL.  In practice, this often means comparing the C* to
NULL, and adding B's offset if and only if the C* was not NULL.
[There are cases where this can be easily optimized away, but it's
true in general.]  This check requires time, of course, and adds
at least a few assembly instructions overhead to your executable
program, but it's absolutely required for compliance with the C++
standard.

The second exception occurs when the offset is zero.  Let's assume
that the A sub-object physically comes first, and that there is no
vtable.  (This is not required by the spec, but it's common enough.)
In that case, the offset of the A sub-object could be 0.  If so,
then converting from a C* to an A* is simply a matter of adding 0 --
which is the same as doing nothing.

So in the example program above, the calls to a() and b() are
required to check to see that the C* is NULL, and to respect this
by passing NULL to parameter p in functions a() and b().
Therefore, the assert will trigger.

...So why is it different with member functions?

    c->bfunc();  // Assume struct B defines B::bfunc()

The answer is that this call is illegal when c is NULL.  When you do
it, the compiler is allowed to generate any code that it wants to.

For instance, it's perfectly legal for the compiler to do a standard
conversion from C* to B*, by inserting the extra assembly-language
statements needed to check if the C* is null, and only add the
offset of B in a C object if the original C* is not null.  In this
case, bfunc() will be called with a NULL pointer for "this" -- and
it's assert (let's assume it has one) will trigger.

But wait a minute.  In this scenario, we've already got
assembly-language instructions used to detect a NULL C* used for a
member function call.  Once we find it, is calling bfunc() with a
NULL "this" pointer the best we can come up with?  It would make
more sense to call a run-time function which reports the error, so
that the programmer can correct it.  Indeed, the C++ standard says
nothing to prevent this.

But the program doesn't need to call a run-time function for this,
either.  Instead, it could post a message to this newsgroup (saying
"Hey, I do it too").  Or, it could format your hard disk, or
calculate the first 10,000,000 digits of the irrational number Pi.
All of these things are legal, and yet none of them is very likely.
However, there is one thing that the compiler can do about non-NULL
values which might even be considered Obvious(tm).

And that one thing is: Nothing.

Remember that checking for NULL, and applying the offset only if the
value is non-NULL, requires at least a few extra assembly language
instructions to be added to your program and executed every time the
conversion takes place.  But here's a case where the pointer is
required NOT to be NULL -- and if it is, then whatever happens, it's
the programmer's fault.  So why should programs that do this
correctly have to include the extra assembly-language instructions to
check for NULL?

They don't have to include this.  The compiler is free to simply add
the offset, without checking for the illegal NULL case first.  In
fact, that's exactly what many popular compilers do.  AFAIK, there's
only one drawback to this approach:  If function bfunc() contains a
statement like this:
    assert(this);
then this assert probably not catch the case where B's offset in C
was added to a C* (or where B's offset in D was added to a D*, etc.)

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: James Kuyper <kuyper@wizard.net>
Date: 1998/07/21
Raw View
Christopher Eltschka wrote:
>
> James Kuyper wrote:
> >
> > David Abrahams wrote:
> > >
> > > On 16 Jul 98 03:11:20 GMT, AllanW@my-dejanews.com wrote:
> > ...
> > > >Absolutely.  Once I've asserted that this!=NULL, I can be ABSOLUTELY
> > > >CERTAIN that this particular problem hasn't occured.
> > >
> > > Wrong-o. See above.
> >
> > If you verify that "this!=NULL", then the particular problem caused by
> > the possibility that "this==NULL" hasn't occured. If you make the
> > mistake of inferring some larger degree of protection, that's your
> > fault, not a drawback of the technique itself. As has been pointed out,
> > under some (perhaps most) implementations, it is possible to call a
> > non-virtual member function using a NULL this. The test catches one of
> > many possible problems. The fact that there are many other problems that
> > it doesn't catch is not an argument against making the test.
> >
> > If a given medicine protects against the common cold, would you refuse
> > to take it just because it doesn't protect you against anything else?
>
> Would you buy a medicine which *may* protect you against the common
> cold, depending on facts which you normally don't consider, say the
> number of Adenosines on your Chromosome 5?

Yes, depending upon my estimate of the probability that it would work
for me, and the cost. I don't know of any medicine that is absolutely
guaranteed to work, and the reasons for the failures are often just as
much a mystery to the doctors as they would be to me. I presume,
therefore, that you refuse all medicines?

> This is what he told you: The this!=NULL check doesn't reliably
> prevent you from calling member functions with NULL pointers.

It detects some calls using NULL pointers. That's better than letting
them occur undetected.

> Indeed, there are a lot of situations where it probably will not
> detect that fault. But since it *looks* as if it catched that
> error reliably, you might end up thinking that error didn't occur.

It doesn't look reliable to me, so I won't end up thinking that.

> Also note that looking at this in the debugger doesn't help here,
> you really have to look at the pointer the member was called upon.
> It's like buying an insurance which protects you against car
> accidents, but only if you drive on certain roads (where the type of
> road may even depend on the state you are driving in, and in general
> may not be trivial to find out). Would you buy that insurance?

Yes, if the roads where it does protect me are sufficiently common to
justify the price being charged for the coverage. Competition is
relevant here - if policies with more reliable coverage are available,
they're worth a higher premium (but not an arbitrarily large premium). I
guarantee you that your current auto insurance has lots of gaps in its
coverage. Unless you work in the insurance industry, you're probably not
even aware of what they are - examining your insurance policy is not
sufficient unless you have a strong background in insurance law.
Therefore, I assume you don't have any auto insurance? That's illegal in
most states, you know ;-)

NULL pointers are the single most common kind of invalid pointers. Most
of the proposed reasons for expecting the test to fail ignore the fact
that pointer conversions are required to preserve nullness. Thus, I
think the coverage provided by this check is significant. Whether it's
worth the cost depends upon the application.

Do you know of any technique that can be used to detect all invalid
pointers? How expensive is this technique? Until some such technique is
available, there will remain some usefulness in detecting some uses of
invalid pointers, even if that method is unreliable.


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: David R Tribble <david.tribble@noSPAM.central.beasys.com>
Date: 1998/07/21
Raw View
AllanW@my-dejanews.com wrote:
>>> ... Absolutely.  Once I've asserted that this!=NULL, I can be
>>> ABSOLUTELY >CERTAIN that this particular problem hasn't occured.

David Abrahams wrote:
>>> Wrong-o. See above.

James Kuyper wrote:
>> If you verify that "this!=NULL", then the particular problem caused
>> by the possibility that "this==NULL" hasn't occured. If you make the
>> mistake of inferring some larger degree of protection, that's your
>> fault, not a drawback of the technique itself. As has been pointed
>> out, under some (perhaps most) implementations, it is possible to
>> call a non-virtual member function using a NULL this. The test
>> catches one of many possible problems. The fact that there are many
>> other problems that it doesn't catch is not an argument against
>> making the test.
>>
>> If a given medicine protects against the common cold, would you
>> refuse to take it just because it doesn't protect you against
>> anything else?

Christopher Eltschka wrote:
> Would you buy a medicine which *may* protect you against the common
> cold, depending on facts which you normally don't consider, say the
> number of Adenosines on your Chromosome 5?
>
> This is what he told you: The this!=NULL check doesn't reliably
> prevent you from calling member functions with NULL pointers.
> Indeed, there are a lot of situations where it probably will not
> detect that fault. But since it *looks* as if it catched that
> error reliably, you might end up thinking that error didn't occur.
> Also note that looking at this in the debugger doesn't help here,
> you really have to look at the pointer the member was called upon.
> It's like buying an insurance which protects you against car
> accidents, but only if you drive on certain roads (where the type of
> road may even depend on the state you are driving in, and in general
> may not be trivial to find out). Would you buy that insurance?

So, paraphrasing the argument, thus: "Since the test only works
some of the time instead of all of the time, it isn't worth doing."
Or perhaps: "A misleading test is worse than no test at all."
In other words, a test that isn't 100% correct is a bad idea.
And leaving it out has positive benefits; the program runs faster
and isn't cluttered with almost-useless, as well as misleading,
test code.  So the program is better off without it.

Except when it crashes.

Sorry, but a test that catches 10% of the errors is better than
no test at all, which catches 0%.  We're talking about a situation
where it's impossible to catch all errors anyway, but still possible
to catch a few.  Don't the principles of good programming dictate
that we should at least try to catch as many errors as we can?

-- David R. Tribble, dtribble@technologist.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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Matt Seitz" <mseitz@meridian-data.com>
Date: 1998/07/21
Raw View
David R Tribble wrote in message
<35B4C484.3561@noSPAM.central.beasys.com>...
>So, paraphrasing the argument, thus: "Since the test only works
>some of the time instead of all of the time, it isn't worth doing."
>Or perhaps: "A misleading test is worse than no test at all."
>In other words, a test that isn't 100% correct is a bad idea.
>And leaving it out has positive benefits; the program runs faster
>and isn't cluttered with almost-useless, as well as misleading,
>test code.  So the program is better off without it.
>
>Except when it crashes.
>
>Sorry, but a test that catches 10% of the errors is better than
>no test at all, which catches 0%.  We're talking about a situation
>where it's impossible to catch all errors anyway, but still possible
>to catch a few.  Don't the principles of good programming dictate
>that we should at least try to catch as many errors as we can?


Actually, the goal should be to find and remove as many errors as we can.
The question is, does this test help or hurt us in meeting that goal?

In that light, a response to your argument is:

"Yes, when it works, the test will help us find the error.  But what happens
when the test doesn't work?  A programmer may look at that test and say,
'Well, I know the problem can't be caused by using a NULL pointer, because
then my assert would have been triggered.'  Therefore, the programmer goes
off and wastes time and may introduce new trying to find either find a
different cause or, in desperation, try to come up with some workaround that
seems to solve the problem but really doesn't."

So, in my mind the question become "Is it more likely that the test will
help (by finding the bug) or that the test will hurt (by hiding the bug)?"

Perhaps a way to get the benefit without the harm is to add a comment to the
test.  By telling the programmer that this test may pass, even if it is
being called with a NULL pointer, the programmer will not be lulled into a
false sense of security.




[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Darron Shaffer <darron.shaffer@beasys.com>
Date: 1998/07/22
Raw View
David R Tribble <david.tribble@noSPAM.central.beasys.com> writes:

] Christopher Eltschka wrote:
....
] >
] > This is what he told you: The this!=NULL check doesn't reliably
] > prevent you from calling member functions with NULL pointers.
] > Indeed, there are a lot of situations where it probably will not
] > detect that fault. But since it *looks* as if it catched that
] > error reliably, you might end up thinking that error didn't occur.
] > Also note that looking at this in the debugger doesn't help here,
] > you really have to look at the pointer the member was called upon.
]
] So, paraphrasing the argument, thus: "Since the test only works
] some of the time instead of all of the time, it isn't worth doing."
] Or perhaps: "A misleading test is worse than no test at all."
] In other words, a test that isn't 100% correct is a bad idea.
] And leaving it out has positive benefits; the program runs faster
] and isn't cluttered with almost-useless, as well as misleading,
] test code.  So the program is better off without it.
]
] Except when it crashes.
]
] Sorry, but a test that catches 10% of the errors is better than
] no test at all, which catches 0%.  We're talking about a situation
] where it's impossible to catch all errors anyway, but still possible
] to catch a few.  Don't the principles of good programming dictate
] that we should at least try to catch as many errors as we can?
]

Of course, a better solution would be a compiler switch that says
"always check pointers for NULL when calling member functions through
them".

This could fit nicely into a set of similar compiler options for use
during testing/debugging.

Darron Shaffer

--
 __  __  _
 _ ) ___ _\   Enterprise Middleware Solutions Darron J. Shaffer
 __) __    \  BEA Systems Inc.   Sr. Software Engineer
              17101 Preston Rd   darron.shaffer@beasys.com
              LB# 115, Ste 260    Voice: (972) 738-6137
              Dallas, TX 75248    Fax:   (972) 738-6111
       http://www.beasys.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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: AllanW@my-dejanews.com
Date: 1998/07/22
Raw View
In article <35B366D8.612F4F01@physik.tu-muenchen.de>,
  Christopher Eltschka <celtschk@physik.tu-muenchen.de> wrote:
> James Kuyper wrote:
> >
> > David Abrahams wrote:
> > >
> > > On 16 Jul 98 03:11:20 GMT, AllanW@my-dejanews.com wrote:
> > ...
> > > >Absolutely.  Once I've asserted that this!=NULL, I can be ABSOLUTELY
> > > >CERTAIN that this particular problem hasn't occured.
> > >
> > > Wrong-o. See above.
> >
> > If you verify that "this!=NULL", then the particular problem caused by
> > the possibility that "this==NULL" hasn't occured. If you make the
> > mistake of inferring some larger degree of protection, that's your
> > fault, not a drawback of the technique itself. As has been pointed out,
> > under some (perhaps most) implementations, it is possible to call a
> > non-virtual member function using a NULL this. The test catches one of
> > many possible problems. The fact that there are many other problems that
> > it doesn't catch is not an argument against making the test.
> >
> > If a given medicine protects against the common cold, would you refuse
> > to take it just because it doesn't protect you against anything else?
>
> Would you buy a medicine which *may* protect you against the common
> cold, depending on facts which you normally don't consider, say the
> number of Adenosines on your Chromosome 5?
> This is what he told you: The this!=NULL check doesn't reliably
> prevent you from calling member functions with NULL pointers.
> Indeed, there are a lot of situations where it probably will not
> detect that fault. But since it *looks* as if it catched that
> error reliably, you might end up thinking that error didn't occur.

This falls into the category of "the mistake of inferring some larger
degree of protection."  Furthermore, if it's buried in an
implementation file (instead of being exposed in a header file), then
it doesn't "look" like anything at all to most programmers.

> Also note that looking at this in the debugger doesn't help here,
> you really have to look at the pointer the member was called upon.

Which you can do in the debugger.  Verifying that the pointer is NULL
is pointless, of course; you already know that this happened by the
very fact that the assert() was triggered.  But the debugger will
allow you to examine a stack trace, to find out exactly where the
function was called from, so that you can then figure out how it came
to use a NULL pointer in code where the pointer wasn't supposed to be
NULL.

> It's like buying an insurance which protects you against car
> accidents, but only if you drive on certain roads (where the type of
> road may even depend on the state you are driving in, and in general
> may not be trivial to find out). Would you buy that insurance?

If we bought insurance on that type of basis, and if most of the
policies were just short of free, then yes, I would buy it as part of
a much larger portfolio.

Using assert(this) doesn't constitute debugging.  It's one tool, one
very tiny unimportant tool in the toolbox.  But most of the debugging
tools I use are unimportant in and of themselves.  They are individual
sugar crystals; individually they mean very little, but collectively
they can be very sweet.

Corny analogy, I guess, but it beats your cold/chromosone and your
insurance/one-road-only similes.
              "Life is like a metaphor, only different."

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: jkanze@otelo.ibmmail.com
Date: 1998/07/22
Raw View
In article <6p03df$aac$1@nnrp1.dejanews.com>,
  AllanW@my-dejanews.com wrote:
> In article <6oncha$t1h@news3.euro.net>,
>   "Martijn Lievaart" <Invalid@against.spam.nl> wrote:
> > So what will this do?
> >
> > struct A { int x; };
> > struct B { int x; };
> > struct C : A, B {};
> > void a(A *p) { assert(p != NULL); }
> > void b(B *p) { assert(p != NULL); }
> >
> > int main()
> > {
> >   C* c = 0;
> >   a(c);
> >   b(c);
> > }
> >
> > If I understand correctly than either of the assertions may or may not
fire?
> > This would make these asserts as worthless as the assert(this!=NULL) case
> > and the whole idea of testing on NULL wouldn't work. This wich would
further
> > strengthen your last argument. I may even put passing NULL ptrs in the way
> > you described above on my "don't ever do this" list (Even more effictive
C++
> > ;^>). Please prove me wrong.
>
> Neither of the assertions is guaranteed to fire.

Both are guaranteed to fire.  Think about it for a moment; instead of
an assert, replace it with an if -- do one thing if the pointer is null,
another if not.  This has to work, or you couldn't use null pointers
for anything.

--
James Kanze    +33 (0)1 39 23 84 71    mailto: kanze@gabi-soft.fr
        +49 (0)69 66 45 33 10    mailto: jkanze@otelo.ibmmail.com
GABI Software, 22 rue Jacques-Lemercier, 78000 Versailles, France
Conseils en informatique orient   e objet --
              -- Beratung in objektorientierter Datenverarbeitung

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: AllanW@my-dejanews.com
Date: 1998/07/20
Raw View
In article <6oncha$t1h@news3.euro.net>,
  "Martijn Lievaart" <Invalid@against.spam.nl> wrote:
> So what will this do?
>
> struct A { int x; };
> struct B { int x; };
> struct C : A, B {};
> void a(A *p) { assert(p != NULL); }
> void b(B *p) { assert(p != NULL); }
>
> int main()
> {
>   C* c = 0;
>   a(c);
>   b(c);
> }
>
> If I understand correctly than either of the assertions may or may not fire?
> This would make these asserts as worthless as the assert(this!=NULL) case
> and the whole idea of testing on NULL wouldn't work. This wich would further
> strengthen your last argument. I may even put passing NULL ptrs in the way
> you described above on my "don't ever do this" list (Even more effictive C++
> ;^>). Please prove me wrong.

Neither of the assertions is guaranteed to fire.  However, they are
NOT worthless -- because chances are excellent that at least one of
them WILL fire.  And tracking down the cause of a failed assert is
much easier than tracking down the cause of a segment violation, or
whatever else might happen with invalid use of a null pointer.

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: jkanze@otelo.ibmmail.com
Date: 1998/07/20
Raw View
In article <6oncha$t1h@news3.euro.net>,
  "Martijn Lievaart" <Invalid@against.spam.nl> wrote:
> This got me thinking,
>
> David Abrahams wrote in message <35ae121e.654996185@news.motu.com>...
> (snip)
> >
> >I guess I should have explained myself in more detail: if you're using
> >multiple inheritance, all but one of the base classes have a different
> >address from the entire object. On most architectures, if you add an
> >offset to a NULL pointer it's not NULL anymore. So the detection will
> >fail.  For example:
> >
> >struct A { void a() { assert(this != NULL); } int x; };
> >struct B { void b() { assert(this != NULL); } int x; };
> >
> >struct C : A, B {};
> >
> >int main()
> >{
> >  C* c = 0;
> >  c->a();   // At least one of these won't assert
> >  c->b();   // probably this one.
> >}
> >
> (snip)
> >
> >Not quite. What I'm trying to say is that some people write functions
> >which accept NULL parameters intentionally, with semantics like
> >"Accepts a Foo* argument, or NULL. If it's NULL, it has these default
> >semantics...". I usually don't do that, because it's a way for NULL
> >pointers to creep into the code.
> >
>
> So what will this do?
>
> struct A { int x; };
> struct B { int x; };
> struct C : A, B {};
> void a(A *p) { assert(p != NULL); }
> void b(B *p) { assert(p != NULL); }
>
> int main()
> {
>   C* c = 0;
>   a(c);
>   b(c);
> }
>
> If I understand correctly than either of the assertions may or may not fire?

No.  Both must fail.  This is a legal program; the compiler is required
to check for null before converting the pointers.  Calling a function
through a null pointer is undefined behavior.  Since the compiler knows
that it cannot occur, it knows that the pointer is not null, and so doesn't
have to check.

> This would make these asserts as worthless as the assert(this!=NULL) case
> and the whole idea of testing on NULL wouldn't work. This wich would further
> strengthen your last argument. I may even put passing NULL ptrs in the way
> you described above on my "don't ever do this" list (Even more effictive C++
> ;^>). Please prove me wrong.
>
> So the real questions are:
>
> If I cast a pointer to a pointer to baseclass, and this pointer is NULL, is
> the pointer to the baseclass guaranteed to remain NULL?

The baseclass pointer is guaranteed to remain null.

> If so, doesn't that also apply to any this pointer?

Because any program which calls a non-static member function through a
null pointer invokes undefined behavior, and all bets are off.

--
James Kanze    +33 (0)1 39 23 84 71    mailto: kanze@gabi-soft.fr
        +49 (0)69 66 45 33 10    mailto: jkanze@otelo.ibmmail.com
GABI Software, 22 rue Jacques-Lemercier, 78000 Versailles, France
Conseils en informatique orient   e objet --
              -- Beratung in objektorientierter Datenverarbeitung

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1998/07/18
Raw View
"Martijn Lievaart" <Invalid@against.spam.nl> writes:

>So the real questions are:

>If I cast a pointer to a pointer to baseclass, and this pointer is NULL, is
>the pointer to the baseclass guaranteed to remain NULL?
>If so, doesn't that also apply to any this pointer?

If you convert a null pointer to a different type, it must remain
a null pointer if the cast is valid. That is always true: for data
pointers, function pointers, and pointers-to-members.

Simple examples:

class B1 { ... };
class B2 { ... };
class D : public B1, public B2 { ... };
class E { ... }; // unrelated class

D* dp=0;

void f(B2*);

static_cast<B2>(dp) // yields a null pointer
f(dp)               // f gets a null pointer
(E*)(dp)            // yields a null pointer

But calling a non-static member function via a null pointer has
undefined results. "Undefined" means the compiler is allowed to
check for null and preserve it, or not check for null and apply
an adjustment, or detect and report the error, or anything else.

--
Steve Clamage, stephen.clamage@sun.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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Paul D. DeRocco" <pderocco@ix.netcom.com>
Date: 1998/07/15
Raw View
Steve Clamage wrote:
>
> "this" is a pointer, and any pointer can be compared for
> equality to NULL. "this" will compare equal if and only if
> it is a null pointer. That isn't the problem.
>
> The problem is the test is nearly useless. Calling a non-static
> member function from a null pointer has undefined results.
> The test will protect against the invalid call only sometimes,
> and with some compilers. If the function is virtual, the call won't
> succeed, and you never get to the point where you check for null.

If the function is non-virtual, it shouldn't matter whether the pointer
is valid or not, since internally the call is just a plain function
call. If the function is virtual (which it wasn't in the example), then
of course you can't invoke the function through a null pointer, so
there's no need for the test within the function.

> The problem can also show up when calling a non-virtual function
> of a base class from a null pointer of derived-class type.
> Since calling the function via a null pointer is invalid,
> compilers typically do not check for null before applying
> the pointer adjustment. (Doing so would slow down every function
> call.) The function gets called with a non-null pointer that doesn't
> point to an object. Boom!

That's not my experience. Borland absolutely does check every pointer
before offsetting it. In fact, I wrote to them a long time ago
requesting that they not do this for the specific case of invoking a
virtual function, since it's going to crash anyway whether a null
pointer is offsetted or not; but they didn't listen.

(Given the frequency of pointer offsetting in C++, I always thought CPUs
should have an add-immediate-if-nonzero instruction to support it.)

--

Ciao,
Paul


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: AllanW@my-dejanews.com
Date: 1998/07/15
Raw View
In article <6oful7$rm2$1@nnrp1.dejanews.com>,
  jkanze@otelo.ibmmail.com wrote:
> In article <6oe6b0$f03$1@nnrp1.dejanews.com>,
>   AllanW@my-dejanews.com wrote:
> > > Steve Clamage wrote:
> > > > "this" is a pointer, and any pointer can be compared for
> > > > equality to NULL. "this" will compare equal if and only if
> > > > it is a null pointer. That isn't the problem.
> > > >
> > > > The problem is the test is nearly useless.
> > Depends on the platform, I guess.  Microsoft defines an ASSERTVALID macro
> > which (for debug calls only) calls object->isValid() -- which isn't even
> > defined in non-debug builds.  isValid normally does many checks before
> > calling base::isValid(), one of which is to check for a null pointer.
>
> So it might be valid as a Microsoft extension.  Although somehow I doubt
> it, if virtual functions are involved.  How can they know which virtual
> function to call without knowing the dynamic type of the object?  And how
> can they know the dynamic type of the object if there isn't one?

I wouldn't call it an extension, I would call it a useful assertion.

I'm sure your right about virtual functions.  But please remember that
not all member functions are virtual.  In the non-virtual case, many
(most?) compilers can avoid a v-table lookup (for that matter, can
avoid taking up space in the v-table) by simply calling the function
directly.  That's why the "virtual" keyword is required -- so that the
compiler can invoke the virtual mechanism only when it is needed.

In all cases, if the program calls a member function using a null
pointer (or any other invalid value), THIS IS AN ERROR and the
programmer has no right to expect the program to perform in ANY
particular manner.  However, for the non-virtual case, when this
particular error occurs, many (most?) platforms will implement the
following behavior (even though it's not required to): first, load
that invalid (null) value into the register (or whatever) that
carries the object address, exactly as if the value was valid;
second, call the member function, exactly as if nothing was wrong
with the object address.

This works because these compilers don't use the v-table in order to
make the non-virtual function call.  You could consider this an
optimization, I suppose -- detecting the fact that only one member
function can be reached, so branching there directly -- not only saving
the time and space required for the v-table lookup, but also saving
space in the v-table itself!  Indeed, this is why the keyword "virtual"
was invented -- to allow the compiler to skip the v-table lookup when
it isn't required.

> I think Steve's point still stands : as a test, it is nearly useless, since
> you will normally have crashed before entering the function.

As a test, it might be useful.  Although it won't catch 100% of this
type of error, and on some platforms it might not catch any instances
of this type of error, it is nevertheless harmless and legal, and
indeed might catch at least some instances of this error on some
platforms.

Even though it's true that before you get to this point you will have
violated enough rules that you COULD have crashed, it's also true that
if the function is not virtual, you might NOT have crashed yet
(although it's likely to come soon).  You may be able to report the
error in a more useful manner than a stack dump or abend or invalid
results, or whatever else would have happened had the error not been
detected when it was.

> > > > Calling a non-static
> > > > member function from a null pointer has undefined results.
> > Exactly; it is a programmer error, and robust classes should help to
> > detect programmer errors.
>
> Fine, but that has nothing to do with Steve's point.  The point is that
> you cannot call a member function through a null pointer, so normally,
> any tests made for NULL within the function are too late -- if the
> pointer was null, you won't get into the member function.

Sure you would, sometimes, for non-virtual functions.  The relevant
quote was removed; here it is again:

> > We may have strayed off-topic here.  This is about standards, not
> > debugging.  The original question was on topic: Are we allowed to
> > compare "this" against NULL?  The answer is that this might not
> > detect 100% of all abuses on all platforms, but it does detect some
> > errors on some platforms, and it is perfectly valid (in the sense
> > that "this" should never be NULL in any conforming program on any
> > platform).  I encourage you to use it in test builds.

But what if you're on a platform where, as you say:
# any tests made for NULL within the function are too late -- if the
# pointer was null, you won't get into the member function.
What then?  Is the test non-compliant?

NO!  There is absolutely nothing in the language that prevents the
programmer from specifying code such as:
    if (!this) { crash("Value of this was null"); }
Furthermore, the value of "this" should never be null, for EXACTLY
the same reasons that it is an error to call a member function with
a null pointer.  So we have:

                    Platforms that  always    Platforms that might call
                    die  as  soon  as  you    a  member  function  with
                    call a member function    this == null  for certain
                    with  a  null  pointer    non-compliant    programs

  What happens if   The test is dead code     The test might catch it
  you  test   for   Trivial space wasted      Trivial space used
  this   ==  null   No harm done              Might not catch all errors,
                                              but still no harm done

Right?

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: David R Tribble <david.tribble@noSPAM.central.beasys.com>
Date: 1998/07/15
Raw View
>>> "this" is a pointer, and any pointer can be compared for
>>> equality to NULL. "this" will compare equal if and only if
>>> it is a null pointer. That isn't the problem.

Steve Clamage:
>> The problem is the test is nearly useless.

jkanze@otelo.ibmmail.com wrote:
> I think Steve's point still stands: as a test, it is nearly useless,
> since you will normally have crashed before entering the function.

Steve Clamage:
>> Calling a non-static
>> member function from a null pointer has undefined results.

jkanze@otelo.ibmmail.com wrote:
> ...
> The point is that
> you cannot call a member function through a null pointer, so normally,
> any tests made for NULL within the function are too late -- if the
> pointer was null, you won't get into the member function.

Except that you CAN call a member function through a null pointer
(on most real implementations), provided that the function is not
virtual.  Try this:

    #include <stdio.h>

    class Foo
    {
    public:
                Foo();
                ~Foo();
        int     func();

    private:
        int     value;
    };

    int Foo::func()
    {
        //return value;     // [1] This will break
        return 123;         // [2] This will work
    }

    int main()
    {
        Foo *  pfoo = 0;    // [3] Note: null pointer!
        int    i;

        printf("Calling null->func()...\n");
        i = pfoo->func();   // [4] Note: null pointer!
        printf("...returned %d\n", i);

        return 0;
    }

This works because of the way non-virtual member functions are
resolved by (most) linkers; you don't need an object to call the
function, since it has a well-known address at compilation time,
and there is no vtable lookup required at runtime.  (You can
argue that this is "implementation-defined" but the plain fact of
the matter is that it is possible on most existing implementations.)

The 'return' statement at [1] won't work if the 'this' pointer
is bad, because even though the function gets called okay, its
'this' pointer is null, and there is no valid 'value' member to
access.  But if we don't access any members of Foo, the function
executes without incident.

While it's true that your program will probably crash before
entering a virtual member function when it is called through an
invalid object pointer (because a vtable lookup is required), it
will very likely make it into a non-virtual member function
regardless of the object pointer's value.  And that's why it
makes sense to check 'this', or the magic number of the object,
or *somehow* validate the object before proceeding any further.
Better to do too much checking than not enough.

BTW, I don't take out my assert() calls when I ship; software is
*never* tested enough (no matter how good the QA staff is), and a
failure in the field is easier to debug if a message gets
displayed.  That's why I put the asserts there in the first
place, to catch fatal "should not occur" bugs.


-- David R. Tribble, dtribble@technologist.com --
-- C++, the PL/1 of the 90s.
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: Basjes@nlr.nl (Niels Basjes)
Date: 1998/07/15
Raw View
On 15 Jul 98 05:51:50 GMT, AllanW@my-dejanews.com wrote:

>As a test, it might be useful.  Although it won't catch 100% of this
>type of error, and on some platforms it might not catch any instances
>of this type of error, it is nevertheless harmless and legal, and
>indeed might catch at least some instances of this error on some
>platforms.

That is exactly the intent of what I had in mind: Being able to handle
a couple more error situations without crashing.

>Even though it's true that before you get to this point you will have
>violated enough rules that you COULD have crashed, it's also true that
>if the function is not virtual, you might NOT have crashed yet
>(although it's likely to come soon).  You may be able to report the
>error in a more useful manner than a stack dump or abend or invalid
>results, or whatever else would have happened had the error not been
>detected when it was.

Exactly.

....
>But what if you're on a platform where, as you say:
># any tests made for NULL within the function are too late -- if the
># pointer was null, you won't get into the member function.
>What then?  Is the test non-compliant?
>
>NO!  There is absolutely nothing in the language that prevents the
>programmer from specifying code such as:
>    if (!this) { crash("Value of this was null"); }
>Furthermore, the value of "this" should never be null, for EXACTLY
>the same reasons that it is an error to call a member function with
>a null pointer.  So we have:
>
>                    Platforms that  always    Platforms that might call
>                    die  as  soon  as  you    a  member  function  with
>                    call a member function    this == null  for certain
>                    with  a  null  pointer    non-compliant    programs
>
>  What happens if   The test is dead code     The test might catch it
>  you  test   for   Trivial space wasted      Trivial space used
>  this   ==  null   No harm done              Might not catch all errors,
>                                              but still no harm done

Very clear, thanks.

--

Best regards,

Niels.

=======================================================================
| ir. Niels Basjes              | National Aerospace Laboratory |  _  |
| Phone       : +31-20-5113381  | Anthony Fokkerweg 2           | /   |
| Fax         : +31-20-5113912  | 1059 CM Amsterdam             |(NLR)|
| E-Mail@NLR  : Basjes@NLR,nl   | The Netherlands               |  _/ |
| E-Mail@Home : Niels@Basjes,nl | http://www.nlr.nl             |     |
=======================================================================
Spam block : Replace the , with a .
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: abrahams@motu.com (David Abrahams)
Date: 1998/07/15
Raw View
On 15 Jul 98 05:51:50 GMT, AllanW@my-dejanews.com wrote:


>I wouldn't call it an extension, I would call it a useful assertion.
>
>I'm sure your right about virtual functions.  But please remember that
>not all member functions are virtual.  In the non-virtual case, many
>(most?) compilers can avoid a v-table lookup (for that matter, can
>avoid taking up space in the v-table) by simply calling the function
>directly.  That's why the "virtual" keyword is required -- so that the
>compiler can invoke the virtual mechanism only when it is needed.
>
>In all cases, if the program calls a member function using a null
>pointer (or any other invalid value), THIS IS AN ERROR and the
>programmer has no right to expect the program to perform in ANY
>particular manner.  However, for the non-virtual case, when this
>particular error occurs, many (most?) platforms will implement the
>following behavior (even though it's not required to): first, load
>that invalid (null) value into the register (or whatever) that
>carries the object address, exactly as if the value was valid;
>second, call the member function, exactly as if nothing was wrong
>with the object address.

I started to post this argument myself until I enumerated all the ways
in which it actually won't work on most architectures. That includes
any time you use multiple-inheritance, and especially so if you use
virtual inheritance. Add to that every time a virtual function is
called, and I think of it as a bad bet.

Aren't assertions the one thing you want to be absolutely sure of? I
mean, when I write an assertion, I want to *know* that if I pass by
that line of code silently, the condition or invariant it is trying to
check really has been preserved. Unreliable debugging checks are
actually detrimental to program stability. In the end I concluded that
if I was worried about NULL object pointers creeping into my code I
had better find a different way to prevent them.

One good approach is to enforce some programming discipline: use
references instead of pointers wherever possible, and don't write
functions which accept NULL pointer parameters (assert if you get
them).

-Dave


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: stephen.clamage@sun.com (Steve Clamage)
Date: 1998/07/15
Raw View
AllanW@my-dejanews.com writes:

>In article <6oful7$rm2$1@nnrp1.dejanews.com>,
>  jkanze@otelo.ibmmail.com wrote:
>> In article <6oe6b0$f03$1@nnrp1.dejanews.com>,
>>   AllanW@my-dejanews.com wrote:
>> > > Steve Clamage wrote:
>> > >
>> > > > "this" is a pointer, and any pointer can be compared for
>> > > > equality to NULL. "this" will compare equal if and only if
>> > > > it is a null pointer. That isn't the problem.
>> > > >
>> > > > The problem is the test is nearly useless.
>> > ...

> ...But please remember that
>not all member functions are virtual.  In the non-virtual case, many
>(most?) compilers can avoid a v-table lookup (for that matter, can
>avoid taking up space in the v-table) by simply calling the function
>directly.  That's why the "virtual" keyword is required -- so that the
>compiler can invoke the virtual mechanism only when it is needed.

> ... However, for the non-virtual case, when this
>particular error occurs, many (most?) platforms will implement the
>following behavior (even though it's not required to): first, load
>that invalid (null) value into the register (or whatever) that
>carries the object address, exactly as if the value was valid;
>second, call the member function, exactly as if nothing was wrong
>with the object address.

> ...

>> I think Steve's point still stands : as a test, it is nearly useless, since
>> you will normally have crashed before entering the function.

>As a test, it might be useful.  Although it won't catch 100% of this
>type of error, and on some platforms it might not catch any instances
>of this type of error, it is nevertheless harmless and legal, and
>indeed might catch at least some instances of this error on some
>platforms.

Consider this example, which contains no virtual functions:

    #include <iostream.h>

    struct B1 {
 int i1, j1;
    };

    struct B2 {
 int i2, j2;
 void foo();
    };

    void B2::foo() {
 cerr << "this=" << (void*)this << '\n';
    }

    struct D : B1, B2 {
 int i3, j3;
    };

    int main()
    {
  D* dp=0;
  dp->foo(); // prints "this=0x08" on my system
    }

In typical class layouts, an adjustment to the object pointer
is needed for the call dp->foo(). If it isn't needed on your
system, change the declaration of D to
 struct D: B2, B1 { ... };
One version or the other is certain to require an adjustment,
since a pointer to D cannot point simultaneously to both base
classes.

Since a call to a member function via a null pointer has
undefined results, the compiler is not required to check dp for
null before adjusting it. Indeed, the compiler I use does not
perform the check, since it would slow down every member
function call.

When B2::foo is entered, "this" is not null, but also does
not point to a B2 object. On my system, "this" points to
an address which cannot correspond to any object. (On a
system that checks every pointer for null before adjusting,
B::foo would find a null "this" pointer.)

As David Abrahams pointed out in another post, relying on a
test for null gives a false sense of security. Examples abound
where the test either will never occur, or will falsely report
a valid pointer. Cluttering up your code with low-utility tests
is not the road to success. If you want to test for null, make
the test before the member function is called.

--
Steve Clamage, stephen.clamage@sun.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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: David R Tribble <david.tribble@noSPAM.central.beasys.com>
Date: 1998/07/15
Raw View
>> > "this" is a pointer, and any pointer can be compared for
>> > equality to NULL. "this" will compare equal if and only if
>> > it is a null pointer. That isn't the problem.
>>
>> The problem is the test is nearly useless.

Steve Clamage wrote:
 [code clipped]
> ...
>
> As David Abrahams pointed out in another post, relying on a
> test for null gives a false sense of security. Examples abound
> where the test either will never occur, or will falsely report
> a valid pointer. Cluttering up your code with low-utility tests
> is not the road to success. If you want to test for null, make
> the test before the member function is called.

That's assuming you have control over the pointers to your objects.
If your code is part of a library that a customer uses, you can't
control what his pointers look like, nor what pointers he calls
your classs functions through, nor can you insure that he resets
his pointers to null after he deletes them.

So while testing for null might not catch every bug, validating
the object *somehow* is not a futile effort.  If you have
complete control over your objects (perhaps your classes are
not publicly known outside of your program or library), you
can probably omit these "useless" tests.  If you have customers
using your classes, however, it might be wise to prevent
disaster by some sort of validation.  (I mentioned a few schemes
in a previous post, <35A6AD74.F1C@noSPAM.central.beasys.com>.)

Another scheme, though it may be a bit paranoid, is to keep a
list of pointers to all currently live objects (probably as a
private static member of the class).  That way each member
function can call a validation function that scans the list
(or hashes into it) to check that their 'this' pointer really
points to a live object.  Of course, as mentioned before, all
bets are off when you're calling virtual functions.

Another scheme is not to allow customers to use pointers at
all, but rather proxy objects that contain a (private) pointer
to the real object.  Since these proxy objects are self-contained,
the customer is less likely to corrupt the object pointer.

Of course, preventing the customer from stomping on memory with
a wild pointer is nigh impossible.  So it still makes sense to
validate object contents.


-- David R. Tribble, dtribble@technologist.com --
-- C++, the PL/1 of the 90s.


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: AllanW@my-dejanews.com
Date: 1998/07/16
Raw View
In article <35acc2bc.569125606@news.motu.com>,
  abrahams@motu.com wrote:
> On 15 Jul 98 05:51:50 GMT, AllanW@my-dejanews.com wrote:
>
> >I wouldn't call it an extension, I would call it a useful assertion.
> >
> >I'm sure your right about virtual functions.  But please remember that
> >not all member functions are virtual.  In the non-virtual case, many
> >(most?) compilers can avoid a v-table lookup (for that matter, can
> >avoid taking up space in the v-table) by simply calling the function
> >directly.  That's why the "virtual" keyword is required -- so that the
> >compiler can invoke the virtual mechanism only when it is needed.
> >
> >In all cases, if the program calls a member function using a null
> >pointer (or any other invalid value), THIS IS AN ERROR and the
> >programmer has no right to expect the program to perform in ANY
> >particular manner.  However, for the non-virtual case, when this
> >particular error occurs, many (most?) platforms will implement the
> >following behavior (even though it's not required to): first, load
> >that invalid (null) value into the register (or whatever) that
> >carries the object address, exactly as if the value was valid;
> >second, call the member function, exactly as if nothing was wrong
> >with the object address.
>
> I started to post this argument myself until I enumerated all the ways
> in which it actually won't work on most architectures. That includes
> any time you use multiple-inheritance, and especially so if you use
> virtual inheritance. Add to that every time a virtual function is
> called, and I think of it as a bad bet.

Multiple inheritance, even virtual inheritance, still won't prevent it
from working on non-virtual functions.  If the compiler can generate a
call to the function without using a v-table, then it's possible to get
there with a null "this".

> Aren't assertions the one thing you want to be absolutely sure of? I
> mean, when I write an assertion, I want to *know* that if I pass by
> that line of code silently, the condition or invariant it is trying to
> check really has been preserved.

Absolutely.  Once I've asserted that this!=NULL, I can be ABSOLUTELY
CERTAIN that this particular problem hasn't occured.

> Unreliable debugging checks are
> actually detrimental to program stability.

I assume you're referring to misuse of assert(), such as asserting
that a disk read didn't fail instead of handling the failure.  If so,
then you are quite right.

> In the end I concluded that
> if I was worried about NULL object pointers creeping into my code I
> had better find a different way to prevent them.

Yes, I would even agree with this too, in limited fashion.

If I'm working on an application that seems to have a problem with
NULL pointers, I never assume that anything will tell me about it
in English.  I begin diligently adding checks throughout my code.

If, on the other hand, I'm writing a robust class, I try to add
checks to detect and report common errors as soon as possible.
You can't close something you didn't open, you can't rename
something that isn't initialized, etc.  In debug builds, as soon
as the error is detected, I call a special error report routine
that doesn't exist in release builds.  That routine reports the
error, allows the programmer to trace the stack, etc.

Among those many checks, one of the less important but still very
useful ones is to try to detect invalid pointers.  For some classes
this gets extreme -- I maintain a list of all constructed and
recently-destructed elements, and make sure that "this" is on the
correct list.  Most of the time this is too much work, so I make
the one check that's easy and very inexpensive: I check to see if
"this" is null.

Please go back to the original post in this thread.  Niels Basjes
wants to make his code more robust by adding such checks.  He didn't
say that he planned to call the class with a NULL pointer; he just
wanted to know if such an error check was legal (he already knew that
it was useful).

The responses he got were initially all along the lines of "Your
program should check to make sure that the pointer isn't NULL before
it gets used."  It's evident to me that he already knew that, but was
trying to add some extra checks to make his class more robust.

> One good approach is to enforce some programming discipline: use
> references instead of pointers wherever possible,

What; you never had a null reference?  That isn't just skill, sir,
it's also luck.

> and don't write
> functions which accept NULL pointer parameters (assert if you get
> them).

Are you suggesting that he should assert that a pointer isn't NULL?
Because that's almost where we started!  It's just a general
application of the specific concept of asserting that "this" isn't
NULL.

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: saroj@bear.com
Date: 1998/07/16
Raw View
In article <35acc2bc.569125606@news.motu.com>,
  abrahams@motu.com wrote:
> On 15 Jul 98 05:51:50 GMT, AllanW@my-dejanews.com wrote:
>
[snipped]

> One good approach is to enforce some programming discipline: use
> references instead of pointers wherever possible, and don't write
> functions which accept NULL pointer parameters (assert if you get
> them).
>

I do not think passing references instead of a pointer for output
is a good idea. I use const reference to avoid copying of parameter;
non-const reference for non-const operators and explicit pointers when a
variable will be modified. Stroustrup basically recommends the same
approach.

- Saroj Mahapatra

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: jkanze@otelo.ibmmail.com
Date: 1998/07/16
Raw View
In article <35acc2bc.569125606@news.motu.com>,
  abrahams@motu.com wrote:

> One good approach is to enforce some programming discipline: use
> references instead of pointers wherever possible, and don't write
> functions which accept NULL pointer parameters (assert if you get
> them).

Both good suggestions.  More generally, don't use raw pointers.  I
find that most of my pointers are either RefCntPtr or ManagedPtr --
in both cases, I check for NULL in operator* and operator->.

--
James Kanze    +33 (0)1 39 23 84 71    mailto: kanze@gabi-soft.fr
        +49 (0)69 66 45 33 10    mailto: jkanze@otelo.ibmmail.com
GABI Software, 22 rue Jacques-Lemercier, 78000 Versailles, France
Conseils en informatique orient   e objet --
              -- Beratung in objektorientierter Datenverarbeitung

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: abrahams@motu.com (David Abrahams)
Date: 1998/07/16
Raw View
On 16 Jul 98 03:11:20 GMT, AllanW@my-dejanews.com wrote:

>In article <35acc2bc.569125606@news.motu.com>,
>  abrahams@motu.com wrote:
>> On 15 Jul 98 05:51:50 GMT, AllanW@my-dejanews.com wrote:
>> >
>> >In all cases, if the program calls a member function using a null
>> >pointer (or any other invalid value), THIS IS AN ERROR and the
>> >programmer has no right to expect the program to perform in ANY
>> >particular manner.  However, for the non-virtual case, when this
>> >particular error occurs, many (most?) platforms will implement the
>> >following behavior (even though it's not required to): first, load
>> >that invalid (null) value into the register (or whatever) that
>> >carries the object address, exactly as if the value was valid;
>> >second, call the member function, exactly as if nothing was wrong
>> >with the object address.
>>
>> I started to post this argument myself until I enumerated all the ways
>> in which it actually won't work on most architectures. That includes
>> any time you use multiple-inheritance, and especially so if you use
>> virtual inheritance. Add to that every time a virtual function is
>> called, and I think of it as a bad bet.
>
>Multiple inheritance, even virtual inheritance, still won't prevent it
>from working on non-virtual functions.  If the compiler can generate a
>call to the function without using a v-table, then it's possible to get
>there with a null "this".

I guess I should have explained myself in more detail: if you're using
multiple inheritance, all but one of the base classes have a different
address from the entire object. On most architectures, if you add an
offset to a NULL pointer it's not NULL anymore. So the detection will
fail.  For example:

struct A { void a() { assert(this != NULL); } int x; };
struct B { void b() { assert(this != NULL); } int x; };

struct C : A, B {};

int main()
{
  C* c = 0;
  c->a();   // At least one of these won't assert
  c->b();   // probably this one.
}

That's the non-virtual inheritance case, where, although non-NULL,
this is probably a value close to 0. In the virtual-inheritance case,
the offset to the base class is usually stored somewhere in the
object. You're even more likely to find yourself with undetectable
problems.

There was recently some discussion on the list of implementations that
locate base class members at *later* memory locations than the derived
class members. On these compilers, you're out of luck even with
single-inheritance.

>> Aren't assertions the one thing you want to be absolutely sure of? I
>> mean, when I write an assertion, I want to *know* that if I pass by
>> that line of code silently, the condition or invariant it is trying to
>> check really has been preserved.
>
>Absolutely.  Once I've asserted that this!=NULL, I can be ABSOLUTELY
>CERTAIN that this particular problem hasn't occured.

Wrong-o. See above.

>> Unreliable debugging checks are
>> actually detrimental to program stability.
>
>I assume you're referring to misuse of assert(), such as asserting
>that a disk read didn't fail instead of handling the failure.  If so,
>then you are quite right.

Nope. I mean a check that looks like it's an effective barrier against
errors, but actually isn't.

>> In the end I concluded that
>> if I was worried about NULL object pointers creeping into my code I
>> had better find a different way to prevent them.
>
>Yes, I would even agree with this too, in limited fashion.
>
>If I'm working on an application that seems to have a problem with
>NULL pointers, I never assume that anything will tell me about it
>in English.  I begin diligently adding checks throughout my code.
>

Good idea. But for this particular problem, you might be better off
with different checks.
<snip>

>Please go back to the original post in this thread.  Niels Basjes
>wants to make his code more robust by adding such checks.  He didn't
>say that he planned to call the class with a NULL pointer; he just
>wanted to know if such an error check was legal (he already knew that
>it was useful).

After thinking about it a bit, I came to the conclusion that it's not
as useful as it might appear, and might in fact be dangerous, since it
can produce a false sense of security. I kept the original post in
mind when I posted; I'm not sure what you'd like me to see by
reviewing it.

>> One good approach is to enforce some programming discipline: use
>> references instead of pointers wherever possible,
>
>What; you never had a null reference?  That isn't just skill, sir,
>it's also luck.

To the extent that we credit luck for bugs and programming errors, we
undermine our own power to prevent them in the future. I always try to
come up with an explanation that makes me (or, if absolutely
neccessary, some other programmer) responsible for the problem.

And no, as far as I can remember, I never had a NULL reference.

>> and don't write
>> functions which accept NULL pointer parameters (assert if you get
>> them).
>
>Are you suggesting that he should assert that a pointer isn't NULL?
>Because that's almost where we started!  It's just a general
>application of the specific concept of asserting that "this" isn't
>NULL.

Not quite. What I'm trying to say is that some people write functions
which accept NULL parameters intentionally, with semantics like
"Accepts a Foo* argument, or NULL. If it's NULL, it has these default
semantics...". I usually don't do that, because it's a way for NULL
pointers to creep into the code.

If you think about where NULL pointers can actually come from, you'll
be in a much better position to prevent them from cropping up in
unexpected places. C++ gives you lots of power to control that.
Actually, I think dangling non-NULL pointers and references are a much
bigger problem.

-Dave
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1998/07/16
Raw View
AllanW@my-dejanews.com writes:

>> I started to post this argument myself until I enumerated all the ways
>> in which it actually won't work on most architectures. That includes
>> any time you use multiple-inheritance, and especially so if you use
>> virtual inheritance. Add to that every time a virtual function is
>> called, and I think of it as a bad bet.

>Multiple inheritance, even virtual inheritance, still won't prevent it
>from working on non-virtual functions.  If the compiler can generate a
>call to the function without using a v-table, then it's possible to get
>there with a null "this". ...

>> Aren't assertions the one thing you want to be absolutely sure of? I
>> mean, when I write an assertion, I want to *know* that if I pass by
>> that line of code silently, the condition or invariant it is trying to
>> check really has been preserved.

>Absolutely.  Once I've asserted that this!=NULL, I can be ABSOLUTELY
>CERTAIN that this particular problem hasn't occured.

I show in another article how calling a non-virtual function from
a null pointer can result in an invalid non-null value for "this".
It is likely to happen in the presence of multiple inheritance,
making the check for null inside the function worthless.

--
Steve Clamage, stephen.clamage@sun.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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: AllanW@my-dejanews.com
Date: 1998/07/16
Raw View
In article <6oikqo$pvl@engnews1.Eng.Sun.COM>,
  stephen.clamage@sun.com (Steve Clamage) wrote:
> AllanW@my-dejanews.com writes:
>
> >In article <6oful7$rm2$1@nnrp1.dejanews.com>,
> >  jkanze@otelo.ibmmail.com wrote:
> >> In article <6oe6b0$f03$1@nnrp1.dejanews.com>,
> >>   AllanW@my-dejanews.com wrote:
> >> > > Steve Clamage wrote:
> >> > >
> >> > > > "this" is a pointer, and any pointer can be compared for
> >> > > > equality to NULL. "this" will compare equal if and only if
> >> > > > it is a null pointer. That isn't the problem.
> >> > > >
> >> > > > The problem is the test is nearly useless.
> >> > ...
>
> > ... However, for the non-virtual case, when this
> >particular error occurs, many (most?) platforms will implement the
> >following behavior (even though it's not required to): first, load
> >that invalid (null) value into the register (or whatever) that
> >carries the object address, exactly as if the value was valid;
> >second, call the member function, exactly as if nothing was wrong
> >with the object address.
>
> > ...
>
> >> I think Steve's point still stands : as a test, it is nearly useless, since
> >> you will normally have crashed before entering the function.
>
> >As a test, it might be useful.  Although it won't catch 100% of this
                                            ^^^^^^^^^^^^^^^^^^^
> >type of error, and on some platforms it might not catch any instances
> >of this type of error, it is nevertheless harmless and legal, and
> >indeed might catch at least some instances of this error on some
> >platforms.
>
> Consider this example, which contains no virtual functions:
>
>     #include <iostream.h>
>
>     struct B1 {
>  int i1, j1;
>     };
>
>     struct B2 {
>  int i2, j2;
>  void foo();
>     };
>
>     void B2::foo() {
>  cerr << "this=" << (void*)this << '\n';
>     }
>
>     struct D : B1, B2 {
>  int i3, j3;
>     };
>
>     int main()
>     {
>   D* dp=0;
>   dp->foo(); // prints "this=0x08" on my system
>     }
>
> In typical class layouts, an adjustment to the object pointer
> is needed for the call dp->foo(). If it isn't needed on your
> system, change the declaration of D to
>  struct D: B2, B1 { ... };
> One version or the other is certain to require an adjustment,
> since a pointer to D cannot point simultaneously to both base
> classes.

Yes, granted.  In this thread, we have no identified two cases
where the test for this!=NULL is very likely NOT to detect the
problem: virtual functions (unless called without the virtual
mechanism!), and functions in base classes of multiple inheritance
(MI) heirarchies.

But not all programs use MI at all, and in those that do, many
use it quite sparingly.  In the system I'm currently working on,
there's somewhere around 100 classes (I'm not about to go count
them).  Of these, about 20-25 have any sort of inheritance, and
there's only one class that multiply inherits two others.  Even
that one case isn't strictly neccesary, but we saw no harm in
doing it (I didn't know I would be writing about it in this
message 8-).  Not all projects are like this, of course; for
some projects, half or even 3/4 of the classes could be involved
in MI for very valid reasons.  But I don't think that my type of
project is rare, either.

In your sample code above, running on your system, a consistent
check for "this!=NULL" will catch about 80% of all non-virtual
member function calls.  You're probably wondering how I arrived
at that number.  It assumes that B1 and B2 are also used as
complete objects, and the usage of B1 and B2 in your code is
about as prevalent as the usage of D.  (That is, it doesn't
matter how many B1, B2 and D objects there are; it only matters
how many calls you make to member functions.)  It also assumes
that B1, B2, and D have an equal number of public non-virtual
member functions, not counting functions inherited by D from
B1 or B2.  This calculation also ignores function hiding/overriding.


               Complete object     Complete object     Complete object
                   is a B1             is a B2             is a D

Calls to B1   "this!=NULL" will   (does not apply)    "this!=NULL" will
functions      catch the error                         catch the error

Calls to B2   (does not apply)    "this!=NULL" will   "this!=NULL" WILL
functions                          catch the error   NOT catch the error

Calls to D    (does not apply)    (does not apply)    "this!=NULL" will
functions                                              catch the error


There are five relevant cases, and the catch will work in four of them
(on your system, based on what you said above for B1 calls working
properly but not B2 calls).  4/5 = 80%  (YMMV)

> Since a call to a member function via a null pointer has
> undefined results, the compiler is not required to check dp for
> null before adjusting it. Indeed, the compiler I use does not
> perform the check, since it would slow down every member
> function call.

A reasonable and normally quite safe optimization.  I expect that
exceptions are quite rare.

> When B2::foo is entered, "this" is not null, but also does
> not point to a B2 object. On my system, "this" points to
> an address which cannot correspond to any object. (On a
> system that checks every pointer for null before adjusting,
> B::foo would find a null "this" pointer.)

Which explains the 20% of cases that don't work.  (YMMV)

> As David Abrahams pointed out in another post, relying on a
> test for null gives a false sense of security.

Then educate the class users.  Whatever mechanism you use to
report that you detected a null pointer should also mention
that the test is not 100% effective.

Where I've worked, we use a custom ASSERT() instead of the
standard assert() because we don't want the code littered with
strings that report every test we make.  Instead, giving the
source file name and line number is sufficient for any programmer
with access to the source code.  There, comments explain any
detail that the programmer should know when an assert has been
triggered.

For instance:
        // We might be able to catch SOME cases where this
        // function is called with a null "this" pointer.
        ASSERT(this);

> Examples abound
> where the test either will never occur,

That hasn't come up in this thread before.  Offhand I can't
think of a case where the test won't be made at all; only
cases where the test fails to find invalid pointers.

But even so, if the test does not occur, it still did no
harm and violated no standards.

> or will falsely report
> a valid pointer.

This one WOULD surprise me.  In fact, it would prove that
everything I've been saying is completely ignorant, because
I've been saying that the test is harmless.

Would you please explain why this might happen?  Better yet,
give an example where, at least on your system, a perfectly
valid pointer is reported to be equal to NULL?  If you can
do this, I promise to concede defeat, and then shut up about
this topic forever.

> Cluttering up your code with low-utility tests
> is not the road to success.

No, it's a roadmap that may point you in the right direction.

Of course, you can take this to extremes (i.e. testing to make
sure that 1+1 really equals 2), then demonstrate that it's too
extreme, and use this as a basis for throwing out the technique
completely.  This is called "throwing out the baby with the bath
water."  Please, note: I'm not advocating that every possible
"low-utility test" should be used in every possible place.

But well-placed usage of simple tests, guaranteed to be valid
on every system if the program is correct, can be an enormous
time saver.  And really, that's all that assert() was ever useful
for in the first place.

> If you want to test for null, make
> the test before the member function is called.

Good programs should always do this, of course.  As a reminder,
I've always stated that by the time "this" is detected to be null,
it's far too late to do anything about it -- except to bring it to
the programmer's attention.

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: tom_lippincott@advisories.com (Tom Lippincott)
Date: 1998/07/16
Raw View
In article <6oivdl$ic1$1@nnrp1.dejanews.com>, AllanW@my-dejanews.com wrote:

> Once I've asserted that this!=NULL, I can be ABSOLUTELY
> CERTAIN that this particular problem hasn't occured.

While other people have noted that one can't be certain in the cases of
virtual functions or multiple inheritance, I see an even more insidious
problem: a compiler "knows" that this cannot be null, and therefore a good
compiler will optimize away the assertion.  In this case the assertion
would misdirect the programmer, making the bug even harder to catch.

That said, I have used assert( this != NULL ) in a situation where there
was a particular error it would catch.  (My compiler at the time did not
optimize away the assertion.)  Later, I redesigned the module to avoid the
problem.

In article <35acc2bc.569125606@news.motu.com>, abrahams@motu.com wrote:

> One good approach is to enforce some programming discipline: use
> references instead of pointers wherever possible, and don't write
> functions which accept NULL pointer parameters (assert if you get
> them).

I (mostly) take a more radical approach: if a parameter is a pointer, the
function has well-defined and useful semantics covering that case.
Otherwise, the parameter is a reference.  (I try to always wrap array
parameters in a class.)

I also try to put an assertion before every use of * or ->, except when
applied to this.

                                              --Tom Lippincott


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: clamage@Eng.Sun.COM (Steve Clamage)
Date: 1998/07/17
Raw View
AllanW@my-dejanews.com writes:

>In article <6oikqo$pvl@engnews1.Eng.Sun.COM>,
>  stephen.clamage@sun.com (Steve Clamage) wrote:

>But even so, if the test does not occur, it still did no
>harm and violated no standards.

>> or will falsely report
>> a valid pointer.

>This one WOULD surprise me.  In fact, it would prove that
>everything I've been saying is completely ignorant, because
>I've been saying that the test is harmless.

You have an assertion that that this!=NULL. The test passes,
falsely reporting (or implying) that the pointer is valid,
in the various cases that have so far been identified.

I didn't mean that a valid pointer could be reported as null.

--
Steve Clamage, stephen.clamage@sun.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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: James Kuyper <kuyper@wizard.net>
Date: 1998/07/17
Raw View
Steve Clamage wrote:
>
> AllanW@my-dejanews.com writes:
>
> >In article <6oikqo$pvl@engnews1.Eng.Sun.COM>,
> >  stephen.clamage@sun.com (Steve Clamage) wrote:
>
> >But even so, if the test does not occur, it still did no
> >harm and violated no standards.
>
> >> or will falsely report
> >> a valid pointer.
>
> >This one WOULD surprise me.  In fact, it would prove that
> >everything I've been saying is completely ignorant, because
> >I've been saying that the test is harmless.
>
> You have an assertion that that this!=NULL. The test passes,
> falsely reporting (or implying) that the pointer is valid,

Good cover! inserting that "(or implying)" makes your statements true.
However, I caught you! That phrase wasn't neither in nor implied by the
previous messages. The implication you've made is provided by the
programmer, not the code, and it's not an implication I would have made
in that circumstance.

> in the various cases that have so far been identified.
>
> I didn't mean that a valid pointer could be reported as null.

But the only pointers it reports about are the NULL ones. Therefore, I
would be just as surprised as Alan if it falsely reported a valid
pointer.


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: "Martijn Lievaart" <Invalid@against.spam.nl>
Date: 1998/07/18
Raw View
This got me thinking,

David Abrahams wrote in message <35ae121e.654996185@news.motu.com>...
(snip)
>
>I guess I should have explained myself in more detail: if you're using
>multiple inheritance, all but one of the base classes have a different
>address from the entire object. On most architectures, if you add an
>offset to a NULL pointer it's not NULL anymore. So the detection will
>fail.  For example:
>
>struct A { void a() { assert(this != NULL); } int x; };
>struct B { void b() { assert(this != NULL); } int x; };
>
>struct C : A, B {};
>
>int main()
>{
>  C* c = 0;
>  c->a();   // At least one of these won't assert
>  c->b();   // probably this one.
>}
>
(snip)
>
>Not quite. What I'm trying to say is that some people write functions
>which accept NULL parameters intentionally, with semantics like
>"Accepts a Foo* argument, or NULL. If it's NULL, it has these default
>semantics...". I usually don't do that, because it's a way for NULL
>pointers to creep into the code.
>

So what will this do?

struct A { int x; };
struct B { int x; };
struct C : A, B {};
void a(A *p) { assert(p != NULL); }
void b(B *p) { assert(p != NULL); }

int main()
{
  C* c = 0;
  a(c);
  b(c);
}

If I understand correctly than either of the assertions may or may not fire?
This would make these asserts as worthless as the assert(this!=NULL) case
and the whole idea of testing on NULL wouldn't work. This wich would further
strengthen your last argument. I may even put passing NULL ptrs in the way
you described above on my "don't ever do this" list (Even more effictive C++
;^>). Please prove me wrong.

So the real questions are:

If I cast a pointer to a pointer to baseclass, and this pointer is NULL, is
the pointer to the baseclass guaranteed to remain NULL?
If so, doesn't that also apply to any this pointer?


Greetz,
Martijn (see sig for mailaddress)
--
#include <stddisclaimer.h>
My email address is intentionally invalid agains spam.
Please reply to mlievaar at orion in nl
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: James Kuyper <kuyper@wizard.net>
Date: 1998/07/18
Raw View
David Abrahams wrote:
>
> On 16 Jul 98 03:11:20 GMT, AllanW@my-dejanews.com wrote:
...
> >Absolutely.  Once I've asserted that this!=NULL, I can be ABSOLUTELY
> >CERTAIN that this particular problem hasn't occured.
>
> Wrong-o. See above.

If you verify that "this!=NULL", then the particular problem caused by
the possibility that "this==NULL" hasn't occured. If you make the
mistake of inferring some larger degree of protection, that's your
fault, not a drawback of the technique itself. As has been pointed out,
under some (perhaps most) implementations, it is possible to call a
non-virtual member function using a NULL this. The test catches one of
many possible problems. The fact that there are many other problems that
it doesn't catch is not an argument against making the test.

If a given medicine protects against the common cold, would you refuse
to take it just because it doesn't protect you against anything else?
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: AllanW@my-dejanews.com
Date: 1998/07/14
Raw View
> > Basjes@nlr.nl (Niels Basjes) writes:
> >> I had the following idea to make my C++ code more robust :
> >> Check if the "this" pointer is NULL inside a regular (non static)
> >> member function (example of what I have in mind at the bottom).
> >
> >> Is this kind of checking allowed (according to the standards) and how
> >> portable is it between different compilers that are currently
> >> available (It works on Sun CC 4.1 and GCC 2.7.2) ?

> Steve Clamage wrote:
> > "this" is a pointer, and any pointer can be compared for
> > equality to NULL. "this" will compare equal if and only if
> > it is a null pointer. That isn't the problem.
> >
> > The problem is the test is nearly useless.
Depends on the platform, I guess.  Microsoft defines an ASSERTVALID macro
which (for debug calls only) calls object->isValid() -- which isn't even
defined in non-debug builds.  isValid normally does many checks before
calling base::isValid(), one of which is to check for a null pointer.

> > Calling a non-static
> > member function from a null pointer has undefined results.
Exactly; it is a programmer error, and robust classes should help to
detect programmer errors.

> > The test will protect against the invalid call only sometimes,
> > and with some compilers.
True.  This is a debugging aid, not a panacea.

[Snip]
> > Null pointer checks should occur before the pointer is used
> > in any way, not after.
Yes.  But detecting the error has value as well.  Inside the member
function it's clearly too late to do anything about it, but very often
not too late to detect and report it.

In article <35A6AD74.F1C@noSPAM.central.beasys.com>,
  dtribble@technologist.com wrote:
> On the other hand, you might care to check that 'this' points to
> an object of the *expected* type, once the call does succeed.  This
> check is useful for the following reasons:
[Snipped discussion of object versions, pointers to deleted objects, etc.]
Yes.  I sometimes use all of these techniques in my isValid() functions
(except the checksum -- I've never had to go that far yet).

We may have strayed off-topic here.  This is about standards, not
debugging.  The original question was on topic: Are we allowed to
compare "this" against NULL?  The answer is that this might not
detect 100% of all abuses on all platforms, but it does detect some
errors on some platforms, and it is perfectly valid (in the sense
that "this" should never be NULL in any conforming program on any
platform).  I encourage you to use it in test builds.

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: AllanW@my-dejanews.com
Date: 1998/07/14
Raw View
In article <xgLp1.8826$y4.438237@newscene.newscene.com>,
  "Al Stevens" <alstevens@midifitz.com> wrote:
> >One way to check that an object is of the expected type is to
> >have it contain a "magic number" member with a value that is
> >unique to that class.  If the 'this' object doesn't have the
> >correct magic number, then you know that the object is not correct
> >and something went wrong (probably a bad pointer to a deleted
> >object).  It a good idea to set the magic number member to the
> >correct value in the constructor, and to set it to a bogus
> >(different) value in the destructor.
>
> Maybe you could use typeid for that test and avoid having to contrive a
> magic number data member.
>
> if (typeid(*this) == typeid(Class))

This would fail if class Der was derived from class Class.  Even if this
didn't happen, it might defeat the whole purpose; having the compiler
agree that this should be a Class object doesn't guarantee that the
object is valid.

As for contriving the magic number, nobody says it has to be an
integer.  I sometimes use this:
    class Robust : public Base {
        static char magicA, magicB;
#ifndef NDEBUG
        char *magic1;
#endif
        // ... Other data members ...
#ifndef NDEBUG
        char *magic2;
        bool isValid();
#endif
    public:
        Robust() : magic1(&magicA), magic2(&magicB) { /* ... */ }
        ~Robust() { assert(isValid()); magic1=magic2=0; }
        // ... etc ...
    };
    char Robust::magic;
#ifndef NDEBUG
    bool Robust::isValid() {
        if (!this) return false;
        if (magic1!=&magicA || magic2!=&magicB) return false;
        // Additional sanity checks here
        return Base::isValid(); // or "return true;" if no base classes
    }
#endif

Most member functions call
    assert(isValid());
before doing anything else.  As with all assert() calls, these disappear
in release builds.

Note that the object size changes between debug and release builds.
Sometimes this is a problem, for instance when some of the programmers
might be using production libraries in debug code.  In this case, the
solution is to remove the #ifndef declarations, so that magic1 and
magic2 are always defined.  This takes up 8 extra bytes per object
(on my platform), which is often not very noticable.

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: jkanze@otelo.ibmmail.com
Date: 1998/07/14
Raw View
In article <6oe6b0$f03$1@nnrp1.dejanews.com>,
  AllanW@my-dejanews.com wrote:
> > Steve Clamage wrote:
> > > "this" is a pointer, and any pointer can be compared for
> > > equality to NULL. "this" will compare equal if and only if
> > > it is a null pointer. That isn't the problem.
> > >
> > > The problem is the test is nearly useless.
> Depends on the platform, I guess.  Microsoft defines an ASSERTVALID macro
> which (for debug calls only) calls object->isValid() -- which isn't even
> defined in non-debug builds.  isValid normally does many checks before
> calling base::isValid(), one of which is to check for a null pointer.

So it might be valid as a Microsoft extension.  Although somehow I doubt
it, if virtual functions are involved.  How can they know which virtual
function to call without knowing the dynamic type of the object?  And how
can they know the dynamic type of the object if there isn't one?

I think Steve's point still stands : as a test, it is nearly useless, since
you will normally have crashed before entering the function.

> > > Calling a non-static
> > > member function from a null pointer has undefined results.
> Exactly; it is a programmer error, and robust classes should help to
> detect programmer errors.

Fine, but that has nothing to do with Steve's point.  The point is that
you cannot call a member function through a null pointer, so normally,
any tests made for NULL within the function are too late -- if the
pointer was null, you won't get into the member function.

--
James Kanze    +33 (0)1 39 23 84 71    mailto: kanze@gabi-soft.fr
        +49 (0)69 66 45 33 10    mailto: jkanze@otelo.ibmmail.com
GABI Software, 22 rue Jacques-Lemercier, 78000 Versailles, France
Conseils en informatique orient   e objet --
              -- Beratung in objektorientierter Datenverarbeitung

-----== Posted via Deja News, The Leader in Internet Discussion ==-----
http://www.dejanews.com/rg_mkgrp.xp   Create Your Own Free Member Forum


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: Basjes@nlr.nl (Niels Basjes)
Date: 1998/07/10
Raw View
Hi,

I had the following idea to make my C++ code more robust :
Check if the "this" pointer is NULL inside a regular (non static)
member function (example of what I have in mind at the bottom).

Is this kind of checking allowed (according to the standards) and how
portable is it between different compilers that are currently
available (It works on Sun CC 4.1 and GCC 2.7.2) ?


class foo
{
public:
    void Print()
    {
        if (this == NULL)
            cerr << "ERROR: \"this\" is NULL" << endl;
        else
            cerr << "Bla bla." << endl;
    }
};

int main()
{
    foo * x = new foo();
    x->Print();
    delete (x);
    x=NULL;

    x->Print();
    return 0;
}


--

Best regards,

Niels.

=======================================================================
| ir. Niels Basjes              | National Aerospace Laboratory |  _  |
| Phone       : +31-20-5113381  | Anthony Fokkerweg 2           | /   |
| Fax         : +31-20-5113912  | 1059 CM Amsterdam             |(NLR)|
| E-Mail@NLR  : Basjes@NLR,nl   | The Netherlands               |  _/ |
| E-Mail@Home : Niels@Basjes,nl | http://www.nlr.nl             |     |
=======================================================================
Spam block : Replace the , with a .


[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: stephen.clamage@Sun.COM (Steve Clamage)
Date: 1998/07/10
Raw View
Basjes@nlr.nl (Niels Basjes) writes:

>I had the following idea to make my C++ code more robust :
>Check if the "this" pointer is NULL inside a regular (non static)
>member function (example of what I have in mind at the bottom).

>Is this kind of checking allowed (according to the standards) and how
>portable is it between different compilers that are currently
>available (It works on Sun CC 4.1 and GCC 2.7.2) ?

"this" is a pointer, and any pointer can be compared for
equality to NULL. "this" will compare equal if and only if
it is a null pointer. That isn't the problem.

The problem is the test is nearly useless. Calling a non-static
member function from a null pointer has undefined results.
The test will protect against the invalid call only sometimes,
and with some compilers. If the function is virtual, the call won't
succeed, and you never get to the point where you check for null.

The problem can also show up when calling a non-virtual function
of a base class from a null pointer of derived-class type.
Since calling the function via a null pointer is invalid,
compilers typically do not check for null before applying
the pointer adjustment. (Doing so would slow down every function
call.) The function gets called with a non-null pointer that doesn't
point to an object. Boom!

Null pointer checks should occur before the pointer is used
in any way, not after.

---
Steve Clamage, stephen.clamage@sun.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://reality.sgi.com/austern_mti/std-c++/faq.html              ]






Author: David R Tribble <david.tribble@noSPAM.central.beasys.com>
Date: 1998/07/11
Raw View
> Basjes@nlr.nl (Niels Basjes) writes:
>> I had the following idea to make my C++ code more robust :
>> Check if the "this" pointer is NULL inside a regular (non static)
>> member function (example of what I have in mind at the bottom).
>
>> Is this kind of checking allowed (according to the standards) and how
>> portable is it between different compilers that are currently
>> available (It works on Sun CC 4.1 and GCC 2.7.2) ?

Steve Clamage wrote:
> "this" is a pointer, and any pointer can be compared for
> equality to NULL. "this" will compare equal if and only if
> it is a null pointer. That isn't the problem.
>
> The problem is the test is nearly useless. Calling a non-static
> member function from a null pointer has undefined results.
> The test will protect against the invalid call only sometimes,
> and with some compilers. If the function is virtual, the call won't
> succeed, and you never get to the point where you check for null.
>
> The problem can also show up when calling a non-virtual function
> of a base class from a null pointer of derived-class type.
> Since calling the function via a null pointer is invalid,
> compilers typically do not check for null before applying
> the pointer adjustment. (Doing so would slow down every function
> call.) The function gets called with a non-null pointer that doesn't
> point to an object. Boom!
>
> Null pointer checks should occur before the pointer is used
> in any way, not after.

Quite right.

On the other hand, you might care to check that 'this' points to
an object of the *expected* type, once the call does succeed.  This
check is useful for the following reasons:

1.  Client code may be out of date with respect to your library
code, meaning that client code that contructs an object of type Foo
might be compiled with an obsolete header file, so his Foo object
is different that what your library code expects.  Or the other
way around, the client could be compiled using the newer header
file for the class but his program is linked with an obsolete
version of your library.  This is sometimes referred to as an
"object versioning" or "version mismatch" problem.

2.  Pointers to deleted objects might not get reset to null, and
might get used later under the assumption that they still point
to live objects.  In this situation, the pointers are no longer
pointing to proper objects.  Worse, they could be pointing to
objects of a completely different type that reside in the same
memory that used to be occupied by the old object.

3.  Object data might get corrupted.

One way to check that an object is of the expected type is to
have it contain a "magic number" member with a value that is
unique to that class.  If the 'this' object doesn't have the
correct magic number, then you know that the object is not correct
and something went wrong (probably a bad pointer to a deleted
object).  It a good idea to set the magic number member to the
correct value in the constructor, and to set it to a bogus
(different) value in the destructor.

You can also check that the object is of the expected version,
to detect client/library version mismatches.  One way is to store
the version number as a private member in the object, and have
every member function validate it before they access any other
members of the object.

Finally, you can go all out and put some sort of checksum in the
object, in order to detect object corruption.  This is expensive
because the checksum must be recomputed every time you change
a member of the object.  But for really critical applications,
this may be an acceptable cost for reliability.

Be warned, also, that changing the members of a class (for a
new release of your library) can spell disaster for old client
code, especially if the class has virtual member functions.
Changing class declarations for new releases must be planned
carefully, starting at version 1.0.

Another problem to consider is that NULL is only one invalid
pointer value out of all the possible invalid values (as many
as 2^32 bad values are possible on 32-bit systems).  Calling a
member function via a corrupt pointer might be impossible to
detect until it's too late.  The best strategy is to try to
restrict your client's access to your objects as much as possible.
But be aware that you can't completely prevent clients from
handing you bad pointers.


-- David R. Tribble, dtribble@technologist.com --
-- C++, the PL/1 of the 90s.
---
[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]





Author: "Al Stevens" <alstevens@midifitz.com>
Date: 1998/07/11
Raw View
>One way to check that an object is of the expected type is to
>have it contain a "magic number" member with a value that is
>unique to that class.  If the 'this' object doesn't have the
>correct magic number, then you know that the object is not correct
>and something went wrong (probably a bad pointer to a deleted
>object).  It a good idea to set the magic number member to the
>correct value in the constructor, and to set it to a bogus
>(different) value in the destructor.


Maybe you could use typeid for that test and avoid having to contrive a
magic number data member.

if (typeid(*this) == typeid(Class))



[ 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://reality.sgi.com/austern_mti/std-c++/faq.html              ]