Hello,
I found out that, unfortunately, the newest GPC has a changed processing of constructors. As far as I know, even the virtual methods should be identified in the compilation time in the sense, that when calling a constructor for an object (for example one with destroyed layout) the methods are called inside the constructor the static way. GPC 20020410 causes segmentation fault when I try to call a constructor (and a virtual method inside) on an object which was FillChar'ed (obviously, the same is the case when we use a pointer to an array of objects created by GetMem). See the test program below:
program tester;
type t=object procedure proc;virtual; constructor Init; end;
procedure t.proc; begin writeln('Proc'); end;
constructor t.init; begin writeln('Init'); proc; end;
var p:array[0..10] of t;
begin fillchar(p,sizeof(p),0); p[1].Init; end.
I assume, there was some optimalization added to call the virtual methods really the virtual way. Or maybe the constructor creates the VMTP at the end of the constructor process. Is there a chance to get the previous behavior back ?
Regards, Adam Naumowicz
-------------------------------------- WWW: http://math.uwb.edu.pl/~adamn/ --------------------------------------
On 18 Apr 2002 at 19:01, Adam Naumowicz wrote:
[...]
program tester;
type t=object procedure proc;virtual; constructor Init; end;
procedure t.proc; begin writeln('Proc'); end;
constructor t.init; begin writeln('Init'); proc; end;
var p:array[0..10] of t;
begin fillchar(p,sizeof(p),0); p[1].Init; end.
I assume, there was some optimalization added to call the virtual methods really the virtual way. Or maybe the constructor creates the VMTP at the end of the constructor process. Is there a chance to get the previous behavior back ?
Are you sure that you are not overwriting the VMT with the "fillchar" call? IIRC, it is not a good idea at all to use fillchar on records and objects. It might be better to initialise the fields manually.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~African_Chief email: African_Chief@bigfoot.com
Hello,
On Thu, 18 Apr 2002, Prof Abimbola Olowofoyeku wrote:
On 18 Apr 2002 at 19:01, Adam Naumowicz wrote:
[...]
program tester;
type t=object procedure proc;virtual; constructor Init; end;
procedure t.proc; begin writeln('Proc'); end;
constructor t.init; begin writeln('Init'); proc; end;
var p:array[0..10] of t;
begin fillchar(p,sizeof(p),0); p[1].Init; end.
I assume, there was some optimalization added to call the virtual methods really the virtual way. Or maybe the constructor creates the VMTP at the end of the constructor process. Is there a chance to get the previous behavior back ?
Are you sure that you are not overwriting the VMT with the "fillchar" call? IIRC, it is not a good idea at all to use fillchar on records and objects. It might be better to initialise the fields manually.
I'm sure I do overwrite it! But still it should be possible (other Pascal compilers I know and some previous versions of GPC allow for it) to use virtual methods within a constructor. The FillChar example maybe doesn't make much sense, but let's say I have something like that:
type t=object constructor Init; procedure Proc;virtual; end;
constructor t.Init; begin Proc; end;
procedure t.Proc; begin end;
ar=array[0..10000] of t; pa=^ar; var p:pa;
begin GetMem(p,n*SizeOf(t)); p^[1].Init; end.
Then the elements of the array should know their type, that is, even after GetMem there should be a way to call the virtual method. Again you may say that this example is not a good programming technique - I agree, but still I would like to have the choice to use it or not. It used to work with static objects with GPC and Borland's compilers as well as FPC allow for it.
Regards, Adam Naumowicz
-------------------------------------- WWW: http://math.uwb.edu.pl/~adamn/ --------------------------------------
On 19 Apr 2002 at 9:10, Adam Naumowicz wrote:
[...]
Are you sure that you are not overwriting the VMT with the "fillchar" call? IIRC, it is not a good idea at all to use fillchar on records and objects. It might be better to initialise the fields manually.
I'm sure I do overwrite it! But still it should be possible (other Pascal compilers I know and some previous versions of GPC allow for it) to use virtual methods within a constructor.
It is perfectly possible. I use it all the time.
The FillChar example maybe doesn't make much sense,
No, it doesn't ;)
but let's say I have something like that:
type t=object constructor Init; procedure Proc;virtual; end;
constructor t.Init; begin Proc; end;
procedure t.Proc; begin end;
ar=array[0..10000] of t; pa=^ar; var p:pa;
begin GetMem(p,n*SizeOf(t)); p^[1].Init; end.
Then the elements of the array should know their type, that is, even after GetMem there should be a way to call the virtual method.
The elements of the array know their type and you can call the virtual method - as long as you allocate memory for the object with "New".
Again you may say that this example is not a good programming technique - I agree, but still I would like to have the choice to use it or not. It used to work with static objects with GPC and Borland's compilers as well as FPC allow for it.
It still works with GPC. The problem here is using "Getmem" instead of "New". This might be an unnecessary restriction in GPC (i.e., having to use "New") - but it may well be documented (I don't remember - but I know that I don't use "Getmem" for object pointers, and perhaps there was a reason).
Remember also that "New" is extended for objects under BP (and compatibles). So, a different and perhaps "better" way of doing the above may be something like (untested);
type pt = ^t; t=object .... ... type ar=array[0..10000] of pt; var p : ar;
begin for n := 0 to max do New (p[n], Init); end;
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~African_Chief email: African_Chief@bigfoot.com
On Fri, 19 Apr 2002, Prof Abimbola Olowofoyeku wrote:
Then the elements of the array should know their type, that is, even after GetMem there should be a way to call the virtual method.
The elements of the array know their type and you can call the virtual method - as long as you allocate memory for the object with "New".
.....
It still works with GPC. The problem here is using "Getmem" instead of "New". This might be an unnecessary restriction in GPC (i.e., having to use "New") - but it may well be documented (I don't remember - but I know that I don't use "Getmem" for object pointers, and perhaps there was a reason).
That is obviuos, but my question was related to a change of the GPC behaviour (some previous versions I have at hand i.e. 20010121 were able to compile it properly). I actually asked this question some time ago, but then it was about string schemata. Now, I noticed the same with objects and that caused my question. Therefore, I suppose that now, a constructor called as a static method (without new) doesn't (re)create VMTP at all ?
type pt = ^t; t=object .... ... type ar=array[0..10000] of pt; var p : ar;
That's not actually the same - the point was to allocate only the needed space with GetMem - not even the array of pointers with maximal possible size...
Regards, Adam Naumowicz
-------------------------------------- WWW: http://math.uwb.edu.pl/~adamn/ --------------------------------------
On 19 Apr 2002 at 12:44, Adam Naumowicz wrote:
[..]
It still works with GPC. The problem here is using "Getmem" instead of "New". This might be an unnecessary restriction in GPC (i.e., having to use "New") - but it may well be documented (I don't remember - but I know that I don't use "Getmem" for object pointers, and perhaps there was a reason).
That is obviuos, but my question was related to a change of the GPC behaviour (some previous versions I have at hand i.e. 20010121 were able to compile it properly). I actually asked this question some time ago, but then it was about string schemata. Now, I noticed the same with objects and that caused my question. Therefore, I suppose that now, a constructor called as a static method (without new) doesn't (re)create VMTP at all ?
Dunno. Only Peter and Frank can perhaps answer that question.
type pt = ^t; t=object .... ... type ar=array[0..10000] of pt; var p : ar;
That's not actually the same - the point was to allocate only the needed space with GetMem - not even the array of pointers with maximal possible size...
Yes.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~African_Chief email: African_Chief@bigfoot.com
Prof Abimbola Olowofoyeku wrote:
On 19 Apr 2002 at 9:10, Adam Naumowicz wrote:
I found out that, unfortunately, the newest GPC has a changed processing of constructors. As far as I know, even the virtual methods should be identified in the compilation time in the sense, that when calling a constructor for an object (for example one with destroyed layout) the methods are called inside the constructor the static way.
That's not really what it did before. What I changed was to remove a (normally) redundant VMT pointer assignment. Usually the VMT pointer is assigned when the variable is created (i.e., at program/routine start for non-pointer objects, and after memory allocation when using `New').
Until recently, GPC *also* assigned the VMT when calling a constructor which is therefore redundant (especially when calling a constructor within `New', one could see two assignments next to each other which made it clear that one was redundant).
but let's say I have something like that:
type t=object constructor Init; procedure Proc;virtual; end;
constructor t.Init; begin Proc; end;
procedure t.Proc; begin end;
ar=array[0..10000] of t; pa=^ar; var p:pa;
begin GetMem(p,n*SizeOf(t)); p^[1].Init; end.
Then the elements of the array should know their type, that is, even after GetMem there should be a way to call the virtual method.
The elements of the array know their type and you can call the virtual method - as long as you allocate memory for the object with "New".
Indeed.
First question: Why don't you simply write `New (p)' (which works)? I suppose because you don't really want 10000 elements, but rather just a place-holder for a "large" number while you actually allocate less. Of course, it might have been a good idea to actually state this in your mail and to provide a definition of n in your code fragment above, so we wouldn't have to guess ...
Secondly: Why don't you use a schema type (`ar(n:Integer)=array[0..n-1] of t; [...] New(p,n)')? This also works. But if you want to make things extra hard for you by using place-holders and manually computed memory sizes, you should not be surprised when also the initialization gets harder ...
That said, you can initialize objects manually if you really want to, by using `SetType' (e.g., `{$local X+} SetType(p^[1], TypeOf (t)); {$endlocal}').
It still works with GPC. The problem here is using "Getmem" instead of "New". This might be an unnecessary restriction in GPC (i.e., having to use "New")
I don't think it's an "unnecessary restriction". `GetMem' just does what it says -- it allocates a block of memory and nothing more. It's a low-level routine -- use it with caution. `New' is a high-level routine for memory allocation in a Pascal context.
People sometimes get confused by the fact that both of them do the actual allocation in the same way (in GPC and also in BP AFAIR). But that's not the point -- the difference is what `New' does *besides* the initialization. Using `GetMem' you can skip those initializations ... if that's what you want ...
Frank
Frank Heckenbach wrote:
Prof Abimbola Olowofoyeku wrote:
On 19 Apr 2002 at 9:10, Adam Naumowicz wrote:
I found out that, unfortunately, the newest GPC has a changed processing of constructors. As far as I know, even the virtual methods should be identified in the compilation time in the sense, that when calling a constructor for an object (for example one with destroyed layout) the methods are called inside the constructor the static way.
That's not really what it did before. What I changed was to remove a (normally) redundant VMT pointer assignment. Usually the VMT pointer is assigned when the variable is created (i.e., at program/routine start for non-pointer objects, and after memory allocation when using `New').
Until recently, GPC *also* assigned the VMT when calling a constructor which is therefore redundant (especially when calling a constructor within `New', one could see two assignments next to each other which made it clear that one was redundant).
So you could have done the other way around, assigning the VMT pointer
only when calling a constructor. This would have given what Adam was expecting. Anyway an object is not valid until a constructor is called. Is there any drawback for that ?
Maurice
Maurice Lombardi wrote:
Frank Heckenbach wrote:
Prof Abimbola Olowofoyeku wrote:
On 19 Apr 2002 at 9:10, Adam Naumowicz wrote:
I found out that, unfortunately, the newest GPC has a changed processing of constructors. As far as I know, even the virtual methods should be identified in the compilation time in the sense, that when calling a constructor for an object (for example one with destroyed layout) the methods are called inside the constructor the static way.
That's not really what it did before. What I changed was to remove a (normally) redundant VMT pointer assignment. Usually the VMT pointer is assigned when the variable is created (i.e., at program/routine start for non-pointer objects, and after memory allocation when using `New').
Until recently, GPC *also* assigned the VMT when calling a constructor which is therefore redundant (especially when calling a constructor within `New', one could see two assignments next to each other which made it clear that one was redundant).
So you could have done the other way around, assigning the VMT pointer
only when calling a constructor. This would have given what Adam was expecting. Anyway an object is not valid until a constructor is called. Is there any drawback for that ?
Matter of taste probably ... If I declare `var MyObj: MyType' (i.e., not a pointer), I'd expect `TypeOf (MyObj)' (i.e., the VMT pointer) to have some reasonable value immediately.
Of course, that basically reduces constructors to almost regular methods with no special "magic" -- which I consider a good thing since any avoidable "magic" is only a source of confusion. Placing this magic at the point of creation of the objects seems more reasonable since that's where other kinds of initializations (schema discriminants, file variable default state, explicit initializers) are done.
Also, I don't see what we'd win the other way. It might perhaps work in Adam's special case, but in general, that's still bound to fail. If the object contains some fields of string, schema or file type (or in the future, types with explicit initializers), it still won't work with this FillChar/GetMem approach.
What I'm rather thinking of is a "magic" built-in procedure to initialize some given variable the same way it's initialized on creation. Yes, it's magic again (though it would require an explicit call, so it's not magic behind the scenes). And since it would do all the initializations (including discriminants and files, e.g.), it would be more consistent ...
Frank
Hello,
On Fri, 19 Apr 2002, Frank Heckenbach wrote:
Maurice Lombardi wrote:
Frank Heckenbach wrote:
Prof Abimbola Olowofoyeku wrote:
On 19 Apr 2002 at 9:10, Adam Naumowicz wrote:
I found out that, unfortunately, the newest GPC has a changed processing of constructors. As far as I know, even the virtual methods should be identified in the compilation time in the sense, that when calling a constructor for an object (for example one with destroyed layout) the methods are called inside the constructor the static way.
That's not really what it did before. What I changed was to remove a (normally) redundant VMT pointer assignment. Usually the VMT pointer is assigned when the variable is created (i.e., at program/routine start for non-pointer objects, and after memory allocation when using `New').
Until recently, GPC *also* assigned the VMT when calling a constructor which is therefore redundant (especially when calling a constructor within `New', one could see two assignments next to each other which made it clear that one was redundant).
So you could have done the other way around, assigning the VMT pointer
only when calling a constructor. This would have given what Adam was expecting. Anyway an object is not valid until a constructor is called. Is there any drawback for that ?
Matter of taste probably ...
I think, that the optimalization of redundant VMTP creation is not very fruitful. In my opinion, the gain it gives is not worth making another step which breaks compatibility with other compilers like Delphi, support for which is one of the nice GPC features. The silly examples I gave you were not just made up, but they came from a piece of code which has worked fine with several compilers for more than 15 years...
What I'm rather thinking of is a "magic" built-in procedure to initialize some given variable the same way it's initialized on creation. Yes, it's magic again (though it would require an explicit call, so it's not magic behind the scenes). And since it would do all the initializations (including discriminants and files, e.g.), it would be more consistent ...
That sounds reasonable, but probably requires much effort involved ;-( Regards, Adam Naumowicz
-------------------------------------- WWW: http://math.uwb.edu.pl/~adamn/ --------------------------------------
Adam Naumowicz wrote:
What I'm rather thinking of is a "magic" built-in procedure to initialize some given variable the same way it's initialized on creation. Yes, it's magic again (though it would require an explicit call, so it's not magic behind the scenes). And since it would do all the initializations (including discriminants and files, e.g.), it would be more consistent ...
That sounds reasonable, but probably requires much effort involved ;-(
As I tried to explain, it probably wouldn't, since it would do just the same that already happens when variables are created. AFAICS (i.e., if I haven't overlooked anything), the main problem is to agree on a name ;-), then I could implement it as a matter of minutes ...
Frank
On 19 Apr 2002 at 22:28, Frank Heckenbach wrote: [...]
That sounds reasonable, but probably requires much effort involved ;-(
As I tried to explain, it probably wouldn't, since it would do just the same that already happens when variables are created. AFAICS (i.e., if I haven't overlooked anything), the main problem is to agree on a name ;-), then I could implement it as a matter of minutes ...
In my other post, I suggested "Initialize". This has the advantage of being a Delphi-compatible name. And the implementation can also be Delphi-compatible, if that can easily be achieved.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~African_Chief email: African_Chief@bigfoot.com
Prof Abimbola Olowofoyeku wrote:
An "initialize-any-variable" procedure would be a good idea. In Delphi, a similar procedure exists, called "Initialize". This is the Dephi documentation on it:
"procedure Initialize(var V [ ; Count: Integer ] );
Description [...]
In my other post, I suggested "Initialize". This has the advantage of being a Delphi-compatible name. And the implementation can also be Delphi-compatible, if that can easily be achieved.
Well, almost. Of course, we don't always fill it with zeros, but rather do the appropriate initializations. :-) And I think (hope) we can also omit the second parameter since this is equivalent to initializing an array (which can be achieved by type-casting if nothing else helps).
I've implemnted it now, so the attached test passes. If you think that's not enough, please send me additional tests.
Frank
"procedure Initialize(var V [ ; Count: Integer ] );
Description [...]
In my other post, I suggested "Initialize". This has the advantage of being a Delphi-compatible name. And the implementation can also be Delphi-compatible, if that can easily be achieved.
Well, almost. Of course, we don't always fill it with zeros, but rather do the appropriate initializations. :-) And I think (hope) we can also omit the second parameter since this is equivalent to initializing an array (which can be achieved by type-casting if nothing else helps).
Afaik that is also usable to init more vars at once.
Marco van de Voort wrote:
"procedure Initialize(var V [ ; Count: Integer ] );
Description [...]
In my other post, I suggested "Initialize". This has the advantage of being a Delphi-compatible name. And the implementation can also be Delphi-compatible, if that can easily be achieved.
Well, almost. Of course, we don't always fill it with zeros, but rather do the appropriate initializations. :-) And I think (hope) we can also omit the second parameter since this is equivalent to initializing an array (which can be achieved by type-casting if nothing else helps).
Afaik that is also usable to init more vars at once.
Do you mean the second parameter in the Delphi version, or the type casting "trick"?
Frank
Marco van de Voort wrote:
being a Delphi-compatible name. And the implementation can also be Delphi-compatible, if that can easily be achieved.
Well, almost. Of course, we don't always fill it with zeros, but rather do the appropriate initializations. :-) And I think (hope) we can also omit the second parameter since this is equivalent to initializing an array (which can be achieved by type-casting if nothing else helps).
Afaik that is also usable to init more vars at once.
Do you mean the second parameter in the Delphi version, or the type casting "trick"?
I was thinking that since Delphi initialisation effectively means zeroing, why not sort local vars so that all initable vars are behind each other, and zero them all together?
Marco van de Voort wrote:
Marco van de Voort wrote:
being a Delphi-compatible name. And the implementation can also be Delphi-compatible, if that can easily be achieved.
Well, almost. Of course, we don't always fill it with zeros, but rather do the appropriate initializations. :-) And I think (hope) we can also omit the second parameter since this is equivalent to initializing an array (which can be achieved by type-casting if nothing else helps).
Afaik that is also usable to init more vars at once.
Do you mean the second parameter in the Delphi version, or the type casting "trick"?
I was thinking that since Delphi initialisation effectively means zeroing, why not sort local vars so that all initable vars are behind each other, and zero them all together?
If you want to zero out the whole block of memory, this might be a little more efficient. However, if only certain fields have to be initialized -- e.g., for EP strings, only the discriminant, arguably the length, but certainly not the (often large) array of chars -- you only do extra work this way.
Frank
I was thinking that since Delphi initialisation effectively means zeroing, why not sort local vars so that all initable vars are behind each other, and zero them all together?
If you want to zero out the whole block of memory, this might be a little more efficient. However, if only certain fields have to be initialized -- e.g., for EP strings, only the discriminant, arguably the length, but certainly not the (often large) array of chars -- you only do extra work this way.
Afaik all Delphi inited types are pointer types on assembly level. Except maybe variants, I don't know their internals that well atm.
But GPC having a different implementation on this level doesn't really matter. It is an optimization, not a requirement.
On 24 Apr 2002 at 3:55, Frank Heckenbach wrote:
[Initialize]
I've implemnted it now, so the attached test passes. If you think that's not enough, please send me additional tests.
Looks okay to me. You may want to implement a "Finalize" procedure as well. This is the Delphi documentation of it:
"procedure Finalize( var V [; Count: Integer] );
Description Finalize should be used only in situations where a dynamically allocated variable is deallocated by other means than the Dispose procedure. Dynamic arrays can never be deallocated using the Dispose procedure, but can be freed by passing them to Finalize.
For global variables, local variables, objects, and dynamic variables deallocated using Dispose, the compiler generates code that finalizes all long strings, variants, and interfaces contained by the variable when the instance is destroyed.
If a dynamic variable meets the following two conditions, a call to Finalize is required to finalize the variable before it can be deallocated.
The variable is deallocated by other means than the Dispose standard procedure (for example using FreeMem). The variable contains long strings, variants, or interfaces, not all of which are empty or Unassigned.
Finalize simply sets all long strings to empty and all variants and interfaces to Unassigned, thus properly releasing any memory that was referenced by the long strings and variants.
In cases where several variables are deallocated in a contiguous memory block such as a dynamically allocated array of strings, the additional Count parameter can be specified to finalize all variables in one operation.
If the variable specified in a call to Finalize contains no long strings, variants, or interfaces, the compiler eliminates the call and generates no code for it."
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~African_Chief email: African_Chief@bigfoot.com
Prof Abimbola Olowofoyeku wrote:
On 24 Apr 2002 at 3:55, Frank Heckenbach wrote:
[Initialize]
I've implemnted it now, so the attached test passes. If you think that's not enough, please send me additional tests.
Looks okay to me. You may want to implement a "Finalize" procedure as well. This is the Delphi documentation of it:
"procedure Finalize( var V [; Count: Integer] );
Done. (Though currently the only types that have some finalization are files, but this will change in the future.)
Frank
On 19 Apr 2002 at 13:18, Frank Heckenbach wrote: [...]
That's not really what it did before. What I changed was to remove a (normally) redundant VMT pointer assignment. Usually the VMT pointer is assigned when the variable is created (i.e., at program/routine start for non-pointer objects, and after memory allocation when using `New').
Until recently, GPC *also* assigned the VMT when calling a constructor which is therefore redundant (especially when calling a constructor within `New', one could see two assignments next to each other which made it clear that one was redundant).
Pardon my ignorance - but shouldn't we assign the VMT when a constructor is called? It seems that, unless this is done, one cannot guarantee that the assignment has taken place. I guess this raises another question - whether constructors should be treated as anything special, or whether they should be treated just like any other method. I believe that BP and Delphi (and, it seems, FreePascal) give special treatment to constructors, but I don't know exactly all the things that they do with constructors in order to treat them specially. But I know that if you have an object that has virtual methods but you don't first call a constructor before calling the methods, you will get a crash with all these compilers, but not with GPC.
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~African_Chief email: African_Chief@bigfoot.com
Prof Abimbola Olowofoyeku wrote:
On 19 Apr 2002 at 13:18, Frank Heckenbach wrote: [...]
That's not really what it did before. What I changed was to remove a (normally) redundant VMT pointer assignment. Usually the VMT pointer is assigned when the variable is created (i.e., at program/routine start for non-pointer objects, and after memory allocation when using `New').
Until recently, GPC *also* assigned the VMT when calling a constructor which is therefore redundant (especially when calling a constructor within `New', one could see two assignments next to each other which made it clear that one was redundant).
Pardon my ignorance - but shouldn't we assign the VMT when a constructor is called? It seems that, unless this is done, one cannot guarantee that the assignment has taken place.
Yes, we can (under normal conditions), since it happens whenever the object starts to exist (i.e., at program start, routine start or after memory allocation for global, local, and dynamic variables, respectively).
These things may look somewhat different to someone who's familiar with GPC's internals than someone who's not. Within GPC, there's a function (init_any) which is called exactly in those situations mentioned above, and which does all necessary initializations (currently for strings, schemata, files and objects). So objects are nothing special from this point of view.
In BP things are somewhat different: It doesn't have schemata, its strings don't store their capacity (which has some disadvantages), and for files they use a combination of "magic" values (which reduces the probability of finding a valid value in an uninitialized variable to 3/65536 under random conditions) and an `Assign' procedure which initializes and completely overwrites the file variable (which has some drawbacks, e.g. that re-assigning an already opened file leaves the file handle and the buffered data in the wild; that files must be assigned (i.e., no internal files possible); and that a coding mistake (forgetting to assign) can fail (i.e., accidentally use another existing file rather than causing a proper error) with a chance of 3/65536).
This leaves objects as something special in BP. (One might guess what they would have done if OOP had not been introduced as a later add-on, but had been present in the initial design. Maybe they'd also have done a general initialization concept, including files ...) BP also does some other strange things with constructors (IIRC, the memory allocation when calling a constructor within `New', is done from *within* the constructor (i.e., the object "allocates itself"); I had looked at the generated code when I used BP, and except for some extra complexity, I didn't seem to find any point in it -- which was confirmed when I later found out that it's not necessary in GPC while providing effectively the same functionality).
What GPC does seems much more logical to me: It allocates the memory, initializes the new variable (like it initializes strings, schemata and files), and then calls the constructor.
BP also has another strangeness, in that objects that have neither constructors nor destructors nor virtual methods don't have a VMT at all. This has some strange effects, e.g. since `SizeOf' reads the size from the VMT (if the type has one), applying `SizeOf' to an unintialized object that has neither of these things works, but stops working if you, say, add a (completely unrelated) virtual method (for another strange effect see the example below). These effects are probably not intentional, but they indicate that the concept might not be really good ...
So in GPC every object has a VMT, and therefore has to initialize the VMT pointer on creation to be compatible to BP in some reasonable cases (i.e., applying `SizeOf' to an object which has no constructor -- which works in BP if it also has no destructor and virtual method, as described above).
To sum up, when a constructor is called, the VMT pointer has already been set under normal conditions. It might not hurt to set it again, but it would not really solve any problem, either, since in `GetMem' situations, object fields of string, schema, etc. type would still remain uninitialized. Currently, in such a situation, one can do it all manually (with `SetType' for objects, and some more or less dirty tricks to set the discriminants). An initialize-any-variable (name?) builtin procedure as I suggested would seem like a better solution to this problem. (Though I'm still not sure why this problem arises at all -- I suppose to be bug-compatible to BP's lack of support for variables of dynamic size!?)
program Blah;
type pa = ^a; a = object f: Integer; end;
pb = ^b; b = object (a) g: Integer; end;
var v: pa;
begin v := New (pb); if SizeOf (v^) = SizeOf (b) then WriteLn ('OK') else if SizeOf (v^) = SizeOf (a) then WriteLn ('failed: size of a') else WriteLn ('failed: yet another size') end.
Frank
On 19 Apr 2002 at 22:17, Frank Heckenbach wrote: [...]
To sum up, when a constructor is called, the VMT pointer has already been set under normal conditions. It might not hurt to set it again, but it would not really solve any problem, either, since in `GetMem' situations, object fields of string, schema, etc. type would still remain uninitialized. Currently, in such a situation, one can do it all manually (with `SetType' for objects, and some more or less dirty tricks to set the discriminants). An initialize-any-variable (name?) builtin procedure as I suggested would seem like a better solution to this problem. (Though I'm still not sure why this problem arises at all -- I suppose to be bug-compatible to BP's lack of support for variables of dynamic size!?)
Perhaps. But it is likely to arise more for people who are new to GPC, or who are porting existing code from BP/Delphi/Virtual Pascal. An "initialize-any-variable" procedure would be a good idea. In Delphi, a similar procedure exists, called "Initialize". This is the Dephi documentation on it:
"procedure Initialize(var V [ ; Count: Integer ] );
Description
Initialize should be used only in situations where a variable is dynamically allocated by other means than the New standard procedure.
For global variables, local variables, objects, and dynamic variables allocated using New, the compiler generates code that initializes all long strings and variants contained by a variable upon creation of the variable. A call to Initialize is required to initialize a variable before it can be used if:
The dynamic variable is created by other means than the New standard function (for example using GetMem or ReallocMem). The variable contains long strings, variants, or interfaces. The memory allocated for the variable is not initialized to zeros.
Initialize simply zeros out the memory occupied by long strings, variants, and interfaces, causing long strings to be empty and variants and interfaces to be Unassigned.
In cases where several variables are allocated in a contiguous memory block, the additional Count parameter can be specified to initialize all variables in one operation.
If the variable specified in a call to Initialize contains no long strings, variants, or interfaces, the compiler eliminates the call and generates no code for it."
Best regards, The Chief -------- Prof. Abimbola A. Olowofoyeku (The African Chief) web: http://www.bigfoot.com/~African_Chief email: African_Chief@bigfoot.com