Re: C++ Frequently Questioned Answers
Eugene Gershnik wrote:
On Nov 10, 4:58 am, "Andrei Alexandrescu (See Website For Email)"
<SeeWebsiteForEm...@erdani.org> wrote:
David Abrahams wrote:
It's very simple. I'm saying that a program's memory is highly and
implicitly structured, e.g. there are pointers pointing to various
memory objects and there's always an implicit assumption (that was
partially proven during compilation) that the pointers point to
appropriately organized memory. In contrast, a resource such as a file
has no structure - it's essentially untyped, and all we can do is to
manually implement protocols that confer it structure.
I don't think I understand your point. To give a concrete example on
Win32 almost everything: files, mutexes, threads are all opaque
HANDLEs. They are typed and very much structured. The program has to
know what methods can be called on a particular handle. If you call
ReleaseMutex on a file handle (allowed by syntax) the results are UB.
Isn't this exactly how memory pointers behave? Is yes, what is the
difference?
First, I recall there is a strict mode in Windows programming in which
many handles have different types (HBITMAP, HBRUSH, HINTERNET etc.)
although indeed I also seem to recall that the examples you mention
(files, mutexes, and threads) indeed do not have separate types assigned
to them. But that does not dilute your argument, which I will distill
to: "Since one can devise an API based entirely on void*, how come
memory is more structured than anything else?"
The answer is, void* describes untyped memory which is not that much
different from a file that can only be type as a collection of bits. But
as soon as typing comes into play, memory does become "special". Consider:
struct Foo
{
float x;
Bar * p;
};
The integrity of the program using a Foo is dependent (and partially
proven during compilation) by the fact that when you have a Foo and you
fetch its first 4 bytes, they are going to look and feel like a float,
and when you fetch the next 4 bytes and dereference that as a pointer,
you'll get something that looks more or less like a Bar.
You can argue that inside OS HANDLEs are just pointers or indices to
some memory but this level and this memory are completely hidden from
application code. As far as this code is concerned HANDLE is just an
opaque artifact not connected to memory in any way.
That is correct. HANDLEs are as good (actually as bad) at describing
structured memory as void* is. In fact, is HANDLE is a typedef for void*?
And how do we
implement them? By relying on structured memory (e.g. encapsulation,
virtual functions, etc.)
Yes, but I think the word 'memory' in this discussion takes two
slightly different meanings. You use it in the most general sense
while in the resource-or-not duscussion people usually mean the subset
programmer explicitly asks for.
I don't think anybody would argue that all kinds of memory be treated
the same way as files or sockets (this would be infinitey recursive as
you point out above). The claim is solely about the parts explicitly
allocated by some action of the code (i.e. new, malloc etc.)
It's not infinite recursion (did you actually mean infinite regression?)
that's the issue. The issue is that freeing dynamically-allocated memory
poses life-threatening risks to the program in ways that closing files
or sockets does not.
I have tried to argue on this newsgroup and in my talk of this year at
ACCU that garbage collection is fundamental to type safety, with limited
success, so I'm not getting my hopes high this time either :o).
It's true that every step of every program depends on the integrity of
memory, whereas other resources are in some sense "optional."
Only in abstract sense. Every non-trivial program communicates with
outside world and, these days, often deals with multiple threads.
These facilities are not optional in practice. At the end the user
doesn't care whether the program misbehaves because of access to
invalid memory location or because a mutex wasn't locked.
That is correct. I wouldn't mix mutexes in the discussion as threads are
an unwieldy beast of their own. But what I'm saying is that detecting
e.g. writes through a closed file/socket handle is possible, while
dereference of a dangling pointer is not. Why? Because memory is "special".
Andrei
--
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]