Peter Gerwinski wrote:
BTW: Do TV programs actually rely on the zeroing out, or is this just a paranoia initialization?
They do.
Bad style! Well, at least then there should be a BIG warning in the BPCompat objects.pas not to use the library with long strings or other schemata or any initalized fields...
[String constants as Object IDs ...]
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.
I don't think it's so bad: Strings are more flexible than Integers. I really like to index a `RessourceFile' (in TV) with Strings ...
As Pierre said: string comparison are less efficient than integer compares, and generally I think *internal* things (i.e. things that must only be read by computers, not by humans) should use integers rather than strings when there's no real need for strings. Resource files, AFAIK, are binary files, anyway, so what good would it be to have some strings scattered in them?
A different thing would be textual "resource" files (e.g. like Windoze .ini files): for such things it can be useful to have a string ID (class name), with the compiler switch we discussed, *additionally*, but I think the primary ID should be an integer. (BTW: It might seem that string IDs are "more unique" than integer IDs, but I think the opposite is the case: many programmers will write some "TEdit" class (or "TWindow", "TString", ...), but with integer IDs there are no problems if they choose a unique programmer ID first.)
BTW: The compiler switch for the string class names could perhaps be saved if the VMTs are generated in the main program (we discussed this for other reasons, anyway). Then, the compiler could know if any part of the program referred to a string ID (this information would have to be stored in the .gpi file for units and modules), so the compiler could decide by itself whether or not to include the string ID. The same, of course, is true for the integer ID, so any program will contain just those ID(s) that it uses. So both IDs could coexist peacefully... :-)
Syntax proposal:
Object(...)[StringID,NumID]
Either or both IDs can be omitted. NumID defaults to 0, StringID to the class name (identifier). Accessible (readable) through fields ("object constants") called "ClassName" and "ClassID".
If the "other program" 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.
Anyway, I have more and more the impression that "Delphi compatibility" users desire does not mean that the GNU Pascal compiler must be able to compile Delphi's VCL, but that GNU Pascal has its own "VCL" with a 100% compatible API.
That's what I meant: if the format of the ID (string or integer) is only relied upon by the library, we could do it differently in gpc. But I'm not really convinced that Delphi handles these IDs as strings, could someone please check it out?
BTW: What about the QueryInterface things? They might make "100% source compatibility" with Delphi impossible, anyway, as soon as interfaces are used.
??? Sorry, I don't understand.
I don't either... ;-)
I mean, I don't really understand what QueryInterface does, or how it's used, but AFAI understood it, it's needed to access interfaces. If that's right, interface acces in Delphi and gpc might be so different that programs that use interfaces simply can't compile with Delphi and gpc at the same time (without some {$IFDEF}s, of course). Any Delphi users to explain this or show some code that uses interfaces?
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...
One pre-result: I like that third method, but it causes some techical problems as well. This task *is* a nontrivial one!
Next pre-result: We're getting on! I think we can use this way... :-)
But how many fields and which type??? I don't think that's a good idea.
One additional field of type "pointer to base object" will allow to store as much additional information as desired with the object.
In which class, and pointer to which class? Sure, a pointer to TObject could do anything, but would require type casts (which I consider bad style), or absolute declarations (even worse).
Some additional Boolean fields, stored in a packed array, cannot hurt.
See `tree.*' in the GCC source code for an example of what I mean with "extensibility". The "tree nodes" used in the GCC and GPC front-end are in fact Objects (written in C, not in C++!), and they contain such extension fields at some central nodes in the object tree.
Sorry, I don't really feel like wading through 100kB of C code now, but I guess they put in the "unused" fields because they're emulating objects -- for lack of an inheritance mechanism that allows to add fields when they're really needed. So I don't see how this would apply to gpc OOP.
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.
:-) Just what I proposed above: One additional pointer. It seems that we in fact agree, but we chose different words for it.
Not completely: my suggestion is to introduce pointers to appropriate types, e.g. pointers to some kind of video objects from all classes that access video. The (abstract) video class would provide the services required by those classes. That's a bit more specific than just a general purpose pointer.
Another thing (not belonging to the topics above, but I didn't want to start a new mail):
I think it could be useful to let the "IS" operator do an implicit type cast, so e.g. the following would compile:
type a=object end;
b=object(a) c:integer end;
var d:^a;
begin if d^ is b then writeln(d^.c) end.
So when an "if" condition contains just one "is"-comparison (no other conditions with "and" or "or"), the object should be type cast to the actual type for the scope of the "then" statement. (Of course, this could be generalized to more compilcated conditions, but I think this simple case would suffice for most purposes.)
I don't know how difficult this is to implement, but it would be a nice feature. E.g., in a program I have some situations like:
if CurrentEditor^ is TTextEditor then TTextEditor(CurrentEditor^).TextSearch(...)
I think this type cast should be superfluous (from a "high level" point of view). (Of course, the "is" currently is a "TypeOf" comparison.)