From: Walter Bright on
David Abrahams wrote:
> When I make that claim about C++ I am never thinking specifically
> about references through invalid pointers, because that basically
> never happens to me anymore.

They rarely happen to me anymore, either, but that is not necessarily
because C++ has gotten better. It's just that, over the many years, I've
sort of built up patterns of thought and practice that protect against them.

It's like wandering through your house at night with the lights off. You
eventually learn where the stairs and coffee table are and remember not
to fall down them or bark your shins <g>.

But it's unreasonable to expect that level of expertise in general.

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

From: Andrei Alexandrescu (See Website For Email) on
David Abrahams wrote:
> I guess so. We were talking more generally about using a pointer to
> an object that another part of your program thought it had torn down
> or caused to disappear. In C++ that translates into arbitrary memory
> overwrites which can sometimes be detected by the OS or an external
> tool; in Java that translates into the use of a zombie object, which
> can never be detected except maybe by the addition of handwritten
> asserts to your code.

Which is an important thing! I'm not sure I understand the point. Do you
call a "zombie object" something like, e.g., a closed file? In that
case, isn't it a good thing that that file's write method can check that
the file is still open? And isn't it comforting to know that the file
could only have been closed by actual legal code? I mean, these zombie
objects are not just appearing in the program nilly-willy, they are the
result of the runtime deterministically executing code as instructed by
code. No? So then what would there be to complain about?

>>>>The memory-safe program wins because it never overwrites arbitrary
>>>>memory; so all objects unaffected by a bug respect their invariants.
>>>
>>>The same is trivially true of C++: all objects unaffected by a bug
>>>respect their invariants.
>>
>>This is wrong.
>
>
> How can it possibly be wrong?

I don't care about a set that I know exists but I can't define.

> I understand that if you believe certain "sealed" subsystems (like a
> logging module) are themselves bug-free then you don't need to look at
> them for the cause of your bug... but then you wouldn't look to such a
> system in a C++ program either, even if it *can* get stomped by
> erroneous code.
>
> I guess I just don't see any black-and-white difference here. Can you
> help me understand how this advantage plays out in practice?

I don't think I can give it a better shot, partly because I can't
dedicate enough time to it, partly because I can't explain it well, and
partly because the whole issue is not as black and white as you might
think I believe it is. It's not that someone can sit down with pen and
paper and prove formally something of this kind.


Andrei

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

From: Andrei Alexandrescu (See Website For Email) on
David Abrahams wrote:
> "Andrei Alexandrescu (See Website For Email)"
>>I disagree. As I explained before: in Java bugs can be made modular in
>>ways that are not possible in C++, because you have true memory
>>isolation between objects.
>
>
> True memory isolation is very nice, but I don't see how it helps make
> bugs modular in practice. Once you discover something is wrong you
> have no way of knowing where the code causing the wrongness was or
> what the wrongness has affected [see below].
>
> When I make that claim about C++ I am never thinking specifically
> about references through invalid pointers, because that basically
> never happens to me anymore. I'm just thinking about what happens to
> the program state.

Well if we consider C++ code that never references the wrong memory, the
differences from Java narrow considerably, and whatever arguments I can
bring in the discussion narrows considerably.

> One way to think of this is that if you imagine the use of
> FORTRAN-style data structures with indexing instead of pointers (and
> actually I think in some sense you need these in Java because there's
> no pointer arithmetic), even integers can "dangle." Just shorten an
> array and there you are. So I don't think there's anything intrinsic
> in keeping pointers valid (even if valid only means pointing to an
> object that should really be gone) that limits the spread of broken
> invariants.

There is something intrinsic. The off-bounds integer can be easily
caught; the dangling pointer can't (unless it's implemented otherwise
than a memory address, which I don't see as interesting).

> However, I doubt the practicality of writing whole systems that way.
> Eventually you'll end up with functions that require a certain
> relationship between parameters (e.g. end >= begin), and then to
> maintain the "no assumptions about the inputs" stance you have to
> check these relationships, and throwing an exception becomes part of
> the function's defined behavior. Since the function calling with
> arguments (end < begin) is already holding broken data or broken
> assumptions, all hell then breaks loose.

I don't understand this part. Basically if you have memory safety it's
possible with language-provided encapsulation to define a type that is
able to preserve its own invariant. No? I don't see the hell breaking
loose. It's holding tight :o).

>>>Meaning that in Java, all writes of "references" (a.k.a. pointers) are
>>>synchronized?
>>
>>That is correct. They are guaranteed to be atomic; there is no invalid
>>reference in Java, ever, period.
>
>
> Wow; that does sound slow :)

It's not synchronized via a lock; it's just guaranteed to be an atomic
write. I think that's an important guarantee - it's one of the
guarantees that allows memory safety.

