Waldek Hebisch wrote:
ATM I think that we should have a single-rooted tree of exception classess which should be known to the compiler.
Indeed.
For compatibility with Delphi exception classess should be TObject descendants.
Or the other way around, i.e. let TObject be a descendant of the exception root (I'll call it TException in the following). This would be more Delphi compatible (every TObject could be an exception), and I'd prefer it for other reasons (TException didn't have to carry ballast from TObject).
OTOH, I think TException should be a descendant of Root (no compatibility issues as OOE doesn't deal with exceptions; and Root doesn't carry much ballast).
So we'd have:
Root -> TException -> TObject
(About the drawbacks of letting TObject have a parent, see below.)
Implementing such classes in Pascal seems much easier then hardcoding class declaration in the compiler.
Totally agreed. I never meant to hard-code it in the compiler if it can be avoided at all. IMHO it should go in the RTS (at least in the longer run) and be automatically imported (where appropriate).
The -fbase-object= option looks like the easiest way to implement Root/TObject. Actually, before things stabilize I would like to Root/TObject to be user level code, outside of RTS proper. But I agree that such option may be too dangerous/confusing for users.
For testing and developing, this option might not hurt. But if you say it will be difficult to implement, and in the long run we want something different, perhaps we should do the different thing right away to save effort.
I'm still thinking along the lines of an attribute on declaration of the root class or similar. IMHO, the RTS units should have no special properties here, apart from automatic import (later), so everything that should later go into the RTS could be done in user code now.
Additionally, we may have -fmac-objects directive, which would be equivalent to sum of -fobjects-require-override, -fmethods-always-virtual and -fobjects-are-references.
plus -fno-delphi-method-shadowing -fno-base-object (i.e., so it sets the complete object model, not only turns on certain features. If one wants a mix, one can always give the deviating options afterwards).
I think we should have such an option for all four models, equivalent to the respective combination of positive and negative options. Also, the respective language dialect options should then imply the --foo-objects options (instead of the feature options directly, as e.g. currently --mac-pascal -> --methods-always-virtual).
Hmm, I was affraid that I already propose too many options.
Not a big deal IMHO. ;-)
We would like to have a single "unified" dialect, but the options will allow quite a lot of variation. My reasoning was:
we want support the four object models (dialects)
we need support for migrating legacy code
we need support for "blended code" (code that works both with GPC and other compiler)
is handled by dialect options.
But dialect options in general are very strong options and have a lot of "side-effects". That's why I propose object-model-only options, which are part of the dialect options, but without all the other, unrelated, dialect stuff.
For 2) we need precise control, hence the detailed options.
Agreed.
BTW: BP and Delphi models coexist in a single compiler (namely Delphi itself). So while meaning of -mac-objects looks reasonable clear to me the other look more fuzzy.
True, I hadn't considered this.
As you proposed, -fobjects-are-references and -fobjects-require-override would only apply to "object". Couldn't we also restrict -fdelphi-method-shadowing and -fbase-object= (or whatever it will become) to "class" only? Then each object model option would only imply the former two options (for models that use "object") or the latter two (for "class"), so --bp-objects and --delphi-objects could happily coexist.
In particular, they would not disable the respective other keyword, only the full dialect options would do this. (And it can always be done explicitly with --disable-keyword.)
Waldek Hebisch wrote:
Prof A Olowofoyeku (The African Chief) wrote:
With regard to Root and TObject (OOE and Delphi), I have suggested elsewhere that one could probably be a silent ancestor of the other. In fact, they could be one and the same thing (with names changing, and/or members hidden or revealed depending on Delphi or OOE mode). I am not sure whether this makes any sense ...
I don't think hiding declarations based on directives is a good idea. The problems with the built-in `Result' should be a lesson to us (not quite the same, but a similar topic).
Consider the following program:
[...]
while pointer(c) <> nil do begin writeln(c.classname); c := c.classparent;
However, if we add extra parent to TObject the first printouts would change. Note that while prp2 would crash on non-Delphi class, both prp1 and prp2 will work fine if we implement Delphi tree correctly.
Doesn't it depend on the definition of ClassParent (I suppose it's a Delphi builtin)? If we make it stop (return nil) at the Delphi root class, it would still work if there are other superclasses (kind of hidden from Delphi code then). I admit, it may not be the nicest feature, but perhaps better than the alternatives.
Prof A Olowofoyeku (The African Chief) wrote:
However, if we add extra parent to TObject the first printouts would change. Note that while prp2 would crash on non-Delphi class, both prp1 and prp2 will work fine if we implement Delphi tree correctly.
In which case, TObject and Root would need to be independent of each other.
This would make it difficult to write common code (including exception handling).
Waldek Hebisch wrote:
Prof A Olowofoyeku (The African Chief) wrote:
On 22 Oct 2005 at 20:06, Waldek Hebisch wrote:
<snip>
Compile and run this with BP, and you get garbage. With FPC, you get a warning at compile time, and a runtime error at runtime. With Virtual Pascal, you get a runtime error at runtime. Add and call a constructor, and the problem goes away.
So, a warning is not a bad idea (better than a crashing program).
Maybe I was not clear: I do not want to change how BP objects behave, so the warning for BP objects will stay in place. But I see no need for such warning in other models.
I agree. The Chief's example is just what I meant with an "implementation detail". In particular, to a programmer new to the object model, it wouldn't seem very intuitive why turning some method from static to virtual would necessitate a constructor that wasn't needed before. (One can give reasons with VMT pointers and the magic that constructors and virtual method calls do, but then we're already deep into implementation details.)
So I don't see such a warning as a generally-useful concept, even in the BP model. It's useful regarding the BP *implementation*, i.e. when writing code with GPC and trying to make sure it still works with BP. Therefore I'd rather tend to tie it to the --borland-pascal and --delphi dialect options. Of course, dialect options are generally a rather hard hammer (see above), but when writing code meant to run also under BP, you (almost) need it anyway.
BTW, the warning only can do so much anyway. In fact, under BP, a constructor doesn't only have to exist, it must also be called. This can't be checked at compile-time. BP, and since the last alpha also GPC, can do this, BP with "range checking", GPC with --object-checking, also tied to {$R+} for BP compatibility.
The other variants also have drawbacks, that is why I would prefer to leave the choice to programmers.
Yes, the programmers should have the choice - but the question is whether that choice should be exercised in code or at the command line. With regard to default ancestors, I think it would be problematic and error-prone to change default ancestors at the command line.
The point here is to minimize impact on existing code -- if a programmer have to change the code then she has no choice.
I don't think they should have to change the code WRT base classes, with either of our suggestions. After providing the base class (explicitly, and later in the RTS), and perhaps setting the right options, the actual class declarations should be unaffected (compared to OOE and Delphi).
Prof A Olowofoyeku (The African Chief) wrote:
On 22 Oct 2005 at 23:29, Waldek Hebisch wrote:
Of course, we can limit choice to none, "Root" or "TObject". Also, having to give a long chain of options will somewhat reduce abuse.
Since "Class" should (IMHO) mean an OOE or a Delphi class,
It will always mean either of them (until we add more dialects ;-).
I think the choices should be between "TObject" (default) or "Root". If the answer is "none" or something else, then "Class" in that case does not refer to a Delphi or OOE class, because it would be incompatible with both of them. Should this be allowed? I think not.
It will be necessary in order to implement the root class itself without compiler magic.
Of course, this is a very special case (that should occur exactly two times in the long run -- both in the RTS). Therefore we might also make this case a bit more difficult, in order to simplify normal usage:
- The compiler has N "slots" for root classes (currently N=2, unless we want N=3 for experimenting or whatever).
- There is an option that makes one of these N slots active at any time.
- When a "class" without ancestor is declared, and the active slot is filled, it becomes the implicit ancestor.
- When a "class" is declared, and the active slot is not filled, the class goes into that slot. (I'm not saying "without ancestor" here, as TObject might have non-Delphi ancestors, see above.)
- Additionally, in the previous case a warning is given (so it doesn't happen unintentionally in user code). Or stronger, an error, unless the declaration is specially flagged (with an attribute or whatever).
- Root class slots are transported through GPI files, and RTS GPI files are automatically imported, depending on the dialect. (I plan the latter anyway, to reduce the amount of compiler magic in other areas.) Importing into a slot already occupied will be an error. There is no way to change the slot on import (such as renaming does to the name), so once in the slot, a class will always occupy this slot when it's imported at all.
- The RTS can declare both root classes as regular "class"es, while setting respective object model options (locally), and explicitly disabling the above-mentioend warning (locally, of course), or setting the necessary attribute, respectively.
- Depending on the dialect options, respective interfaces from the RTS are automatically imported. As they will define the root classes, under normal circumstances, every "class" in user space will get an implicit ancestor.
Frank