OOP_(re)

Thu, 5 Jun 1997 16:01:42 +0200



Peter Gerwinski wrote:

> > > Okay for ordinal types, sets and pointers, not sure about Reals.
> > > Strings initialized to zero this way are BROKEN because they get
> > > a capacity of zero which makes them useless.  :-( That's one reason
> > > why I would like to have `ShortString's in GPC.)
> >
> > Ok, right, so FillChar() is a BAD idea... Ok! :-)
>
> ... except if you know exactly that no implicitly initialized types
> - at the moment, these are File and Schema (including String) types -
> are present in the object.  Since they don't belong there anyway
> (pointers to Strings are better than Strings here), `FillChar' can
> still be useful (while dangerous - which it is always).

The danger is with (possibly unknown) descendants that declare such types.

> At least a `BPcompat Objects' Unit *must* have this FillChar. :-|

Hmmm... there could be problems if an object in a ported BP program has a
string field (at least until short strings are implemented and "string[]"
automatically generates short strings).

BTW: Do TV programs actually rely on the zeroing out, or is this just a
paranoia initialization?

> It's done like this at the moment, but BP does it in another way.
> As long as I don't know why (i.e. I suspect some disadvantages of the
> method used currently which didn't show up yet), I am very careful about
> fixing it.

BP must do it within the constructor, because it allocates the memory within
the constructor (in a "New(p,Init)" construct). This reduces the question to:
why do they allocate the memory within the constructor? I suppose they do it
in order to use a common "Constructor support routine" (in objh.asm) -- some
kind of size optimization at the expense of speed. They do that in other
places, too, most prominently the range and stack checks (which are explicit
procedure calls that save a few bytes but cause a big slowdown). Since gpc
favours speed instead of size, I think it's good to do the initialization
outside.

> > But there is another problem, namely to decide whether to call the
> > constructor virtually (i.e. looking up its address via the VMT) or
> > "statically".
>
> What's the problem?  It's just calling another virtual method.

Right. I had forgotten how gpc calls constructors.
But then the question is whether to initialize the VMT before the call.
With the method below, it's simple: initialize it if (and only if) it
hasn't been initialized before.

What must be prevented is that in the following code, the "Load" call would
"initialize" p's VMT link to that of TObject (assuming Load is a virtual
constructor):

  p:PObject
  ...
  Getmem(p,...);
  AssignType(p^,Typeof(TFooObject));
  p^.Load(Stream);

> It is possible:  I would have to pre-initialize (set the VMT to zero)
> each object variable when created:  global variables at startup time, local
> variables when the procedure is entered, dynamical variables when `new'ed
> (without constructor call which would make the pre-initialization
> superfluous).

I think this is a good idea. Perhaps also resetting the VMT after a
destructor call (which is superfluous in a Dispose statement, of course).

> > I could perhaps help with designing the structures and algorithms (see
> > below ;-), if desired,
> > but probably not with hacking the compiler...
>
> Why not?  GPC doesn't bite!  (-; If it does, this is a bug which must be
> reported! :-)

Perhaps gcc, that gpc is written in, bites...

