OOP_(re)

Mon, 26 May 1997 14:48:21 +0200



Peter Gerwinski wrote:

> > Couldn't "Load" be a virtual method instead of a constructor (perhaps a
> > Boolean function that returns if it didn't fail)?
>
> Yes.  IMHO, it could be in BP as well.

I never used TV, but I'd think so, too.

> > And it would be even better if the compiler could do this automatically,
> > i.e. each object type could (optionally) declare an "ObjType" value, and
> > the compiler would provide functions to get the ObjType from a VMT link
> > and vice versa.
> 
> Sounds nice, but how to find a unique number for each object?  Derive it
> from the name?

I don't think it's possible (or sensible) that the compiler derives it
automatically. I thought about this problem several times, but didn't
find a "good" solution. The name? It can change (if you change your naming
conventions, or just don't like the name you chose), and it's against the
"rule" that the identifiers should not influence the compiled code (I'm not
sure if this is actually a rule in the Pascal syntax, but anyway I personally
like it this way).

Also, there are other questions involved. If the code of the object type
changes, should it get a new ID or not? I think, only the programmer can
decide this. If all the object's fields and their semantics stay the same,
the ObjID can usually stay the same. Also, of course, if the object is in
the developing/debugging process. But if the "interface" of an object in
a published program changes, the ID should change as well, to avoid
compatibility problems with older stream files.

Bottom line: I don't think it can be done all automatically, but it can be
done much more comfortably to the programmer than in TV.

What I meant was that the programmer declares the ID, like

type MyObject(TObject)
       Const ObjID:Word = MyBaseID+42;
       ...
     End;

or ... why not also

       Const ObjID:Word = Inherited ObjID+1;

(Though the simple "+1" might not be good, as it can easily lead to
conflicts - but perhaps something similar.)

Concerning the numbering convention, I'd also suggest something "better" than
TV's way, perhaps an ID consisting of 4 parts (perhaps 16 bits for each part,
this would leave room for a growing number of programs for some time (well -
at least as long as 640 KB are enough for everybody... ;-), and the whole
thing would be 64 bits (longlongint?)):

1. "unique" programmer ID
2. ID for a "category" of types (graphical objects, mathematical objects,
     dialog elements, ...) - unique only for the programmer
3. ID for a certain type within a category
4. "version number" for the type (i.e., if the fields or their semantics
   change, the version number will be changed, too, so that old objects in
   streams can be recognized)

> Then what about storing the name of the object itself
> in the Stream?

As I said above, I wouldn't like this. But it could be an idea to have
ObjID be a string instead of a numerical value. But I'm not sure if it's
worth the additional work required...

> > Thinking about how to implement this, I realize this is basically the same
> > as object constants -- something that's missing in BP and I could've used
> > sometimes. This could be a useful extension to BP's objects. (The constants
> > and their values would be inherited if not redeclared, just like virtual
> > methods.)
> 
> And they would be stored in the VMT.  Sounds reasonable.

Exactly. They would need to be _typed_ constants then, wouldn't they?
(But there's no problem about it, except that the programmer has to type
a little bit more...)

Just an addition to the class variables (this might complicate matters a
bit more, though): The question is, should the variables be shared with
child types or duplicated? I think there could be applications for both.
To make clear what I mean, an example:

Type "a" declares a class variable "v".
Type "b(a)" doesn't redeclare "v", so it uses the same variable as "a".
Type "c(a)" redeclares "v", so it gets a variable of its own (of course,
  it must be of the same type as "a.v", but it could have a different
  initializer).

Does this seem reasonable?

To implement this, there must be a pointer to the actual variable in
the VMT (which would point to the same variable for "a" and "b", and to a
different variable for "c").

> Sounds good, but I think the "stabilization" of gpc-2.1 has higher
> priority.  But this might be of interest for gpc-2.2 ...

I (at least) am not in a hurry (now)... ;-)

Pierre Phaneuf wrote:

> And also not put all the symbols in every programs, just those that use
> the "ClassName()" function, optimizing those using constants as their
> parameter and so on...
 
Perhaps it's easier to let "ClassName" be a normal object constant (if
they'll be implemented). Then the programmer could decide which names to
include in the program and how to call them (e.g. the actual identifier
might be "TSomething", but for the ClassName it might be preferable to
have just "Something").