>>Than "all the hell breaks loose starting at this point".
>
>
> Oh, but that's not guaranteed either. If you're going to compare
> value, you should compare the Java guarantee against:
>
> * better speed
> * the flexibility to respond to errors in special ways
> * (I had another but then it flitted away)
>
> Anyway, I'm not claiming to know what's more valuable in the long run,
> I'm just challenging what I think are some of the usual assumptions
> around this question, one of goes something like, "undefined behavior
> in a language has no merits."

Never claimed that. Features with undefined behavior confer efficiency
and expressiveness to programs. The art is finding ways to preserve as
much as possible the speed and the expressiveness, while statically
disallowing programs that aren't correct.


Andrei

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

From: Walter Bright on
David Abrahams wrote:
> I understand that if you believe certain "sealed" subsystems (like a
> logging module) are themselves bug-free then you don't need to look at
> them for the cause of your bug... but then you wouldn't look to such a
> system in a C++ program either, even if it *can* get stomped by
> erroneous code.
>
> I guess I just don't see any black-and-white difference here. Can you
> help me understand how this advantage plays out in practice?

Let's suppose you discover that one of your objects of type X has an
erroneous value in one of its members. With a language that guarantees
that only references to X can ever modify a member of X, you only need
to look at references to X to find (at least the next step) in solving
the problem.

With C++, which can easily have pointer bugs, it could be any code in
the whole program which might be the source of the problem. More things
to check means it takes longer and is harder to track down the problem.

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

From: James Kanze on
Andrei Alexandrescu (See Website For Email) wrote:
> James Kanze wrote:
> > Andrei Alexandrescu (See Website For Email) wrote:
> >> David Abrahams wrote:

> >>> Meaning that in Java, all writes of "references" (a.k.a. pointers) are
> >>> synchronized?

> >> That is correct. They are guaranteed to be atomic; there is no invalid
> >> reference in Java, ever, period.

> > For a particular definition of "invalid". As Jean-Marc pointed
> > out, you can very easily end up with pointers to invalid
> > objects.

> I said "invalid pointer", not "pointer to invalid object".

> "Invalid pointer" = typed pointer pointing to a region of memory that
> the program does not assume is of that type.

I'm having trouble parsing that last statement.

Be it Java or C++, you can end up with a pointer to a destructed
object. Raw memory, in sum. In C++, that's called an invalid
pointer. In C++, of course, there are also other ways of
getting invalid pointers---just forget to initialize one, for
example, and the problem is made worse by the fact that the
system may reuse that raw memory for some other object, perhaps
of a completely different type. But in the end, saying that a
pointer is valid when it points to an object that has ceased to
live, and is no longer usable is just playing word games.

> > I think you and I basically agree here (based at least partially
> > on earlier discussions concerning garbage collection). There is
> > an enhanced degree of safety in Java in this regard. But I
> > don't like statements like "there is no invalid reference" or
> > "you cannot leak memory" (which one often hears)---they give a
> > false sense of security.

> I can't help it. As far as I know there is no invalid reference, but
> there are ways to leak memory.

Whether they exist or not, I've seen both in real Java programs.

> I agree there could be references to objects in states that you didn't
> expect.

If you consider "no longer logically existing" as a state you
didn't expect. In practice, the problem is pretty much the same
as that of a dangling pointer in C++: in Java, you could
potentially protect against it, and detect the case (not that
anyone does), where as in C++, there's a good chance of the
program crashing, which you can't ignore (but it's not
guaranteed, and all too often, where it crashes is far from the
offending code).

> > It's much easier to protect against
> > accidentally using an invalid object, since the memory
> > containing the object cannot be used for any other use as long
> > as there is a pointer to it, but the object may still be
> > invalid.

> Oui. The thing is, the program invalidated the object itself with
> operations defined by the object; it wasn't invalidated as result of
> some unrelated invalid object.

But that doesn't stop it from being undefined behavior. If I do
"delete p" in C++, then use p, p wasn't invalidated as a result
of some unrelated invalid object.

I agree that there are a lot more ways to get undefined behavior
in C++ than in Java. But some of the ways work in both
languages: violating thread safety, etc. And while I don't
think I'd consider using a pointer to a "disposed" object really
undefined behavior in Java, the effect can often be pretty much
the same. (It's not undefined behavior, IMHO, because you can
establish invariants which can be checked at runtime. It's
pretty much the same thing because people don't, including the
people who wrote the standard libraries.)

--
James Kanze (GABI Software) email:james.kanze(a)gmail.com
Conseils en informatique orient�e objet/
Beratung in objektorientierter Datenverarbeitung
9 place S�mard, 78210 St.-Cyr-l'�cole, France, +33 (0)1 30 23 00 34


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