> (* There is only one problem with hacking GPC:  It's addictive ... :*)

Yes, that's the main problem since my Recursive Time-Doubler has broken. ;-)

> > >     const
> > >         SIID_IActiveScriptSiteWindow = '{D10F6761-83E9-11cf-8F20-00805F2CD064}';
>
> I cannot believe that.  Why should they enter it as a String constant, then?

Perhaps to make it more "readable" with the separating hyphens???

> And there are braces, etc.  No, I believe that this is indeed a string to be
> interpreted by some program lying around somewhere ... so we should provide a
> string, too. :-|

I don't think so. If Delphi does it in a bad way, we don't have to duplicate
that. But it would be good if a Delphi user could actually check what Delphi
does.

> > Anyway, I think at least in gpc the internal representation should be an
> > integer, not a string.
>
> Since that other program obviously wants the number as a string surrounded by
> braces, this would be no good idea with respect to compatibility.

If the "other prorgam" is part of the standard library, it would be ok (just
adapting our library to accept integers). Otherwise perhaps declare a
function to convert a numeric ID to a string. This function would have to be
inserted into the Delphi source then, but with a dummy function declared in
Delphi, the code could still be used in Delphi.

BTW: What about the QueryInterface things? They might make "100% source
compatibility" with Delphi impossible, anyway, as soon as interfaces are
used.

> I understand that this will work, but I fear the many special cases it would
> cause to implement a non-standard-format pointer:  They would be allowed in
> all contexts where "ordinary" pointers are allowed, so all internal GPC
> routines handling pointers would have to be modified. :-(

Doesn't the same apply to your method? You pointers are non-standard, too.
E.g., when you want to convert them to an untyped pointer, you have to
subtract the offset from them.

> BTW, using this method, we could also implement pointers to "ordinary" objects
> thus saving the VMT field in each instance.  It's kind of an "external field"
> for each object instance ...

Right, though I doubt one would usually save memory this way, because "most"
(I guess) objects have a pointer pointing to them, many objects have even
more than one pointer. And it would complicate (to the compiler) accessing
VMT fields (ObjID, object constants, class variables).

> Facing the enormous technical problems with your method I am preparing to
> implement mine.  :(Yours is nice too - sorry - and very innovative.  Perhaps
> you want to try yourself on it?:)

I think I've found a third way, one that doesn't influence the pointer format
and doesn't blow up the objects. It does, however, waste some space in the
VMTs. I'm sending it to you by private mail (mainly because it's easier for
me to describe it in German) -- sorry to other readers of the list who are
interested in this discussion, but we'll post the results back to the list
-- whatever it will be finally...

> > But I still can't understand why you can't simply use the pointer from the
> > beginning, i.e. when you assign IDs (like "Factor1ID := Factor1^.ID"), you
> > just do "Factor1ID := @Factor1". Then you have the pointer when you need it.
> > Is it that the whole object "Factor1" can change (be reallocated), not only
> > its contents change -- the "aliasing bug"?  [...]
>
> Here is an example from praxis.
> [skipped desription]

AFAIS, this is the "aliasing bug" and can be solved with pointer-pointers.
(See my reply to the chief, later.)

> I agree this is a design fault in TV, but I doubt that it's is possible to
> design a class library which will never need changes in the innermost part ...

No, not never. But I was objecting to the idea that everyone does their own
changes in such a library just when they need them at the moment.

> > If this doesn't work well (i.e. if many diffs between text and graphics
> > version remain), this might be an indication that TV and TV/graphics are
> > simply different enough to not share the same classes. (And actually, one
> > would hardly ever use objects of both packages in the same program.) So they
> > could be simply two packages with "similar" source then...
>
> Here I disagree.  I have written some programs which can run, on demand, in
> text mode as well as in graphics mode, and *all* objects are shared between
> "both packages" (which are in fact one).

No, you don't disagree! :-) I said "if this doesn't work well..." --
obviously it did after your changes! :-)

> While generalizing Turbo Vision for graphics mode (thus creating BO3), it
> was necessary to introduce new fields to `tView'.  Since I didn't have the
> TV source (I didn't want to spend 600DM (~$400) for it (but finally did,
> you know ...): ), I had to derive successor objects from *each* for-real-use
> object: `tWindow', `tDialog', `tInputLine', ... it was not only bulk of work,
> but also very unclean Pascal.  Once I had the TV source, I threw away BO3,
> added the necessary fields to `tView' and essentially re-wrote TV (BO4.  My
> next project of this kind, BO5, will be completely independend of TV (... like
> BO2 was.  I had better continued BO2 rather than trying to use TV ...)).
>
> This example (where to have fields) proves how difficult it is to design a
> class library for general use.  It might be a good idea to introduce some
> initially unemployed data fields in some base objects just for future use.
> (Not in *the* base object, but somewhere higher in the hierarchy, where
> space doesn't matter so much.)

But how many fields and which type??? I don't think that's a good idea.

Since I don't know what kind of field these are, I'm not sure if the
following applies to the situation:

Put the mode-dependent fields (those that change in all the view objects
between text and graphic mode) into a separate object. There could be an
(abstract) superclass, and special subclasses for text resp. graphics mode.
The actual objects would contain a pointer of the abstract spureclass type,
and the constructors would create text or graphics mode objects as desired.

Of course, this does involve changes to TV, but such changes that "improve"
its design (here: make it more flexible), not putting things in it that
don't belong there.

Actually, this is quite a general principle to get something like "separate
inheritance" (of different parts of an object), by splitting up the object
into several part-objects. There is an overhead (one additional pointer and
its dereferencing), OTOH one can often eliminate case discriminations by
clever use of virtual methods in the new part-objects.
-- 
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