> The way I see it, the best way is to make a legal way to directly call a
> specific class constructor. Like the "typed new()" function (passing an
> object type as the first parameter) does, but being able to put a variable
> there. Maybe a GPC-specific new() function? With another name of course to
> avoid incompatibilities...
 
This would require something like "virtual constructors", wouldn't it?
I.e., the constructors would have to have the same parameters in all
classes, and their addresses would be stored in the VMT. Doesn't seem
impossible, perhaps it's the most logical thing to do.

Peter Gerwinski wrote:

> Look whether the VMT field contains a nonzero value.  To be absolutely
> sure, we can even check whether the pointer really points to a VMT: The
> first Word pointed to must be a positive Integer (the size of the object),
> the second one its negative (which is included for purposes like this
> one).  Problem:  This will only work if the object was pre-initialized
> to have a zero VMT field.  This can be achieved with `New', but not with
> `GetMem', so you have still to be careful when implementing streams.
 
And what about non-dynamic objects (i.e. in the global data or the local
data of a procedure)? They're not often used as it seems, but AFAIK it's
legal to do so -- and I do sometimes. In order to rely on this method,
the compiler would have to initialize their VMT fields to 0, even if the
variable is "uninitialized".

Then we could be sure in all cases but GetMem, and since GetMem is a
"dirty trick", it's the programmer's responsibility to set the VMT to 0
(or whatever) after GetMem'ming.

> > Are we looking for BP compatibility or not?
> 
> We are in part.  The primary goal of GPC is to produce the best Pascal
> compiler the world has ever seen.

I agree with Peter. I, FWIW, didn't use TV -- partly because some parts
of it (including class registration) were, IMHO, badly implemented.
If gpc's TV would be the same, I probably wouldn't use it, either.

>  (See the Info documentation for
> details.)  BP compatibility is desireable because (ii) BP is not too
> bad and (i) many BP programmers will try GPC only if they can use their
> existing programs without any change.
 
So we may need both: a "100% BP compatible" unit for quick changes, and
a "better" gpc unit to which programs can be converted afterwards, and
which new programs can use from the beginning. Actually, I think, the
changes needed for BP programs shouldn't be too big: make the "Load"
constructor virtual (if we do it this way), declare the "ObjID" constant,
and remove the registration stuff. Could even be simplified with some
"IFDEF"s and clever use of the preprocessor, I guess.
 
> How does Delphi do this?  (Sorry for the stupid question, but my
> experience with Delphi is very limited, primarily because I cannot stand
> the mouse-only "user interface".)
 
Argh! Mouse-only for a programmers' tool??? Lucky me that I didn't buy it!

> What about making the structure of the VMT record visible for the program?
> For instance a built-in
> 
>     Type
>       VmtRec = record
>         Size, NegSize: Integer;
>         VirtualMethod: array [ 0..1 ] of Pointer;
>       end (* VmtRec *);
> 
> Where the `1' above stands for the unknown number of methods in a VMT.
 
Then it should be "0..0" or "1..1", but I prefer the following.

> (* I thought about changing the VMT to a schema type
> 
>     Type
>       VmtRec ( n: Integer ) = record
>         Size, NegSize: Integer;
>         VirtualMethod: array [ 1..n ] of Pointer;
>       end (* VmtRec *);
> 
> which would place `n' as an additional field at the very beginning of
> each VMT.  But it's probably better to let `Size' stay the first field and
> to save the additional storage for the number of methods in the object. *)
 
I think it's worth the 4 bytes to have a cleaner structure and not introduce
new "dirty tricks" -- just my experience from the way Borland messed up their
objects. It also wouldn't be the best promotion for schema types, if this
build-in structure, where schema types seem natural, didn't use them.

OTOH, the introduction of object constants would break this structure anyway,
since the VMT wouldn't contain only pointers then. So perhaps only declare

Type
       VmtRec = record
         Size, NegSize: Integer;
         MethodsAndConstants: Void;
       end (* VmtRec *);

and leave all access to "MethodsAndConstants" to the programmer's
responsibility (though I don't see any need for this at all -- I think
anything can be achieved by assigning the VMT offset to an object, and
accessing its fields/methods by normal means).

(BTW: Is "Void" a valid declaration of size 0? Otherwise perhaps "record end",
this is allowed at least in BP.)

A "Parent" field in the VMT seems quite useful to me. Also, a list of the
childs (this would require a "FirstChild", and a "Sibling" field in each
VMT). And perhaps they could be accessed through the VmtRec as well as
as normal object constants...
-- 
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