OOP_(re)

Fri, 30 May 1997 00:35:54 +0200



David Fiddes wrote:

> From: Frank Heckenbach 
> 
> >Would an ID of 8 bytes matter, i.e. are there any stream files where
> >4 additional bytes per object would matter?
> 
>
> Why not make them 16 bytes? Sounds excessive maybe but that's how big a
> Globally Unique IDentifier (GUID)(or more pretensiously a Universally
> Unique IDentifier (UUID)) is...128bits....and there's a stanard for
> generating them...and anyone with a windows machine has an implementation
> of that(and the Mac and some UNIX boxes too). Upwardly comaptible with
> Microsofts Component Object Model(COM) and I'm sure the likes of CORBA use
> them too...

Is this a standard set by M$? If so, I'd only vote for using it, if it (unlike
most M$ "standards")
- was openly documented,
- was not subject to change at M$'s decision, and
- can be used for purposes like ours without possibly conflicting with
  other (future) uses

If this is so, I'd like to know more about these IDs.

The African Chief wrote:

> >BTW: Would it be possible at all? Doesn't TObject refer to TStream?
> No, it doesn't. In bpcompat, TObject has a "Load" constructor that takes
> a TObject parameter.

But doesn't it have to refer to the stream somehow? Do you type-cast the
parameter into a stream (which I wouldn't like, since IMHO type-casting
should be avoided wherever possible - it's the "goto" of data structures),
or did you implement some of a stream's properties within TObject (which
I also don't like, see the discussion about handles)? Sorry for asking,
but I don't have bpcompat at hand, and I can't imagine another way...

> >I don't know all the details here, but isn't it possible to have a
> >TWindow(TObject) with Handle being a field of TWindow, and this function
> >returning a PWindow? This would seem natural to me.
>
> Yes, that is possible. But I don't think it does any harm to have the Handle
> in TObject instead.

Perhaps not (besides wasting 4 bytes...), but I only know from my own
experience that I did sometimes regret thoughts like that. Therefore, I'd
prefer to think about classes very exactly, especially about such base
classes. And again, what's the disadvantage of putting Handle into
another class?

> Perhaps. But there are many times when I have wished that certain
> things existed in BP's TObject and TWindowsObject. Making a
> descendant object is fair enough, but it would have been a lot easier
> for me if the things were in the ancestor.

Easier perhaps, at least at first sight. But when many people use a class
library and change it like that, things can get very complicated soon.
It is a bit more work to create a more detailed class hierarchy and to
introduce all the fields at the "right" place, but I think in the long run,
it's worth the effort - and it's more in the spirit of OOP, as I understand
it.

> >I'm probably missing something - but isn't the address of an object a
> >"unique ID"? (One that makes it very easy to "locate" the object... ;-)

As I said, I might be missing something, but I still don't know what it is...

> That assumes that you already know the object and its address.

If you used the address everywhere you use the ID now, you would know,
wouldn't you?

> If you
> already know  it, there would be no point trying to locate it again.

That's what I meant by "very easy to locate"...

> Also,
> there are a whole load of things that you can do with integers that you
> cannot do with addresses.

Yes, but what do you need to do with IDs? AFAICS, one only needs to compare
them (for equality, not for greater/less), and this can be done with
addresses.

> And, suppose that, for some obscure reason,
> I suddenly wanted to locate all currently active object instances and/or
> their parents (if any), how would I do this using addresses? - but I could
> do this with a loop through the SelfIDs.

Where do you get the SelfIDs from? Perhaps a list of IDs stored in a parent
object? You could put the addresses there instead, couldn't you?

I'm really trying to understand what you do with these IDs!

I hope, it's not only the (bad, IMHO) tendency of Windoze to use "untyped"
handles for each and everything - that might not matter much in C (where
everything is int anway - well almost...), but I think in Pascal we should
try to keep as much typed information (e.g. typed pointers) as possible.

Pierre Phaneuf wrote:

> > Then it would have to be in the GPI file.  Much effort.  I think the few
> > bytes we can save like that (Just those for the strings plus one pointer
> > in each VMT) are not worth it, but perhaps somebody else wants to try
> > himself on this problem? ;-)
>
> This is totally optional to me, and thinking more about it, I was
> wondering of what use having the symbol name of an object at runtime...

I'm also wondering! I never used Delphi, so I don't know what use it has
there. But from my experience, I don't know any case where this is needed -
except debugging, of course, but that's another story.

I think, if some program for any reason does need this information, it could
declare object constants (if implemented), or methods (in the meantime) that
provide this information explicitly, but unless there's a wide need for this
information (which I can't see), I don't think it should be included
automatically.

> >     (* I suggest to define a pointer to a procedure like the constructor *)
> >     (* with an *explicit* `Self' parameter, to cast `LoadPtr' to it and  *)
> >     (* then call it.                                                     *)
>
> Its the explicit part I'm trying to avoid... I guess this part will be
> compiler dependent, but should be among very few (with the TObject.Init
> constructor).

I agree. Also, I don't like the type-casting bit.
But, AFAICS, making Load a virtual method (or virtual constructor in the
future?) would remove all these problems. Since the VMT has been assigned at
this point, a virtual method can be called without any tricks.

> Also note that Java has been widely
> recognized as "C++ as it should have been" (by Bjarne Stroustrup among
> others, the designer of C++), and Java *doesn't* have multiple
> inheritance.

:-)

So, let's make "BP objects as they should have been"! :-)

> For instance, I tried to make my class as much object-oriented as
> possible, free of any procedures. For example, Abstract has been made a
> method of TObject and a similar NoStream method has been added to prevent
> streaming of non-streamable objects (this is very quite not final,
> susceptible to removal).

Perhaps "streamability" would be a good choice for an interface (with the
methods load and store)?

> A thing that would be better to have than multiple inheritance would be
> pure virtual methods. Would allow me to remove that Abstract method! ;-)

