I recently came about the issue of overriding non-virtual methods by virtual ones. GPC currently allows this, but it may be slightly confusing. BP allows it as well, so we shouldn't forbid it. But should we warn? (Always or unless `--borland-pascal' was given?)
type a = object procedure p; end;
b = object (a) procedure p; virtual; end;
Frank
On 2 Jun 2003 at 21:34, Frank Heckenbach wrote:
I recently came about the issue of overriding non-virtual methods by virtual ones. GPC currently allows this, but it may be slightly confusing. BP allows it as well, so we shouldn't forbid it. But should we warn? (Always or unless `--borland-pascal' was given?)
A warning except under '--borland-pascal' is fine. Another thing - an object with virtual methods but no constructor causes confusion under BP - e.g.,
type a = object procedure p; end;
b = object (a) procedure p; virtual; end;
procedure a.p; begin writeln ('a'); end;
procedure b.p; begin writeln ('b'); end;
var foo : a; bar : b;
begin foo.p; bar.p; end.
If this example is compiled with BP then, only 'a' gets printed (BP gives no warning, but silently compiles what it considers to be a faulty program).
Compiled with FPC, you get these warnings: a.pas(6,7) Warning: Virtual methods are used without a constructor in "b" a.pas(30,2) Warning: Variable "bar" does not seem to be initialized
When you run the program, 'a' gets printed, and then a GPF.
Compiled with Delphi, you get no warning, and both 'a' and 'b' get printed. Ditto with GPC, even with '--borland-pascal'.
Add a constructor and a constructor call before the call to 'b.p' and everything is alright under BP and FPC.
So, GPC's current treatment of objects with virtual methods but no constructor seems (at least with the trivial examples) to be consistent with Delphi, but not with BP when using '--borland-pascal'.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~african_chief/
Prof A Olowofoyeku (The African Chief) wrote:
On 2 Jun 2003 at 21:34, Frank Heckenbach wrote:
I recently came about the issue of overriding non-virtual methods by virtual ones. GPC currently allows this, but it may be slightly confusing. BP allows it as well, so we shouldn't forbid it. But should we warn? (Always or unless `--borland-pascal' was given?)
A warning except under '--borland-pascal' is fine.
OK (chief52*.pas). (Also no warning under `--delphi', or should it?)
Another thing - an object with virtual methods but no constructor causes confusion under BP - e.g.,
type a = object procedure p; end;
b = object (a) procedure p; virtual; end;
procedure a.p; begin writeln ('a'); end;
procedure b.p; begin writeln ('b'); end;
var foo : a; bar : b;
begin foo.p; bar.p; end.
If this example is compiled with BP then, only 'a' gets printed (BP gives no warning, but silently compiles what it considers to be a faulty program).
Compiled with FPC, you get these warnings: a.pas(6,7) Warning: Virtual methods are used without a constructor in "b" a.pas(30,2) Warning: Variable "bar" does not seem to be initialized
When you run the program, 'a' gets printed, and then a GPF.
Compiled with Delphi, you get no warning, and both 'a' and 'b' get printed. Ditto with GPC, even with '--borland-pascal'.
Add a constructor and a constructor call before the call to 'b.p' and everything is alright under BP and FPC.
So, GPC's current treatment of objects with virtual methods but no constructor seems (at least with the trivial examples) to be consistent with Delphi, but not with BP when using '--borland-pascal'.
For GPC, your observed behaviour is expected. With what I know about BP, it isn't -- and in fact, when I tested it with BP, it gave a runtime error 210 (range checking or invalid object reference), and with `{$R-}', it hung. I don't know so much about Delphi and FPC, so I can't tell whether it's the expected behaviour there. Maybe it isn't guaranteed to work in Delphi and just happened to in your test (just like your BP test happened to not crash). Do you find anything in the documentation?
In all my Pascal code, I found just one place (and some GPC test programs) where objects have virtual methods and no constructor. These can be fixed easily.
So I think a warning cannot hurt, and unless someone protests, I'll add it (not for abstract object types, of course). (chief51*.pas)
Frank
On 4 Jun 2003 at 0:41, Frank Heckenbach wrote:
I recently came about the issue of overriding non-virtual methods by virtual ones. GPC currently allows this, but it may be slightly confusing. BP allows it as well, so we shouldn't forbid it. But should we warn? (Always or unless `--borland-pascal' was given?)
A warning except under '--borland-pascal' is fine.
OK (chief52*.pas). (Also no warning under `--delphi', or should it?)
I think there should also be no warning under '--delphi'.
Another thing - an object with virtual methods but no constructor causes confusion under BP - e.g.,
type a = object procedure p; end;
b = object (a) procedure p; virtual; end;
procedure a.p; begin writeln ('a'); end;
procedure b.p; begin writeln ('b'); end;
var foo : a; bar : b;
begin foo.p; bar.p; end.
If this example is compiled with BP then, only 'a' gets printed (BP gives no warning, but silently compiles what it considers to be a faulty program).
Compiled with FPC, you get these warnings: a.pas(6,7) Warning: Virtual methods are used without a constructor in "b" a.pas(30,2) Warning: Variable "bar" does not seem to be initialized
When you run the program, 'a' gets printed, and then a GPF.
Compiled with Delphi, you get no warning, and both 'a' and 'b' get printed. Ditto with GPC, even with '--borland-pascal'.
Add a constructor and a constructor call before the call to 'b.p' and everything is alright under BP and FPC.
So, GPC's current treatment of objects with virtual methods but no constructor seems (at least with the trivial examples) to be consistent with Delphi, but not with BP when using '--borland-pascal'.
For GPC, your observed behaviour is expected. With what I know about BP, it isn't -- and in fact, when I tested it with BP, it gave a runtime error 210 (range checking or invalid object reference), and with `{$R-}', it hung. I don't know so much about Delphi and FPC, so I can't tell whether it's the expected behaviour there. Maybe it isn't guaranteed to work in Delphi and just happened to in your test (just like your BP test happened to not crash). Do you find anything in the documentation?
This is all I can find:
"A constructor is a special method that initializes an object containing virtual methods.
Syntax constructor Method (<parameter list>);
Remarks A constructor must be declared with the reserved word constructor.
The constructor initializes an object by setting the link between the object and the virtual method table with addresses of the code for its virtual methods.
A constructor can also be used to initialize the object data fields."
So, I guess that the first statement means that a constructor is expected when there are virtual methods. The penultimate statement also seems to indicate that a constructor is treated as something special by the BP compiler (and therefore probably involves some special processing), whereas I don't believe that this is necessarily the case under GPC. How/where does GPC achieve it's "setting the link between the object and the virtual method table with addresses of the code for its virtual methods"?
PS: I know that, under BP and Delphi, a call to the constructor automatically zeroes out all the object's fields as appropriate (e.g., pointers are set to Nil, numbers are set to zero, and strings are set to empty). I am not sure that this is a behaviour that GPC should imitate - but the fact that GPC does not do this automatically is currently one of the "gotchas" awaiting those coming to GPC from BP and Delphi.
In all my Pascal code, I found just one place (and some GPC test programs) where objects have virtual methods and no constructor. These can be fixed easily.
So I think a warning cannot hurt, and unless someone protests, I'll add it (not for abstract object types, of course). (chief51*.pas)
I agree that a warning cannot hurt.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~african_chief/
Prof A Olowofoyeku (The African Chief) wrote:
PS: I know that, under BP and Delphi, a call to the constructor automatically zeroes out all the object's fields as appropriate (e.g., pointers are set to Nil, numbers are set to zero, and strings are set to empty).
This is not the case with BP or Delphi "object"s. It is Turbo Vision's TObject.Init (a constructor) that provides this functionality; it is not an inherent feature of an "object". In Delphi, all "class"es must be a descendant of TObject (a special class known to the compiler), and this also probably does field zeroing in Delphi classes.
Jay
Jason Burgon. Author of Graphic Vision Version 2.21 available from: http://homepage.ntlworld.com/gvision
Prof A Olowofoyeku (The African Chief) wrote:
So, I guess that the first statement means that a constructor is expected when there are virtual methods. The penultimate statement also seems to indicate that a constructor is treated as something special by the BP compiler (and therefore probably involves some special processing),
I think so, so the warning seems justified for any dialect.
whereas I don't believe that this is necessarily the case under GPC. How/where does GPC achieve it's "setting the link between the object and the virtual method table with addresses of the code for its virtual methods"?
When the object starts to exist, i.e. for global or static ones in the program/unit/module constructor, for (non-static) local ones at the start of the routine, and for dynamic ones immediately after the memory allocation (whether or not a constructor is given in `New').
PS: I know that, under BP and Delphi, a call to the constructor automatically zeroes out all the object's fields as appropriate (e.g., pointers are set to Nil, numbers are set to zero, and strings are set to empty).
Nope. The following gives 1 under BP (2 under GPC). I don't know what Delphi does, but BP certainly doesn't. I think it's the same issue as with global variables (and also non-objects allocated from the heap). Sooner or later one has to learn that unintialized variables and fields are undefined in Pascal, even though GPC (at least on most platforms, perhaps all) zeroes global variables at program start.
program Foo;
type t = record a, b, c: Integer end;
o = object a: Integer; constructor Init; end;
constructor o.Init; begin end;
var p: ^t; q: ^o;
begin New (p); p^.a := 1; p^.b := 2; p^.c := 3; Dispose (p); New (q, Init); WriteLn (q^.a) end.
Frank
On 4 Jun 2003 at 11:03, Frank Heckenbach wrote:
whereas I don't believe that this is necessarily the case under GPC. How/where does GPC achieve it's "setting the link between the object and the virtual method table with addresses of the code for its virtual methods"?
When the object starts to exist, i.e. for global or static ones in the program/unit/module constructor, for (non-static) local ones at the start of the routine, and for dynamic ones immediately after the memory allocation (whether or not a constructor is given in `New').
Sounds just as good as any other solution.
PS: I know that, under BP and Delphi, a call to the constructor automatically zeroes out all the object's fields as appropriate (e.g., pointers are set to Nil, numbers are set to zero, and strings are set to empty).
Nope. The following gives 1 under BP (2 under GPC). I don't know what Delphi does, but BP certainly doesn't. I think it's the same issue as with global variables (and also non-objects allocated from the heap).
It gives 0 under all versions of Delphi.
Sooner or later one has to learn that unintialized variables and fields are undefined in Pascal, even though GPC (at least on most platforms, perhaps all) zeroes global variables at program start.
Except when they are *not* uninitialized (;)) as under Delphi, where all fields are automatically initialized (the documented behaviour for "classes"). This from the help file:
"To create an object, call the constructor method on a class type. For example,
MyObject := TMyClass.Create;
This allocates storage for the new object on the heap, sets the values of all ordinal fields to zero, assigns nil to all pointer and class- type fields, and makes all string fields empty. Other actions specified in the constructor implementation are performed next; typically, objects are initialized based on values passed as parameters to the constructor. Finally, the constructor returns a reference to the newly allocated and initialized object. The type of the returned value is the same as the class type specified in the constructor call."
But the zeroing also seems to apply to old BP-style objects.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~african_chief/
Prof A Olowofoyeku (The African Chief) wrote:
Sooner or later one has to learn that unintialized variables and fields are undefined in Pascal, even though GPC (at least on most platforms, perhaps all) zeroes global variables at program start.
Except when they are *not* uninitialized (;)) as under Delphi, where all fields are automatically initialized (the documented behaviour for "classes").
I still think it's better (and in the spirit of Pascal) to explicitly initialize (in code or declarations) what one wants initialized.
Frank
This came with TurboVision:
Language Guide, Borland Pascal With Objects, p37
<quote>
Virtual methods
By default, methods are "static". With the exception of constructor methods, they can be made "virtual" by including a virtual directive in the method declaration. The compliler resolves calls to static methods at compile time. Calls to virtual methods are resolved at run time; this is known as "late binding".
If an object type declares or inherits any virtual methods, then variables of that type must be "initialized" through a constructor call before any call to a virtual method. Therefore, any object type that declares or inherits any virtual methods must also declare or inherit at least one constructor method.
An object type can "override" (redefine) any of the methods it inherits from its ancestors. If a method declaration in a descendant specifies the same method identifier as a method declaration in an ancestor, then the declaration in the descendant overides the declaration in the ancestor. The scope of an overide method extends over the domain of the descendant in which it's introduced, or until the method identifier is again overridden.
An override of a static method is free to change the method heading any way it pleases. In contrast, an override of a virtual method must match exactly the order, types, and names of the parameters, and the type of the function result, if any. The override must again include a virtual directive.
<end quote>
note: words in quotes were written in italic.
Hope this helps, Russ
Russell Whitaker wrote:
This came with TurboVision:
Language Guide, Borland Pascal With Objects, p37
<quote>
[...]
<end quote>
Sorry, I don't see anything in the quote that's relevant to the subject.
Frank