On Mon, 26 May 1997, Frank Heckenbach wrote:
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.
I agree with you. I am in effect creating a better way to do it, but it will NOT be in BPCOMPAT. I'm a proficient user of TV, and I'll try to have the object parts of the BPCOMPAT package as compatible with it as possible... It *will* be in a GPC-specific class library of mine I'll contribute to the GPC team "When It's Ready (TM)", of course! ;-)
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?)):
- "unique" programmer ID
- ID for a "category" of types (graphical objects, mathematical objects, dialog elements, ...) - unique only for the programmer
- ID for a certain type within a category
- "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)
All this is not really needed in a system similar to TV, since you can register the objects yourself in case of conflict, resolving them to your liking. Though it is a nice idea to have a programmer/vendor part... A call to a unit-specific RegisterType procedure could look like this: RegisterObjects(1), the 1 being a value selected by the application programmer to prevent conflicts with another unit. How about a dword, with the first 16 bits for the unit ID and the last 16 bits for the object ID itself within the unit. Gives you 64K units in a single program with 64K objects per unit... Should be quite enough! :-)
The version number isn't a good idea though... The idea with the object ID is to spawn the correct object to load the object from the stream. The correct way to put versioning in your streamable classes is to have a version number stored at the beginning of your object by your Store method, and have your Load method have a 'case' statement that loads the object correctly according to the various versions.
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...
Not really IMHO... I agree with you on this subject...
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 idea with having a separate ClassName() function is to let you have a ClassName method, variable or constant without losing the functionality.
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.
No... You know that New() works either this way: New(P, Init), where P is a typed pointer (of an object type that has a Init constructor, of course!), where P will be changed to point to the new instance or set as nil if the object didn't construct correctly. The other way is as a function that work like this: P:=New(PObject, Init), which will construct a TObject and return the instance pointer (or nil). It isn't a virtual constructor, you simply choose which object to construct.
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.
Yes, I agree, but let's keep in mind what we're looking to fix in this thread is the BPCOMPAT package, which will have to look like BP libraries are, at least on the outside. BTW, what didn't you like exactly with TV? As for the class registration, what I didn't like was that it used the data segment, which isn't a very clean hack IMHO... Apart from that, it's ok by me (the class registration system!). The Free method is one of the purest evils in there. :-) (and it is in the BPCOMPAT package!)
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.
I'm developing something here for a "GPC enhanced" class library, I'll keep you posted! Up to now, no compiler changes or new features are needed, except for a clean way to spawn an object from its VMT link. I started developing it with BP so I knew exactly how to do it (it is portable, if you don't count the heck of limitations induced by BP (collections are limited to about 16K elements with BP, where they are "big enough" with GPC) ). I "faked" the 'typed New()' code using Turbo Debugger to learn how it worked...
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...
At least Parent would be useful. Note that my class library registration system has a field for the parent object and can walk the registration list to find its childs.
Pierre Phaneuf
"The use of COBOL cripples the mind; its teaching should, therefore, be regarded as a criminal offense." - Edsger W. Dijkstra.