I assume "pure virtual" is the same as "abstract" methods!?
I.e., any class that has at least one abstract method can't be instantiated.

This would indeed be a much better way than Borland's "abtract method" that
causes a runtime error (again, IMHO, a failure by Borland)!

The African Chief wrote:

> No. C++ is clumsy and bloated. However, the problem is that MI is part
> of the language definition. If it were to be implemented in GPC, it wouldn't
> be part of any definition. It would just be a local extension that could be
> ignored by anybody who so wishes. I don't see that having such an
> extension is so bad. Some day, someone will want to use it, and it
> doesn't help to tell them that they don't need it.

Since interfaces are probably easier to implement (see my other mail), and
sufficient for most cases (including all that I know), I suggest implementing
interfaces first. If then there is a need for MI that really can't be solved
with interfaces, it's time to think about adding MI (but actually I'm quite
confident this won't be the case).

> A purely personal thing, perhaps. If I did it my own way, without any
> preconceived notions, I would have an ancestor object which would
> contain everything that I think every object should have.

Right, everything that *every* object should have, not everything that *some*
objects should have. E.g. handles: not every object has a handle, so it should
not be in the base class. If in a windowing system, every instance of TWindow
and its childs will have a handle, then Handle should be a field of TWindow.

I know that this complicates matters, especially in the base classes, but
since this is mostly a one-time job, I think it's worth the effort.

> What I think
> every object should have changes from time to time, and therefore, for
> me, it is better to start with more, than with less ;-)

As I said above, if many people with different opinions worked on such a
class library, it would always be changing. AFAIK, the purpose of OOP is
to have some base classes that can be used by everybody, and more special
classes built on top of them that implement the needs of different
applications.

> > I really don't see what a TWindow object and a
> >TString object has in common that should be in TObject!
>
> They could all have a Name, a SelfID, a Parent, and a Child.

Do you mean a name/ID/parent/child of the *class*, or of the *instance*?
In the former case, I agree (except for the name, which IMHO usually isn't
needed at runtime, see above), in the latter case I don't: a string doesn't
have a parent or a child or a name (besides the compile-time identifier),
and I don't think it usually needs an ID.

> >BP didn't set that
> >trend BTW. The Smalltalk anscestor object doesn't do *ANYTHING*. 
> 
> The first thing I would do to it is to change that omission ;)

Why not simply derive one (or several) classes from it with whatever you
want? That would be in the spirit of OOP (not to change existing classes,
but just derive new ones from them).

> And what is the point of have the ancestor then - just for type
> compatibility ?

Yes - to do things in a typed way that need "Void" or "untyped parameters"
otherwise.

So it should have only properties that really *every* object has - which, of
course, can't be many, merely administrative things like a VMT pointer,
perhaps an empty constructor and destructor, and properties of the class
like parent class, (perhaps) child classes, (perhaps) implemented interfaces,
(perhaps) ID.

Anything more (e.g. load and store methods) will not be common to all objects
(e.g. a mouse object probably can't be stored in a stream), so this should be
introduced in another class (or interface).

> >BPs constructor fills the object with zeros for instance. :-)
>
> Not very useful at all. They might as well not have any constructor
> or destructor.

Agreed. Since TObject is an abstract class, it really doesn't need a
constructor. The destructor is ok, since it's virtual, therefore must be
declared at the base class (this allows removing any object with
"Dispose(...,Done)").

Peter Gerwinski wrote:

> I suggest to forget about the switch and just store a pointer to a string
> constant holding the name of the object in the VMT.  Since we don't need
> to care about a 64k data segment (4GB will be enough for a while), these
> few extra bytes are harmless.

...if there's a real need for it. Otherwise I think, compile-time information
like identfiers does not belong in the executable (apart from debug info).
I think some people wouldn't like their identifiers to be revealed in a
program that's distributed without source.

> > [...] In general, I'm
> > against "magic" things, like the New() function, or the Write()/WriteLn()
> > functions (in BP at least, they cannot be implemented using the
> > language!).
>
> So am I.  That's one beauty of C:  Everything, including `printf' and such,
> is user-definable.  (Said by someone who is hacking a Pascal compiler because
> he cannot stand the ugly syntax of C/C++ ... ;-)

Many "un-definable" Pascal procedures could be defined with overloading
and/or optional parameters. Both is possible to do in Pascal, and I hope,
gpc will have them sometime.

New and Write (and so on) are (basically) a syntactical simplification for
calling procedures that could have been implemented in Pascal. And this is
NOT user-definable in C, it's simply missing there! Printf requires the
user to explicitly state all the types, while Write does this automatically,
and "p=(*t)malloc(sizeof(t))" is not quite the same as "new(p)".

Oh, BTW: "sizeof" is not user-definable in C - so they just made it an
operator instead of a function, what a difference! ;-)

So don't admire C too much for this... ;-)
-- 
Frank Heckenbach, Erlangen, Germany
heckenb@mi.uni-erlangen.de
Turbo Pascal:   http://www.mi.uni-erlangen.de/~heckenb/programs.htm
Internet links: http://www.mi.uni-erlangen.de/~heckenb/links.htm


Frank Heckenbach (heckenb@mi.uni-erlangen.de)

HTML conversion by Lluís de Yzaguirre i Maura
Institut de Lingüística Aplicada - Universitat "Pompeu Fabra"
e-mail: de_yza@upf.es