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.
On Thu, 5 Jun 1997, Frank Heckenbach wrote:
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.
My own UI library (that is to come a bit later than the base class library) does it this way: I have a TVideo abstract object that I descend into more specific objects. For example, theres a text mode object, a VGA mode 13h object (oh gawd! does this look ugly!), S3 specific VGA object (the card I use is a S3) and a VESA object that can do many modes...
Pierre Phaneuf
"The use of COBOL cripples the mind; its teaching should, therefore, be regarded as a criminal offense." - Edsger W. Dijkstra.